源文件

This commit is contained in:
gyq
2025-04-25 09:49:53 +08:00
commit 791d82b9e3
640 changed files with 130029 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
.DS_Store
.hbuilderx
*.log
unpackage
sitemap.json
.prettierrc.cjs
pages/diy/init_data.js

2
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,2 @@
{
}

3160
App.vue Normal file

File diff suppressed because it is too large Load Diff

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 ShopXO免费开源商城 - uniapp手机端
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

68
README.md Normal file
View File

@@ -0,0 +1,68 @@
<p align="center">
<img src="https://shopxoserver.oss-cn-beijing.aliyuncs.com/demo/system/logo.jpg" width="360" />
</p>
<h3 align="center">ShopXO企业级免费开源电商系统 - uniapp手机端</h3>
<div align="center">
```shell
如果对您有帮助,您可以点右上角 “Star” 收藏一下 ,获取第一时间更新,谢谢!
```
</div>
------------------------------------------------------------------------
#### 项目介绍
* 后端支持依托于ShopXO免费开源电商系统开发的uniapp端主题源码
* 支持可视化DIY拖拽装修内置几十种标准组件热区、魔方、自定义等等...
* 默认内置8中搭配主色(可自行扩展更多配色)、自由快捷切换适应各大行业需求
* 已支持小程序微信、QQ、百度、支付宝、头条&抖音、快手)+ H5 + APP
#### 使用教程
`程序打包操作需要有一定的编程技术基础、如需帮助请到git平台提issues寻求帮助`
* 打包教程 [https://doc.shopxo.net/article/1/293727233598554112.html](https://doc.shopxo.net/article/1/293727233598554112.html)
1. 先安装ShopXO免费开源系统 [http://install.shopxo.net/](http://install.shopxo.net/)
2. 将该源码导入HBuilderX开发工具、顶部工具栏 `运行->运行到小程序模拟器->(根据支持平台自行选择、如 微信开发者工具)`
3. App.vue中修改 request_url 和 static_url 地址为自己的商城地址即可使用
4. 主题默认为红色(red),如更改主题 App.vue文件中修改 default_theme 默认主题标识,也可以到商店搜索【默认主题样式】远程控制主题配色
5. 发布、HBuilderX开发工具、顶部工具栏 `发行->(根据支持平台自行选择、如 微信开发者工具)`
#### 官方QQ群、答案`shopxo.net`
* 官方uniapp群679303149
#### 体验码
![二维码.jpg](https://shopxoserver.oss-cn-beijing.aliyuncs.com/demo/system/demo-qrcode.jpg)
#### 当前项目源代码平台
* Gitee[https://gitee.com/zongzhige/shopxo-uniapp](https://gitee.com/zongzhige/shopxo-uniapp)
* GitHub[https://github.com/gongfuxiang/shopxo-uniapp](https://github.com/gongfuxiang/shopxo-uniapp)
* Coding[https://zongzhige.coding.net/public/shopxo/uniapp/git](https://zongzhige.coding.net/public/shopxo/uniapp/git)
* GitCode[https://gitcode.net/zongzhige/shopxo-uniapp](https://gitcode.net/zongzhige/shopxo-uniapp)
* uni-app[https://ext.dcloud.net.cn/plugin?id=6380](https://ext.dcloud.net.cn/plugin?id=6380)
#### ShopXO后端源代码平台
* Gitee[https://gitee.com/zongzhige/shopxo](https://gitee.com/zongzhige/shopxo)
* GitHub[https://github.com/gongfuxiang/shopxo](https://github.com/gongfuxiang/shopxo)
* Coding[https://zongzhige.coding.net/public/shopxo/shopxo/git](https://zongzhige.coding.net/public/shopxo/shopxo/git)
* GitCode[https://gitcode.net/zongzhige/shopxo](https://gitcode.net/zongzhige/shopxo)
#### DIY装修源代码平台
* Gitee[https://gitee.com/zongzhige/shopxo-diy](https://gitee.com/zongzhige/shopxo-diy)
* GitHub[https://github.com/gongfuxiang/shopxo-diy](https://github.com/gongfuxiang/shopxo-diy)
* GitCode[https://gitcode.net/zongzhige/shopxo-diy](https://gitcode.net/zongzhige/shopxo-diy)
#### API接口文档
* [https://doc.shopxo.net/article/2.html](https://doc.shopxo.net/article/2.html)
#### 小程序支持多种配色
![多种配色](https://shopxoserver.oss-cn-beijing.aliyuncs.com/demo/app/color.png "配色.jpg")
#### 小程序效果图片
![小程序页面](https://shopxoserver.oss-cn-beijing.aliyuncs.com/demo/app/page.png "小程序页面.jpg")
#### 小程序DIY装修
![DIY图片](https://shopxoserver.oss-cn-beijing.aliyuncs.com/demo/diy/diy.jpg "DIY图片")

35
androidPrivacy.json Normal file
View File

@@ -0,0 +1,35 @@
{
"version" : "1",
"prompt" : "template",
"title" : "服务协议和隐私政策",
"message" : "  请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/>  你可阅读<a href=\"https://d1.shopxo.vip/?s=agreement/index/document/userregister/is_content/1.html\">《服务协议》</a>和<a href=\"https://d1.shopxo.vip/?s=agreement/index/document/userprivacy/is_content/1.html\">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
"buttonAccept" : "同意并接受",
"buttonRefuse" : "暂不同意",
"hrefLoader" : "system|default",
"backToExit" : "false",
"second" : {
"title" : "确认提示",
"message" : "  进入应用前,你需先同意<a href=\"https://d1.shopxo.vip/?s=agreement/index/document/userregister/is_content/1.html\">《服务协议》</a>和<a href=\"https://d1.shopxo.vip/?s=agreement/index/document/userprivacy/is_content/1.html\">《隐私政策》</a>,否则将退出应用。",
"buttonAccept" : "同意并继续",
"buttonRefuse" : "退出应用"
},
"disagreeMode" : {
"support" : false,
"loadNativePlugins" : false,
"visitorEntry" : false,
"showAlways" : true
},
"styles" : {
"backgroundColor" : "#fff",
"borderRadius" : "6px",
"title" : {
"color" : "#333"
},
"buttonAccept" : {
"color" : "#52C41A"
},
"buttonRefuse" : {
"color" : "#f02720"
}
}
}

423
changelog.md Normal file
View File

@@ -0,0 +1,423 @@
## v6.5.02025-03-05
1. DIY新增数据选项卡、自定义增强优化
2. DIY浮动导航支持快捷导航
3. 订单单号支持复制
4. 在线客服企业微信客服适配h5处理
5. 多商户首页支持分类筛选
6. 商品、文章、活动配置、组合搭配、博客、门店、多商户支持分享图片
7. 商品详情购买按钮增加链接和复制事件
8. 门店类型选择优化
9. 支持同城模式
10. 新增问诊开方支持购物车
11. 优惠券新增详情
## v6.4.02024-12-25
1. 支付宝小程序兼容性优化
2. DIY支持新特性和细节优化
3. 问答支持富文本
4. 博客详情无数据错误修复
5. 门店支持最近选择、地图搜索选择、当前门店切换、支持强制选择门店
6. 开票支持钱包充值
7. 用户中心菜单支持未登录展示
8. 基础信息填写支持授权即提示、并支持手机号码一键绑定
9. 支付组件优化
10. 系统扫码默认走商品搜索,门店详情扫码无效修复
11. 积分商城首页优化
12. 下单支持填写联系人、联系电话、配送时间
13. 新增微信小程序收货组件确认
14. 订单支持订单资源插件
15. 扫码登录地址解析错误优化
16. 底部菜单判断优化
17. app评分优化
## v6.3.02024-10-22
1. 新增DIY装修可以自由设计模板
2. 底部菜单后台DIY管理、并保留原生
3. 商品足迹,增加收藏支持加入购物车
4. 商品评论优化
5. 初始化配置优化
6. 扫码细节优化适配h5
7. 多商户支持二级分类、头部分离优化
8. 全站走大圆角风格
9. 新增公共commom组件、支持onshow事件
10. 微信隐私提示、app管理、用户基础使用提到公共层
11. 定位独立为组件
12. 新增系统扫码统一接口
13. 新增协议
14. 新增送礼插件
15. 商品详情导航优化
16. 支付优化
17. 充值支付方式未选中修复
18. 文章,门店优化
19. 多商户和门店新增核销
20. 礼品卡卡密数据展示优化,新增扫码兑换
21. 多语言优化
22. 优惠券细节优化
## v6.2.0.52024-07-11
1. 门店基础模式优化支持(基础、商品、基础+商品)
2. 文章增加封面支持
## v6.2.0.42024-07-10
1. 商品详情更多门店追踪商品
## v6.2.0.32024-07-09
1. 多商户和门店新增核销
## v6.2.0.22024-07-06
1. 礼品卡卡密数据展示优化
2. 礼品卡新增扫码兑换
## v6.2.0.12024-07-05
1. 修复安卓app下多语言切换无效问题
## v6.2.02024-07-04
1. 新增礼品卡
2. 搜索需要登录优化
3. 登录input优化
4. 支持多语言
5. 增加企业微信客服支持
6. 增加app版本更新
7. 增加app评分提示
8. 增加关于我们页面
9. 钱包提现增加手续费
10. 首页商品魔方白色模式错误修复
11. 基础模式支持商品展示
12. 店铺简介改为富文本编辑器
13. 商品规格只有一级初始化优化
14. 购物车页面底部导航高度优化
15. 支持防伪码扫码
16. 批量下单商品规格图片支持放大查看
17. 新增骨架屏
18. 页面加载层优化
19. 排序和导航搜索展示加载
20. 订单售后页面客服优化
21. 订单开票加载错误修复
22. 新增获取用户当前位置
23. 订单售后详情入口地址错误修复
24. 其他细节优化
25. 门店和多商户客服展示优化
26. 门店新增基础模式
27. 首页中间广告展示优化
28. 独立购物车页面左上角返回按钮颜色错误修复
29. 订单和门店订单快递信息未正常展示修复
30. 钱包优化字段错误修复
31. 商品详情问答优化展示错误修复,及首页加载优化
32. 购物车滑动删除收藏数据类型错误修复
33. 设置页面未登录也可以进入
34. app新增打开权限控制管理
## v6.1.02024-04-25
1. 秒杀首页无数据报错修复
2. 商品页面批发展示优化
3. 积分商城首页优
4. 多商户和多门店客服样式优化
5. 多商户领券和组合搭配优化
6. 门店下单优化
7. 商品页面门店模式下底部操作按钮新增加载
8. 加载层logo默认读取后台配置
9. 优化支付宝小程序自定义页面标题
10. 第三方账户绑定去掉小程序入口
11. 魔方新增更多样式
12. 底部名称直接读取站点名称
13. 商品、博文轮播图片模式优化
14. 商品页批发属性未定义错误修复
15. 虚拟币页面加载细节优化
## v6.0.02024-04-15
1. 地址导入错误修复
2. 商品支持指定规格选中
3. 支付app优化首页标题优化
4. 商品规格点击支持展示模式
5. 积分商城扫码领取积分
6. 购物车顶部导航优化,适配支付宝小程序
7. 选择系统地址优化
8. 购物车,分类优化
9. 物流查询支持多包裹
10. 下单运费支持多运费选择
11. 商品是否展示和符号及单位适配
12. 多语言切换优化
13. 首页汇率切换模仿价格不改变原因修复
14. paypal支付支持
15. 无网络提示优化
16. 博客上传图片错误,首页顶部背景色,分享地址优化
17. 组件优化公共和局部组件
18. 增加虚拟币插件(可转账、转换、充值、提现、支付)
19. 登录和手机绑定优化
20. 支持密码修改
21. 支持绑定邮箱
22. 支持绑定第三方登录微信、qq、支付宝
23. 修复店铺布局错乱问题
24. 分包预加载优化
25. 打开商品速度优化
26. 首页打开速度优化
27. 购物车支持优惠明细查看
28. 地址一键导入错误修复
29. 多语言兼容头条/抖音小程序
30. pages支持多语言切换
31. 商品支持指定规格选中
## v5.0.02024-01-19
1. 支持APPIOS+Android
2. 支持多语言(中文、繁体、英文、西班牙)
3. 购物车支持优惠预计算展示
4. 购物车支持门店购物车双向切换
5. 商品页支持门店购物车展示
6. 门店详情支持默认下单类型选择提示
7. 支持博文发布
8. 新增商品评论
9. 支持订单商品表单
10. 新增下单备注预选择
11. 汇率切换支持图标
12. 订单再次购买支持回购和加购物车选择
13. APP登录支持微信、QQ、苹果、谷歌、本机手机
14. 商品分类页面带排序
15. 订单仅之前的支付方式可支付
16. 下单积分自定义
17. 首页支持地理位置选择
18. h5下首页搜搜框宽度错位优化
19. 地址复制错别字优化
20. 问答分享地址优化
21. 钱包充值页面空修复,问答评论优化
22. 博客评价开关优化
23. 用户中心样式优化
24. 分类页面H5优化
25. 兼容支付宝小程序样式
## v4.0.02023-11-10
1. 手机端全新UI、极致体验优化
2. 首页轮播支持背景色自动切换
3. 分配页面支持全部分配查看
4. 钱包支持转账
5. 新增扫码登录及付款
6. 新增商品魔方
7. 购物车新增商品展示
8. 问答支持评论和点赞
9. 客服和快捷导航优化
10. 商品详情分享和收藏优化
## v3.0.32023-09-18
1. 组合搭配支持展示商品
2. 商品列表去除封面错误提示
## v3.0.22023-09-11
1. 用户基础信息填写,昵称判断有误修复
2. 订单售后退货地址错误修复
3. 新增微信小程序隐私弹窗说明
## v3.0.12023-09-03
1. 用户基础信息web端错误修复
2. 关闭按钮多端兼容性处理
3. 下单页面积分使用提示优化
## v3.0.02023-08-28
1. 新增微信小程序可以提示完善头像及昵称
2. ipad模型下uniapp顶部错位
3. 分销支持订单信息插件及数据统计GMV维度、我的团队新增搜索条件
4. 秒杀适配v3.0新版本
5. 购物车列表默认选中
6. 购物车抛物线优化、搜索页面新增页面搜索框,分类页面支持搜索进入独立页面
7. 组合搭配支持首页展示和组建封装
8. 数据列表读取避免重复请求
9. 分类页面支持标签展示
10. 多商户支持评分、资质查看
11. 开票默认值优化
12. form表单支付错误处理
13. 购物车无效商品支持选择删除
14. 快捷加购错误修复
15. 批量下单数量更新价格错误修复
16. 单页打开领取优惠券提示
17. 分包优化
## v2.3.32023-04-10
* 商品列表使用统一组件
* 商品参数新增弹窗展示
* 初始访问登录页面优化
* 公共url打开支持地图、电话、外部小程序协议方式
* 适配手机底部横线
* 轮播兼容iphone圆角失效问题
* 用户中心菜单支持列表展示样式
* 搜索页面支持九方格和列表展示样式
* 分类支持参数指定跳转
* icon导航图标支持纯图片
* 分类和门店详情适配规格起购数及限购数
* 起购数和限购数提升 到商品规格级别
* 地址新增编号快速搜索选择
* 订单售后页面新增客服展示
* 购买和加购分离统一组件
* 提现初始错误修复
* 立即购买支持多商品
* 积分使用兑换不足提示
* 博客新增评论、点赞
* 新增组合搭配
* 新增列表快捷加入购物车
* 商品列表新增错误提示
* 商家详情新增搜索全站开关控制
* 门店详情新增扫码开关控制
* 新增多规格批量下单
* 插件分包处理
## v2.3.22022-11-30
* 门店详情支持多规格直接加购
* 分类页面支持多规格直接加购+购物车操作
* 新增会员码
* 新增钱包付款码
* 新增个人资料修改
* 新增手机号码修改
* 新增账号注销
* 新增条码二维码生成组件
* 新增用户ID展示
* 购物车分离优化
* 适配微信小程序登录新规
* 可视化新增图文、图片魔方、自定义html组件商品支持左图右文样式
* 下单时间优化、支持默认提示
## v2.3.12022-10-23
* 支付宝获取地图权限优化
* 头条小程序分类不铺满问题修复
* 优化商品分类滑动方案
* 位置选择优化
## v2.3.02022-08-16
* 左右滚动最后一个元素显示不全修复
* 适配paypal支付
* 会员购买支付优化
* 去除头条小程序自定义导航
* 新增虚拟订单快速提交订单
* 头条小程序支持一键获取手机号码登录
* 多商户首页支持自动模式
* 优惠券支持多商户
* 商品错误情况下新增返回按钮
* 限时秒杀优化
* 博客搜索页面分享优化
## v2.2.92022-07-11
* 首页插件数据支持按顺序渲染
* 商品分类支持商品列表模式展示
* 商品详情支持默认选中第一个有效规格
* 商品详情快捷加购自动返回
* 下单地址限制优化
* 多商户首页支持多种样式展示
* 商品url地址使用后端生成、购买导航新增url事件、左侧返回优化
* 登录绑定手机返回优化
* 搜索页面优化、避免返回事件重复加载
## v2.2.82022-05-20
* 活动配置首页推荐支持(图文、九方格、左右滚动)样式展示
* 多商户新增店铺认证资质展示
* 门店详情支持二级分类
* 可视化数据处理错误修复
* 退出仅清除用户信息、微信自动登录强制绑定账号循环修复
* 主题色样式class错误修复
* 适配快手小程序
## v2.2.72022-04-22
* 用户地址支持地理位置选择和地址信息智能识别
* 登录返回上一页h5支持微信自动登录
* 门店首页新增地理位置选择弹窗、门店详情优化
* 商品详情页面导航返回逻辑优化
* 首页搜索和导航固定控制优化
* 博客详情新增分享入口
* 门店详情新增返回按钮关闭开关(适合独立打包)
## v2.2.62022-04-07
* 集成新的客服系统、商品页调整为底部导航入口
* 下单可直接使用门店次卡消费
* 门店详情数据改为分页模式、提高效率
* 商品详情新增相关门店列表入口
* 商品详情底部导航购物车新增可控开关
* 商品详情页提示优化
* 新增可关闭原始购买功能、仅可进入门店购买
* 标签详情分享地址id为空修复
* 支持设置默认下单类型
* 新增商品详情购物车展示开关App.vue中设置
* 新增分享及转发使用页面设置的默认图片及系统默认图片开关App.vue中设置
## v2.2.52022-03-10
* 基础组件类库更新
* 商品详情新增智能工具插件信息提示
* 订单取消后隐藏支付按钮,细节优化
* 登录加提现优化
* 去除微信圈子组件
* 分享默认地址优化
* 新增门店独立首页和搜索页
* 新增分享及转发使用页面设置的图片开关
## v2.2.42022-02-16
* 商品海报配置优化
* 博客支持首页展示及优化
* 新增门店列表和详情
* 商品支持自定义返回
* 支持自定义购买模式
* 适配第三方登录插件
* 下单支持0元不用选择支付方式
* 下单页面支持指定时间选择
* 系统参数读取优化
## v2.2.32021-12-13
* 整体适配H5端
* 订单、钱包、会员等级支付优化适配
* 支持(账号、手机、邮箱)登录注册方式
* 分享逻辑优化全局处理
* 分销新增上级用户、阶梯返佣提示
* 新增独立新增错误页面
* 适配第三方登录插件
* 支持线下支付自定义信息展示
* 规格切换购买数量错误修复
* 富文本详情支持视频、超链接、图片预览
## v2.2.22021-11-23
* 限时秒杀插件支持独立首页
* 活动配置插件支持优惠价格设定
* 标签插件
* 中间广告插件
* 弹屏广告插件
* 哀悼插件
* 文章支持分类页面
* 博客插件
* 分销、会员等级增强版、钱包全面支持小程序
* 用户授权获取用户信息API
* 可视化索引读取错误修复
* 支持菜鸟物流查询
## v2.2.12021-10-24
* 支持微信小程序(首页、分类、购物车、用户中心、商品搜索、商品详情、订单确认、授权登录、订单管理、订单售后、商品收藏、商品足迹、直播、签到、积分商城、多商户、钱包、批发)

712
common/css/animation.css Normal file
View File

@@ -0,0 +1,712 @@
/*
* 垂直放大
*/
.scale-up-ver-bottom {
-webkit-animation: scale-up-ver-bottom 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
animation: scale-up-ver-bottom 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}
.scale-up-ver-bottom-infinite {
-webkit-animation: scale-up-ver-bottom 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite both;
animation: scale-up-ver-bottom 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite both;
}
@keyframes scale-up-ver-bottom {
0% {
-webkit-transform: scaleY(0.4);
transform: scaleY(0.4);
-webkit-transform-origin: 50%;
transform-origin: 50%;
}
80% {
-webkit-transform: scaleY(1);
transform: scaleY(1);
-webkit-transform-origin: 50%;
transform-origin: 50%;
}
85% {
-webkit-transform: rotate(-8deg);
transform: rotate(-8deg);
}
95% {
-webkit-transform: rotate(8deg);
transform: rotate(8deg);
}
100% {
-webkit-transform: rotate(0);
transform: rotate(0);
}
}
/*
* 交替垂直放大
*/
.scale-up-ver-bottom-alternate {
-webkit-animation: scale-up-ver-bottom-alternate 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) alternate both;
animation: scale-up-ver-bottom-alternate 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) alternate both;
}
.scale-up-ver-bottom-alternate-infinite {
-webkit-animation: scale-up-ver-bottom-alternate 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite alternate both;
animation: scale-up-ver-bottom-alternate 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite alternate both;
}
@keyframes scale-up-ver-bottom-alternate {
0% {
-webkit-transform: scaleY(0.4);
transform: scaleY(0.4);
-webkit-transform-origin: 0% 100%;
transform-origin: 0% 100%;
}
100% {
-webkit-transform: scaleY(1);
transform: scaleY(1);
-webkit-transform-origin: 0% 100%;
transform-origin: 0% 100%;
}
}
/*
* 中间区域放大
*/
.scale-up-ver-center {
-webkit-animation: scale-up-ver-center 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
animation: scale-up-ver-center 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}
.scale-up-ver-center-infinite {
-webkit-animation: scale-up-ver-center 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite alternate both;
animation: scale-up-ver-center 1s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite alternate both;
}
@keyframes scale-up-ver-center {
0% {
-webkit-transform: scaleY(0.4);
transform: scaleY(0.4);
}
100% {
-webkit-transform: scaleY(1);
transform: scaleY(1);
}
}
/*
* 淡出
.
*/fade-out-fwd {
-webkit-animation: fade-out-fwd 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
animation: fade-out-fwd 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
.fade-out-fwd-infinite {
-webkit-animation: fade-out-fwd 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) infinite both;
animation: fade-out-fwd 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) infinite both;
}
@keyframes fade-out-fwd {
0% {
-webkit-transform: translateZ(0);
transform: translateZ(0);
opacity: 1;
}
100% {
-webkit-transform: translateZ(80px);
transform: translateZ(80px);
opacity: 0;
}
}
/*
* 淡入
*/
.fade-in-bck {
-webkit-animation: fade-in-bck 2s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
animation: fade-in-bck 2s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}
.fade-in-bck-infinite {
-webkit-animation: fade-in-bck 2s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite both;
animation: fade-in-bck 2s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite both;
}
/*
* 淡入淡出
*/
.fade-in-bck-alternate {
-webkit-animation: fade-in-bck 2s cubic-bezier(0.390, 0.575, 0.565, 1.000) alternate both;
animation: fade-in-bck 2s cubic-bezier(0.390, 0.575, 0.565, 1.000) alternate both;
}
.fade-in-bck-alternate-infinite {
-webkit-animation: fade-in-bck 2s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite alternate both;
animation: fade-in-bck 2s cubic-bezier(0.390, 0.575, 0.565, 1.000) infinite alternate both;
}
@keyframes fade-in-bck {
0% {
-webkit-transform: translateZ(80px);
transform: translateZ(80px);
opacity: 0;
}
100% {
-webkit-transform: translateZ(0);
transform: translateZ(0);
opacity: 1;
}
}
/*
* 文字铺开
*/
.tracking-in-expand {
-webkit-animation: tracking-in-expand 2s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
animation: tracking-in-expand 2s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
}
.tracking-in-expand-infinite {
-webkit-animation: tracking-in-expand 2s cubic-bezier(0.215, 0.610, 0.355, 1.000) infinite both;
animation: tracking-in-expand 2s cubic-bezier(0.215, 0.610, 0.355, 1.000) infinite both;
}
@keyframes tracking-in-expand {
0% {
letter-spacing: -0.5em;
opacity: 0;
}
40% {
opacity: 0.6;
}
100% {
opacity: 1;
}
}
/*
* 心跳
*/
.heartbeat {
-webkit-animation: heartbeat 1s ease-in-out both;
animation: heartbeat 1s ease-in-out both;
}
.heartbeat-infinite {
-webkit-animation: heartbeat 1s ease-in-out infinite both;
animation: heartbeat 1s ease-in-out infinite both;
}
@keyframes heartbeat {
from {
-webkit-transform: scale(1);
transform: scale(1);
-webkit-transform-origin: center center;
transform-origin: center center;
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
10% {
-webkit-transform: scale(0.91);
transform: scale(0.91);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
17% {
-webkit-transform: scale(0.98);
transform: scale(0.98);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
33% {
-webkit-transform: scale(0.87);
transform: scale(0.87);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
45% {
-webkit-transform: scale(1);
transform: scale(1);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
}
/*
* 脉动
*/
.pulsate-bck {
-webkit-animation: pulsate-bck 1s ease-in-out both;
animation: pulsate-bck 1s ease-in-out both;
}
.pulsate-bck-infinite {
-webkit-animation: pulsate-bck 1s ease-in-out infinite both;
animation: pulsate-bck 1s ease-in-out infinite both;
}
@keyframes pulsate-bck {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(0.9);
transform: scale(0.9);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
/*
* 向上弹跳
*/
.bounce-top {
-webkit-animation: bounce-top 1.5s both;
animation: bounce-top 1.5s both;
}
.bounce-top-infinite {
-webkit-animation: bounce-top 1.5s infinite both;
animation: bounce-top 1.5s infinite both;
}
@keyframes bounce-top {
0%, 30%, 60%, 100% {
-webkit-transform: translateY(0);
transform: translateY(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
15% {
-webkit-transform: translateY(-40px);
transform: translateY(-40px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
45% {
-webkit-transform: translateY(-20px);
transform: translateY(-20px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
75% {
-webkit-transform: translateY(-8px);
transform: translateY(-8px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
}
/*
* 向下弹跳
*/
.bounce-bottom {
-webkit-animation: bounce-bottom 1.5s both;
animation: bounce-bottom 1.5s both;
}
.bounce-bottom-infinite {
-webkit-animation: bounce-bottom 1.5s infinite both;
animation: bounce-bottom 1.5s infinite both;
}
@keyframes bounce-bottom {
0%, 30%, 60%, 100% {
-webkit-transform: translateY(0);
transform: translateY(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
15% {
-webkit-transform: translateY(40px);
transform: translateY(40px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
45% {
-webkit-transform: translateY(20px);
transform: translateY(20px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
75% {
-webkit-transform: translateY(8px);
transform: translateY(8px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
}
/*
* 向左弹跳
*/
.bounce-left {
-webkit-animation: bounce-left 1.5s both;
animation: bounce-left 1.5s both;
}
.bounce-left-infinite {
-webkit-animation: bounce-left 1.5s infinite both;
animation: bounce-left 1.5s infinite both;
}
@keyframes bounce-left {
0%, 30%, 60%, 100% {
-webkit-transform: translateX(0);
transform: translateX(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
15% {
-webkit-transform: translateX(-40px);
transform: translateX(-40px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
45% {
-webkit-transform: translateX(-20px);
transform: translateX(-20px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
75% {
-webkit-transform: translateX(-8px);
transform: translateX(-8px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
}
/*
* 向右弹跳
*/
.bounce-right {
-webkit-animation: bounce-right 1.5s both;
animation: bounce-right 1.5s both;
}
.bounce-right-infinite {
-webkit-animation: bounce-right 1.5s infinite both;
animation: bounce-right 1.5s infinite both;
}
@keyframes bounce-right {
0%, 30%, 60%, 100% {
-webkit-transform: translateX(0);
transform: translateX(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
15% {
-webkit-transform: translateX(40px);
transform: translateX(40px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
45% {
-webkit-transform: translateX(20px);
transform: translateX(20px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
75% {
-webkit-transform: translateX(8px);
transform: translateX(8px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
}
/*
* 左侧弹跳
*/
.bounce-in-left {
-webkit-animation: bounce-in-left 2s both;
animation: bounce-in-left 2s both;
}
.bounce-in-left-infinite {
-webkit-animation: bounce-in-left 2s infinite both;
animation: bounce-in-left 2s infinite both;
}
@keyframes bounce-in-left {
0% {
-webkit-transform: translateX(-500px);
transform: translateX(-500px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
opacity: 0;
}
38% {
-webkit-transform: translateX(0);
transform: translateX(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
opacity: 1;
}
55% {
-webkit-transform: translateX(-68px);
transform: translateX(-68px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
72% {
-webkit-transform: translateX(0);
transform: translateX(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
81% {
-webkit-transform: translateX(-28px);
transform: translateX(-28px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
90% {
-webkit-transform: translateX(0);
transform: translateX(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
95% {
-webkit-transform: translateX(-8px);
transform: translateX(-8px);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
}
}
/*
* 翻转
*/
.flip-in-diag-2-tl {
-webkit-animation: flip-in-diag-2-tl 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
animation: flip-in-diag-2-tl 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
.flip-in-diag-2-tl-infinite {
-webkit-animation: flip-in-diag-2-tl 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) infinite both;
animation: flip-in-diag-2-tl 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) infinite both;
}
@keyframes flip-in-diag-2-tl {
0% {
-webkit-transform: rotate3d(-1, 1, 0, 80deg);
transform: rotate3d(-1, 1, 0, 80deg);
opacity: 0;
}
100% {
-webkit-transform: rotate3d(1, 1, 0, 0deg);
transform: rotate3d(1, 1, 0, 0deg);
opacity: 1;
}
}
/*
* 从左滑动
*/
.slide-in-left {
-webkit-animation: slide-in-left 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
animation: slide-in-left 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
.slide-in-left-infinite {
-webkit-animation: slide-in-left 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) infinite both;
animation: slide-in-left 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) infinite both;
}
@keyframes slide-in-left {
0% {
-webkit-transform: translateX(-500px);
transform: translateX(-500px);
opacity: 0;
}
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
opacity: 1;
}
}
/*
* 在fwd中心滑动
*/
.slide-in-fwd-center {
-webkit-animation: slide-in-fwd-center 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
animation: slide-in-fwd-center 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
.slide-in-fwd-center-infinite {
-webkit-animation: slide-in-fwd-center 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) infinite both;
animation: slide-in-fwd-center 2s cubic-bezier(0.250, 0.460, 0.450, 0.940) infinite both;
}
@keyframes slide-in-fwd-center {
0% {
-webkit-transform: translateZ(-1400px);
transform: translateZ(-1400px);
opacity: 0;
}
100% {
-webkit-transform: translateZ(0);
transform: translateZ(0);
opacity: 1;
}
}
/*
* 左前轮旋转
*/
.swirl-in-left-fwd {
-webkit-animation: swirl-in-left-fwd 2s ease-out both;
animation: swirl-in-left-fwd 2s ease-out both;
}
.swirl-in-left-fwd-infinite {
-webkit-animation: swirl-in-left-fwd 2s ease-out infinite both;
animation: swirl-in-left-fwd 2s ease-out infinite both;
}
@keyframes swirl-in-left-fwd {
0% {
-webkit-transform: rotate(-540deg) scale(0);
transform: rotate(-540deg) scale(0);
-webkit-transform-origin: 0 50%;
transform-origin: 0 50%;
opacity: 0;
}
100% {
-webkit-transform: rotate(0) scale(1);
transform: rotate(0) scale(1);
-webkit-transform-origin: 0 50%;
transform-origin: 0 50%;
opacity: 1;
}
}
/*
* 摇动
*/
.shake-bottom {
-webkit-animation: shake-bottom 1s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
animation: shake-bottom 1s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
}
.shake-bottom-infinite {
-webkit-animation: shake-bottom 1s cubic-bezier(0.455, 0.030, 0.515, 0.955) infinite both;
animation: shake-bottom 1s cubic-bezier(0.455, 0.030, 0.515, 0.955) infinite both;
}
@keyframes shake-bottom {
0% {
-webkit-transform: scale(1) rotate(0deg);
transform: scale(1) rotate(0deg);
-webkit-transform-origin: 50%;
transform-origin: 50%;
}
80% {
-webkit-transform: scale(1.2);
transform: scale(1.2);
-webkit-transform-origin: 50%;
transform-origin: 50%;
}
85% {
-webkit-transform: rotate(-16deg);
transform: rotate(-16deg);
}
95% {
-webkit-transform: rotate(16deg);
transform: rotate(16deg);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
-webkit-transform-origin: 50%;
transform-origin: 50%;
}
}
/*
* 震动
*/
.vibrate {
-webkit-animation: vibrate 0.3s linear both;
animation: vibrate 0.3s linear both;
}
.vibrate-infinite {
-webkit-animation: vibrate 0.3s linear infinite both;
animation: vibrate 0.3s linear infinite both;
}
@keyframes vibrate {
0% {
-webkit-transform: translate(0);
transform: translate(0);
}
20% {
-webkit-transform: translate(2px, -2px);
transform: translate(2px, -2px);
}
40% {
-webkit-transform: translate(2px, 2px);
transform: translate(2px, 2px);
}
60% {
-webkit-transform: translate(-2px, 2px);
transform: translate(-2px, 2px);
}
80% {
-webkit-transform: translate(-2px, -2px);
transform: translate(-2px, -2px);
}
100% {
-webkit-transform: translate(0);
transform: translate(0);
}
}
/*
* 模糊显示
*/
.text-focus-in {
-webkit-animation: text-focus-in 2s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
animation: text-focus-in 2s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
}
.text-focus-in-infinite {
-webkit-animation: text-focus-in 2s cubic-bezier(0.550, 0.085, 0.680, 0.530) infinite both;
animation: text-focus-in 2s cubic-bezier(0.550, 0.085, 0.680, 0.530) infinite both;
}
@keyframes text-focus-in {
0% {
-webkit-filter: blur(12px);
filter: blur(12px);
opacity: 0;
}
100% {
-webkit-filter: blur(0px);
filter: blur(0px);
opacity: 1;
}
}
/*
* 果冻
*/
.jello-horizontal {
-webkit-animation: jello-horizontal 0.9s both;
animation: jello-horizontal 0.9s both;
}
.jello-horizontal-infinite {
-webkit-animation: jello-horizontal 0.9s infinite both;
animation: jello-horizontal 0.9s infinite both;
}
@keyframes jello-horizontal {
0% {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
30% {
-webkit-transform: scale3d(1.25, 0.75, 1);
transform: scale3d(1.25, 0.75, 1);
}
40% {
-webkit-transform: scale3d(0.75, 1.25, 1);
transform: scale3d(0.75, 1.25, 1);
}
50% {
-webkit-transform: scale3d(1.15, 0.85, 1);
transform: scale3d(1.15, 0.85, 1);
}
65% {
-webkit-transform: scale3d(0.95, 1.05, 1);
transform: scale3d(0.95, 1.05, 1);
}
75% {
-webkit-transform: scale3d(1.05, 0.95, 1);
transform: scale3d(1.05, 0.95, 1);
}
100% {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}

55
common/css/business.css Normal file
View File

@@ -0,0 +1,55 @@
/*
* 面板信息 - 文本
*/
.panel-item .panel-content .item:last-child {
border: 0 !important;
padding-bottom: 0 !important;
}
.panel-item .panel-content .item .title {
width: 25%;
}
.panel-item .panel-content .item .content {
width: calc(75% - 52rpx);
min-height: 46rpx;
word-wrap: break-word;
word-break: normal;
}
.panel-item .panel-content .item .title,
.panel-item .panel-content .item .content {
line-height: 46rpx;
}
.panel-item-only .panel-content .item .content {
width: 100%;
}
/*
* 面板信息 - 图片
*/
.panel-item .panel-content-images .item {
margin: 20rpx 20rpx 0 0;
}
.panel-item .panel-content-images .item:last-child {
margin-right: 0;
}
.panel-item .panel-content-images .item image {
width: 120rpx;
height: 120rpx !important;
}
/**
* 地址边线
*/
.address-divider {
height: 4px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD4AAAAECAYAAADWIIyPAAAAkklEQVR42mP4jwR+7tr1/4OzM1Xwt46O/6SA3Yd//HeLeU0V3DXjE0H7GGCMvw8f/v/o5UUVT39KTPz/78cPoj398Omf/75Jb6ji6ZSyd/9//PxHnMdBjvyUlEQVT4MC7++DB0R7GuTIlPJ3VPE0KPAePvlDlL1gj3/r6qJaEv+5YwdJSbxn5meqJfGdh74TbS8A1dn662xhNdIAAAAASUVORK5CYII=");
background-repeat-y: no-repeat;
}
/**
* 支付html弹窗
*/
.popup-pay-html-content {
max-height: 80vh;
overflow-y: scroll;
overflow-x: hidden;
}

1267
common/css/lib.css Normal file

File diff suppressed because it is too large Load Diff

1420
common/css/page.css Normal file

File diff suppressed because it is too large Load Diff

265
common/css/plugins.css Normal file
View File

@@ -0,0 +1,265 @@
/*
* 优惠劵
*/
.plugins-coupon-container .item {
overflow: hidden;
height: 180rpx;
}
.plugins-coupon-container .v-left {
width: calc(100% - 140rpx);
padding: 30rpx 0 30rpx 20rpx;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.plugins-coupon-container .v-left .base .symbol {
font-family: Verdana, Tahoma;
}
.plugins-coupon-container .v-left .base .price {
font-weight: 700;
font-family: arial;
font-size: 76rpx;
}
.plugins-coupon-container .v-left .base .unit {
margin-left: 5rpx;
}
.plugins-coupon-container .v-left .base .desc {
margin-left: 20rpx;
}
.plugins-coupon-container .v-left .base-tips,
.plugins-coupon-container .v-left .base-time {
margin-top: 10rpx;
}
.plugins-coupon-container .v-right {
width: 140rpx;
height: 180rpx;
color: #fff;
font-weight: 500;
position: relative;
text-align: center;
}
.plugins-coupon-container .v-right:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
}
.plugins-coupon-container .v-right .circle {
display: block;
position: absolute;
left: -1px;
top: -3px;
width: 3px;
height: 180rpx;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAACpCAYAAADur4c3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3NpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3MjUzYzIwOS04ZWNlLTRlNTctODQ4OC01ZDExOTkwOGNkYmMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTM1QzgxREZGRDI5MTFFNTg3QjhGRUQ1MDY5OURERUQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTM1QzgxREVGRDI5MTFFNTg3QjhGRUQ1MDY5OURERUQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NTJiNzVkOGUtZDc2Yi00MzEzLWFmNmYtYTJkNTRlYTI4YTY1IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjcyNTNjMjA5LThlY2UtNGU1Ny04NDg4LTVkMTE5OTA4Y2RiYyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pvy+vnQAAAEqSURBVHjaYvz//z8DDDAxIAFyOVeBOAHEYfyPMDsLmXMfmfMT2YADDP8h4CEQq4A4aUDMA1LNSKZDXwJxGcg1yJaWIXOeInO+IxuwA+acK0AsA+IEADEbic7hhPOAer4DcQcQMyNb2oLMeYVsADcyZwPMObuBWBTEsQFpI9E54sjO+QvEc0F+YoHKJgHxJ2TnvEM2gBmZswrmnA1AzAXiaJPhHC1k58BNQ3bBTGTOR2QD/iJzFsH8Mw/kHxBHggzn2KA7BxzWyC5Yisz5imwACmc2LLY7QbEN4nCS4ZwAIGZFds5lUEpEdsF6nKn3PTJnAsiAV0BcBsSM5GamFCDmQXYOOJ8iu2Anzrz9HKU8ABlwDYgTKcnbo0XNaFEzWtQgipqOYVLUAAQYAKPWa4c8cIHnAAAAAElFTkSuQmCC) no-repeat;
}
.plugins-coupon-container .item-disabled .v-right {
background: #dfdfdf !important;
color: #c0c0c0 !important;
cursor: no-drop !important;
}
/**
* 标签
*/
.plugins-label {
z-index: 2;
box-sizing: border-box;
white-space: initial;
max-width: 100%;
}
.plugins-label-text {
padding: 10rpx 10rpx 0 10rpx;
}
.plugins-label .lv:not(:last-child) {
margin-right: 10rpx;
}
.plugins-label .lv {
margin-bottom: 10rpx;
}
.plugins-label-img.plugins-label-bottom-left .lv,
.plugins-label-img.plugins-label-bottom-center .lv,
.plugins-label-img.plugins-label-bottom-right .lv {
margin-bottom: 0;
margin-top: 10rpx;
}
.plugins-label .lv view {
padding: 4rpx 12rpx;
-webkit-box-shadow: 0px 1px 2px -1px rgb(0 0 0 / 60%);
box-shadow: 0px 1px 2px -1px rgb(0 0 0 / 60%);
}
.plugins-label-img image {
width: 80rpx !important;
height: 80rpx !important;
}
.plugins-label-top-left {
left: 0;
top: 0;
}
.plugins-label-top-center {
left: 0;
top: 0;
width: 100%;
text-align: center;
}
.plugins-label-top-right {
top: 0;
right: 0;
}
.plugins-label-bottom-left,
.plugins-label-bottom-center,
.plugins-label-bottom-right {
bottom: calc(100% - 380rpx);
}
.plugins-label-bottom-left {
left: 0;
}
.plugins-label-bottom-center {
width: 100%;
text-align: center;
}
.plugins-label-bottom-right {
right: 0;
}
/**
* 商品列表标签处理
*/
.goods-data-rolling-list .plugins-label-bottom-left,
.goods-data-rolling-list .plugins-label-bottom-center,
.goods-data-rolling-list .plugins-label-bottom-right {
bottom: calc(100% - 240rpx) !;
}
.goods-data-list .plugins-label-bottom-left,
.goods-data-list .plugins-label-bottom-center,
.goods-data-list .plugins-label-bottom-right {
bottom: 0;
}
/**
* 灰度样式
*/
.grayscale {
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
-o-filter: grayscale(100%);
}
/**
* 博客 - 列表
*/
.plugins-blog-list .blog-img {
width: 200rpx !important;
height: 170rpx !important;
}
.plugins-blog-list .base {
width: calc(100% - 220rpx);
}
/*
* 博客 - 滚动
*/
.plugins-blog-rolling-list swiper,
.plugins-blog-rolling-list .item .blog-img {
height: 180rpx !important;
}
.plugins-blog-rolling-list .item .blog-title {
left: 0;
bottom: 0;
width: calc(100% - 26rpx);
background-color: rgba(0, 0, 0, .5);
}
/**
* 博客 - 九方格
*/
.plugins-blog-grid-list .item {
width: calc(50% - 10rpx);
float: left;
padding-bottom: 10rpx;
margin-bottom: 20rpx;
}
.plugins-blog-grid-list .item:nth-of-type(2n + 1) {
margin-right: 10rpx;
}
.plugins-blog-grid-list .item:nth-of-type(2n) {
margin-left: 10rpx;
}
.plugins-blog-grid-list .item .blog-img {
width: 100%;
height: 220rpx !important;
}
/**
* 门店 - 数据列表
*/
.plugins-realstore-data-list .item .address-content {
width: calc(100% - 150rpx);
}
.plugins-realstore-data-list .item .address-distance {
right: 20rpx;
bottom: 18rpx;
}
.plugins-realstore-data-list .item .icon-list {
right: 24rpx;
top: 36rpx;
}
.plugins-realstore-data-list .item .icon-list .icon-item {
width: 46rpx;
height: 46rpx;
line-height: 46rpx;
}
.plugins-realstore-data-list .item .icon-item .badge-icon {
top: -10px;
right: 2px;
}
.plugins-realstore-data-list .item .icon-list .icon-item {
background: rgba(226, 44, 8, 0.08);
border-radius: 8rpx;
color: #E46248;
}
.plugins-realstore-data-list .item .icon-list .icon-item:not(:last-child) {
margin-right: 26rpx;
}
.plugins-realstore-data-list .item .logo {
width: 100rpx;
height: 100rpx !important;
}
.plugins-realstore-data-list .item .base-right {
padding-left: 14rpx;
}
.plugins-realstore-data-list .item .base-right .title {
width: calc(100% - 110rpx);
}
/**
* 多商户 - 数据列表
*/
.plugins-shop-data-list .item .logo {
width: 100rpx;
height: 100rpx !important;
}
.plugins-shop-data-list .item .item-right-icon {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
}
.plugins-shop-data-list .item .right-content {
padding-left: 14rpx;
margin-right: 50rpx;
}
.plugins-shop-data-list .item .auth-icon .icon {
width: 28rpx;
height: 28rpx !important;
margin-right: 10rpx;
}
.plugins-shop-data-list .item .desc {
min-height: 72rpx;
line-height: 36rpx;
}

838
common/css/theme.css Normal file
View File

@@ -0,0 +1,838 @@
/******************** 红色 ********************/
/**
* 边线 搭配色、次主色、主色
*/
.theme-red .border-color-main-pair {
border-color: #F6C133 !important;
}
.theme-red .border-color-main-light {
border-color: #ffdbe2 !important;
}
.theme-red .border-color-main {
border-color: #ff0036 !important;
}
/**
* 边框 搭配色、次主色、主色
*/
.theme-red .br-main-pair {
border: 1px solid #F6C133 !important;
}
.theme-red .br-main-light {
border: solid 1px #ffdbe2 !important;
}
.theme-red .br-main {
border: 1px solid #ff0036 !important;
}
/**
* 虚线边框 搭配色、次主色、主色
*/
.theme-red .br-dashed-main-pair {
border: dashed 1px #F6C133 !important;
}
.theme-red .br-dashed-main-light {
border: dashed 1px #ffdbe2 !important;
}
.theme-red .br-dashed-main {
border: dashed 1px #ff0036 !important;
}
/**
* 文本颜色 搭配色、次主色、主色
*/
.theme-red .cr-main-pair {
color: #F6C133 !important;
}
.theme-red .cr-main-light {
color: #ffdbe2 !important;
}
.theme-red .cr-main {
color: #ff0036 !important;
}
/**
* 背景色 搭配色、次主色、主色
*/
.theme-red .bg-main-pair {
background-color: #F6C133 !important;
}
.theme-red .bg-main-light {
background-color: #ffdbe2 !important;
}
.theme-red .bg-main {
background-color: #ff0036 !important;
}
/**
* 导航伪类背景色
*/
.theme-red .nav-active-line::before {
background: #ff0036;
}
.theme-red button[disabled].bg-main-pair {
background-color: #d0cbca !important;
border-color: #d0cbca !important;
color: #eaeaea !important;
}
.theme-red button[disabled].bg-main-light {
background-color: #ffe9ed !important;
border-color: #ffe9ed !important;
color: #ffa5b7 !important;
}
.theme-red button[disabled].bg-main {
background-color: #ffa7ba !important;
border-color: #ffa7ba !important;
color: #fff5f7 !important;
}
/**
* 标题左侧边线
*/
.theme-red .title-left-border::before {
background: linear-gradient(180deg, #ff0036 0%, #ffdbe2 100%);
}
/**
* 导航菜单左侧边线
*/
.theme-red .nav-left-border::before {
background: #ff0036;
}
/******************** 黄色 ********************/
/**
* 边线 搭配色、次主色、主色
*/
.theme-yellow .border-color-main-pair {
border-color: #FF0036 !important;
}
.theme-yellow .border-color-main-light {
border-color: #ffebd2 !important;
}
.theme-yellow .border-color-main {
border-color: #f6c133 !important;
}
/**
* 边框 搭配色、次主色、主色
*/
.theme-yellow .br-main-pair {
border: 1px solid #FF0036 !important;
}
.theme-yellow .br-main-light {
border: solid 1px #ffebd2 !important;
}
.theme-yellow .br-main {
border: 1px solid #f6c133 !important;
}
/**
* 虚线边框 搭配色、次主色、主色
*/
.theme-yellow .br-dashed-main-pair {
border: dashed 1px #FF0036 !important;
}
.theme-yellow .br-dashed-main-light {
border: dashed 1px #ffebd2 !important;
}
.theme-yellow .br-dashed-main {
border: dashed 1px #f6c133 !important;
}
/**
* 文本颜色 搭配色、次主色、主色
*/
.theme-yellow .cr-main-pair {
color: #FF0036 !important;
}
.theme-yellow .cr-main-light {
color: #ffebd2 !important;
}
.theme-yellow .cr-main {
color: #f6c133 !important;
}
/**
* 导航伪类背景色
*/
.theme-yellow .nav-active-line::before {
background: #f6c133;
}
/**
* 背景色 搭配色、次主色、主色
*/
.theme-yellow .bg-main-pair {
background-color: #FF0036 !important;
}
.theme-yellow .bg-main-light {
background-color: #ffebd2 !important;
}
.theme-yellow .bg-main {
background-color: #f6c133 !important;
}
.theme-yellow button[disabled].bg-main-pair {
background-color: #b5a29c !important;
border-color: #b5a29c !important;
color: #8c766f !important;
}
.theme-yellow button[disabled].bg-main-light {
background-color: #fbebd6 !important;
border-color: #fbebd6 !important;
color: #ffcc40 !important;
}
.theme-yellow button[disabled].bg-main {
background-color: #fdd178 !important;
border-color: #fdd178 !important;
color: #fff7e7 !important;
}
/**
* 标题左侧边线
*/
.theme-yellow .title-left-border::before {
background: linear-gradient(180deg, #f6c133 0%, #ffebd2 100%);
}
/**
* 导航菜单左侧边线
*/
.theme-yellow .nav-left-border::before {
background: #f6c133;
}
/******************** 黑色 ********************/
/**
* 边线 搭配色、次主色、主色
*/
.theme-black .border-color-main-pair {
border-color: #D3B881 !important;
}
.theme-black .border-color-main-light {
border-color: #dcdcdc !important;
}
.theme-black .border-color-main {
border-color: #333333 !important;
}
/**
* 边框 搭配色、次主色、主色
*/
.theme-black .br-main-pair {
border: 1px solid #D3B881 !important;
}
.theme-black .br-main-light {
border: solid 1px #dcdcdc !important;
}
.theme-black .br-main {
border: 1px solid #333333 !important;
}
/**
* 虚线边框 搭配色、次主色、主色
*/
.theme-black .br-dashed-main-pair {
border: dashed 1px #D3B881 !important;
}
.theme-black .br-dashed-main-light {
border: dashed 1px #dcdcdc !important;
}
.theme-black .br-dashed-main {
border: dashed 1px #333333 !important;
}
/**
* 文本颜色 搭配色、次主色、主色
*/
.theme-black .cr-main-pair {
color: #D3B881 !important;
}
.theme-black .cr-main-light {
color: #dcdcdc !important;
}
.theme-black .cr-main {
color: #333333 !important;
}
/**
* 背景色 搭配色、次主色、主色
*/
.theme-black .bg-main-pair {
background-color: #D3B881 !important;
}
.theme-black .bg-main-light {
background-color: #dcdcdc !important;
}
.theme-black .bg-main {
background-color: #333333 !important;
}
/**
* 导航伪类背景色
*/
.theme-black .nav-active-line::before {
background: #333333;
}
.theme-black button[disabled].bg-main-pair {
background-color: #bdece8 !important;
border-color: #bdece8 !important;
color: #edfbf9 !important;
}
.theme-black button[disabled].bg-main-light {
background-color: #efefef !important;
border-color: #efefef !important;
color: #b7b7b7 !important;
}
.theme-black button[disabled].bg-main {
background-color: #c7c7c7 !important;
border-color: #c7c7c7 !important;
color: #e2e2e2 !important;
}
/**
* 标题左侧边线
*/
.theme-black .title-left-border::before {
background: linear-gradient(180deg, #333333 0%, #dcdcdc 100%);
}
/**
* 导航菜单左侧边线
*/
.theme-black .nav-left-border::before {
background: #333333;
}
/******************** 绿色 ********************/
/**
* 边线 搭配色、次主色、主色
*/
.theme-green .border-color-main-pair {
border-color: #20A5A2 !important;
}
.theme-green .border-color-main-light {
border-color: #cce8d2 !important;
}
.theme-green .border-color-main {
border-color: #20a53a !important;
}
/**
* 边框 搭配色、次主色、主色
*/
.theme-green .br-main-pair {
border: 1px solid #20A5A2 !important;
}
.theme-green .br-main-light {
border: solid 1px #cce8d2 !important;
}
.theme-green .br-main {
border: 1px solid #20a53a !important;
}
/**
* 虚线边框 搭配色、次主色、主色
*/
.theme-green .br-dashed-main-pair {
border: dashed 1px #20A5A2 !important;
}
.theme-green .br-dashed-main-light {
border: dashed 1px #cce8d2 !important;
}
.theme-green .br-dashed-main {
border: dashed 1px #20a53a !important;
}
/**
* 文本颜色 搭配色、次主色、主色
*/
.theme-green .cr-main-pair {
color: #20A5A2 !important;
}
.theme-green .cr-main-light {
color: #cce8d2 !important;
}
.theme-green .cr-main {
color: #20a53a !important;
}
/**
* 背景色 搭配色、次主色、主色
*/
.theme-green .bg-main-pair {
background-color: #20A5A2 !important;
}
.theme-green .bg-main-light {
background-color: #cce8d2 !important;
}
.theme-green .bg-main {
background-color: #20a53a !important;
}
/**
* 导航伪类背景色
*/
.theme-green .nav-active-line::before {
background: #20a53a;
}
.theme-green button[disabled].bg-main-pair {
background-color: #cfaae2 !important;
border-color: #cfaae2 !important;
color: #e6deea !important;
}
.theme-green button[disabled].bg-main-light {
background-color: #daeade !important;
border-color: #daeade !important;
color: #9dcaa6 !important;
}
.theme-green button[disabled].bg-main {
background-color: #a8c5ae !important;
border-color: #a8c5ae !important;
color: #d8eadc !important;
}
/**
* 标题左侧边线
*/
.theme-green .title-left-border::before {
background: linear-gradient(180deg, #20a53a 0%, #cce8d2 100%);
}
/**
* 导航菜单左侧边线
*/
.theme-green .nav-left-border::before {
background: #20a53a;
}
/******************** 橙色 ********************/
/**
* 边线 搭配色、次主色、主色
*/
.theme-orange .border-color-main-pair {
border-color: #F6C133 !important;
}
.theme-orange .border-color-main-light {
border-color: #fde4d1 !important;
}
.theme-orange .border-color-main {
border-color: #fe6f04 !important;
}
/**
* 边框 搭配色、次主色、主色
*/
.theme-orange .br-main-pair {
border: 1px solid #F6C133 !important;
}
.theme-orange .br-main-light {
border: solid 1px #fde4d1 !important;
}
.theme-orange .br-main {
border: 1px solid #fe6f04 !important;
}
/**
* 虚线边框 搭配色、次主色、主色
*/
.theme-orange .br-dashed-main-pair {
border: dashed 1px #F6C133 !important;
}
.theme-orange .br-dashed-main-light {
border: dashed 1px #fde4d1 !important;
}
.theme-orange .br-dashed-main {
border: dashed 1px #fe6f04 !important;
}
/**
* 文本颜色 搭配色、次主色、主色
*/
.theme-orange .cr-main-pair {
color: #F6C133 !important;
}
.theme-orange .cr-main-light {
color: #fde4d1 !important;
}
.theme-orange .cr-main {
color: #fe6f04 !important;
}
/**
* 背景色 搭配色、次主色、主色
*/
.theme-orange .bg-main-pair {
background-color: #F6C133 !important;
}
.theme-orange .bg-main-light {
background-color: #fde4d1 !important;
}
.theme-orange .bg-main {
background-color: #fe6f04 !important;
}
/**
* 导航伪类背景色
*/
.theme-orange .nav-active-line::before {
background: #fe6f04;
}
.theme-orange button[disabled].bg-main-pair {
background-color: #b5a29c !important;
border-color: #b5a29c !important;
color: #8c766f !important;
}
.theme-orange button[disabled].bg-main-light {
background-color: #fbe9dc !important;
border-color: #fbe9dc !important;
color: #f7c49e !important;
}
.theme-orange button[disabled].bg-main {
background-color: #f7cdad !important;
border-color: #f7cdad !important;
color: #f7efea !important;
}
/**
* 标题左侧边线
*/
.theme-orange .title-left-border::before {
background: linear-gradient(180deg, #fe6f04 0%, #fde4d1 100%);
}
/**
* 导航菜单左侧边线
*/
.theme-orange .nav-left-border::before {
background: #fe6f04;
}
/******************** 蓝色 ********************/
/**
* 边线 搭配色、次主色、主色
*/
.theme-blue .border-color-main-pair {
border-color: #16CEFF !important;
}
.theme-blue .border-color-main-light {
border-color: #d1e4ff !important;
}
.theme-blue .border-color-main {
border-color: #1677ff !important;
}
/**
* 边框 搭配色、次主色、主色
*/
.theme-blue .br-main-pair {
border: 1px solid #16CEFF !important;
}
.theme-blue .br-main-light {
border: solid 1px #d1e4ff !important;
}
.theme-blue .br-main {
border: 1px solid #1677ff !important;
}
/**
* 虚线边框 搭配色、次主色、主色
*/
.theme-blue .br-dashed-main-pair {
border: dashed 1px #16CEFF !important;
}
.theme-blue .br-dashed-main-light {
border: dashed 1px #d1e4ff !important;
}
.theme-blue .br-dashed-main {
border: dashed 1px #1677ff !important;
}
/**
* 文本颜色 搭配色、次主色、主色
*/
.theme-blue .cr-main-pair {
color: #16CEFF !important;
}
.theme-blue .cr-main-light {
color: #d1e4ff !important;
}
.theme-blue .cr-main {
color: #1677ff !important;
}
/**
* 背景色 搭配色、次主色、主色
*/
.theme-blue .bg-main-pair {
background-color: #16CEFF !important;
}
.theme-blue .bg-main-light {
background-color: #d1e4ff !important;
}
.theme-blue .bg-main {
background-color: #1677ff !important;
}
/**
* 导航伪类背景色
*/
.theme-blue .nav-active-line::before {
background: #1677ff;
}
.theme-blue button[disabled].bg-main-pair {
background-color: #efcbf5 !important;
border-color: #efcbf5 !important;
color: #faf1fb !important;
}
.theme-blue button[disabled].bg-main-light {
background-color: #e3eefd !important;
border-color: #e3eefd !important;
color: #b9d3f7 !important;
}
.theme-blue button[disabled].bg-main {
background-color: #bcd3f5 !important;
border-color: #bcd3f5 !important;
color: #eef4fd !important;
}
/**
* 标题左侧边线
*/
.theme-blue .title-left-border::before {
background: linear-gradient(180deg, #1677ff 0%, #d1e4ff 100%);
}
/**
* 导航菜单左侧边线
*/
.theme-blue .nav-left-border::before {
background: #1677ff;
}
/******************** 棕色 ********************/
/**
* 边线 搭配色、次主色、主色
*/
.theme-brown .border-color-main-pair {
border-color: #8B1313 !important;
}
.theme-brown .border-color-main-light {
border-color: #eadcd2 !important;
}
.theme-brown .border-color-main {
border-color: #8B4513 !important;
}
/**
* 边框 搭配色、次主色、主色
*/
.theme-brown .br-main-pair {
border: 1px solid #8B1313 !important;
}
.theme-brown .br-main-light {
border: solid 1px #eadcd2 !important;
}
.theme-brown .br-main {
border: 1px solid #8B4513 !important;
}
/**
* 虚线边框 搭配色、次主色、主色
*/
.theme-brown .br-dashed-main-pair {
border: dashed 1px #8B1313 !important;
}
.theme-brown .br-dashed-main-light {
border: dashed 1px #eadcd2 !important;
}
.theme-brown .br-dashed-main {
border: dashed 1px #8B4513 !important;
}
/**
* 文本颜色 搭配色、次主色、主色
*/
.theme-brown .cr-main-pair {
color: #8B1313 !important;
}
.theme-brown .cr-main-light {
color: #eadcd2 !important;
}
.theme-brown .cr-main {
color: #8B4513 !important;
}
/**
* 背景色 搭配色、次主色、主色
*/
.theme-brown .bg-main-pair {
background-color: #8B1313 !important;
}
.theme-brown .bg-main-light {
background-color: #eadcd2 !important;
}
.theme-brown .bg-main {
background-color: #8B4513 !important;
}
/**
* 导航伪类背景色
*/
.theme-brown .nav-active-line::before {
background: #8B4513;
}
.theme-brown button[disabled].bg-main-pair {
background-color: #a6ded9 !important;
border-color: #a6ded9 !important;
color: #dff1ef !important;
}
.theme-brown button[disabled].bg-main-light {
background-color: #f1e6de !important;
border-color: #f1e6de !important;
color: #d6bdad !important;
}
.theme-brown button[disabled].bg-main {
background-color: #e4cdbc !important;
border-color: #e4cdbc !important;
color: #f9f4f0 !important;
}
/**
* 标题左侧边线
*/
.theme-brown .title-left-border::before {
background: linear-gradient(180deg, #8B4513 0%, #eadcd2 100%);
}
/**
* 导航菜单左侧边线
*/
.theme-brown .nav-left-border::before {
background: #8B4513;
}
/******************** 紫色 ********************/
/**
* 边线 搭配色、次主色、主色
*/
.theme-purple .border-color-main-pair {
border-color: #3C8CEC !important;
}
.theme-purple .border-color-main-light {
border-color: #d6cbfb !important;
}
.theme-purple .border-color-main {
border-color: #623cec !important;
}
/**
* 边框 搭配色、次主色、主色
*/
.theme-purple .br-main-pair {
border: 1px solid #3C8CEC !important;
}
.theme-purple .br-main-light {
border: solid 1px #d6cbfb !important;
}
.theme-purple .br-main {
border: 1px solid #623cec !important;
}
/**
* 虚线边框 搭配色、次主色、主色
*/
.theme-purple .br-dashed-main-pair {
border: dashed 1px #3C8CEC !important;
}
.theme-purple .br-dashed-main-light {
border: dashed 1px #d6cbfb !important;
}
.theme-purple .br-dashed-main {
border: dashed 1px #623cec !important;
}
/**
* 文本颜色 搭配色、次主色、主色
*/
.theme-purple .cr-main-pair {
color: #3C8CEC !important;
}
.theme-purple .cr-main-light {
color: #d6cbfb !important;
}
.theme-purple .cr-main {
color: #623cec !important;
}
/**
* 背景色 搭配色、次主色、主色
*/
.theme-purple .bg-main-pair {
background-color: #3C8CEC !important;
}
.theme-purple .bg-main-light {
background-color: #d6cbfb !important;
}
.theme-purple .bg-main {
background-color: #623cec !important;
}
/**
* 导航伪类背景色
*/
.theme-purple .nav-active-line::before {
background: #623cec;
}
.theme-purple button[disabled].bg-main-pair {
background-color: #d8aed8 !important;
border-color: #d8aed8 !important;
color: #efe4ef !important;
}
.theme-purple button[disabled].bg-main-light {
background-color: #dcd6f1 !important;
border-color: #dcd6f1 !important;
color: #b2a7dc !important;
}
.theme-purple button[disabled].bg-main {
background-color: #bdb0ef !important;
border-color: #bdb0ef !important;
color: #e7e4f1 !important;
}
/**
* 标题左侧边线
*/
.theme-purple .title-left-border::before {
background: linear-gradient(180deg, #623cec 0%, #d6cbfb 100%);
}
/**
* 导航菜单左侧边线
*/
.theme-purple .nav-left-border::before {
background: #623cec;
}

82
common/js/common/base.js Normal file
View File

@@ -0,0 +1,82 @@
export default {
methods: {
//转义符换成普通字符
escape2Html(str) {
if (!str) return str;
var arrEntities = {
'lt': '<',
'gt': '>',
'nbsp': ' ',
'amp': '&',
'quot': '"'
};
return str.replace(/&(lt|gt|nbsp|amp|quot);/ig, function(all, t) {
return arrEntities[t];
});
},
//普通字符转换成转义符
html2Escape(sHtml) {
if (!sHtml) return sHtml;
return sHtml.replace(/[<>&"]/g, function(c) {
return {
'<': '&lt;',
'>': '&gt;',
'&': '&amp;',
'"': '&quot;'
} [c];
});
},
//setData polyfill 勿删!!! (用于转换后的uniapp的项目能直接使用this.setData()函数)
setData: function(obj, callback) {
let that = this;
const handleData = (tepData, tepKey, afterKey) => {
var tepData2 = tepData;
tepKey = tepKey.split('.');
tepKey.forEach(item => {
if (tepData[item] === null || tepData[item] === undefined) {
let reg = /^[0-9]+$/;
tepData[item] = reg.test(afterKey) ? [] : {};
tepData2 = tepData[item];
} else {
tepData2 = tepData[item];
}
});
return tepData2;
};
const isFn = function(value) {
return typeof value == 'function' || false;
};
Object.keys(obj).forEach(function(key) {
let val = obj[key];
key = key.replace(/\]/g, '').replace(/\[/g, '.');
let front, after;
let index_after = key.lastIndexOf('.');
if (index_after != -1) {
after = key.slice(index_after + 1);
front = handleData(that, key.slice(0, index_after), after);
} else {
after = key;
front = that;
}
if (front.$data && front.$data[after] === undefined) {
Object.defineProperty(front, after, {
get() {
return front.$data[after];
},
set(newValue) {
front.$data[after] = newValue;
that.hasOwnProperty("$forceUpdate") && that.$forceUpdate();
},
enumerable: true,
configurable: true
});
front[after] = val;
} else {
that.$set(front, after, val);
}
});
this.$forceUpdate();
isFn(callback) && this.$nextTick(callback);
}
}
}

653
common/js/common/common.js Normal file
View File

@@ -0,0 +1,653 @@
const app = getApp();
// 数据的默认值,避免没有值的时候报错
export const old_radius = { radius: 0, radius_top_left: 0, radius_top_right: 0, radius_bottom_left: 0, radius_bottom_right: 0 };
export const old_padding = { padding: 0, padding_top: 0, padding_bottom: 0, padding_left: 0, padding_right: 0 };
export const old_margin = { margin: 0, margin_top: 0, margin_bottom: 0, margin_left: 0, margin_right: 0 };
export const old_border_and_box_shadow = { border_is_show: '0', border_color: '#FF3F3F', border_style: 'solid',border_size: { padding: 1, padding_top: 1, padding_right: 1, padding_bottom: 1, padding_left: 1, }, box_shadow_color: '', box_shadow_x: 0, box_shadow_y: 0, box_shadow_blur: 0, box_shadow_spread: 0 };
/**
* 判断一个对象是否为空。
*
* 为空的定义是对象的键的数量为0。这适用于任何对象包括普通对象、数组视为对象等。
* 该函数不直接使用`Object.keys(obj).length === 0`进行判断,而是通过封装这个逻辑来提供一个独立的、可重用的函数。
* 这样做可以增加代码的可读性和可维护性,并且抽象了对象为空的检查,使得调用者不需要关心具体的实现细节。
*
* @param obj 要检查的对象。可以是任何类型的对象,包括数组。
* @returns 如果对象为空则返回true否则返回false。
*/
export function is_obj_empty(obj) {
return Object.keys(obj).length === 0;
}
export const border_width = (style) => {
if (!style) { return 0; }
if (style.border_is_show == '1') {
const { padding_left = 0, padding_right = 0 } = style.border_size || {};
return padding_left + padding_right;
} else {
return 0;
}
}
/**
* 根据指定的条件类型和值,判断字段值是否满足条件
* @param fieldValue 字段值,可以是任何类型
* @param type 条件类型,如'contains', 'is-empty', 'greater-than'等
* @param value 用于比较的值,可以是数字或字符串
* @returns 返回一个布尔值,表示字段值是否满足指定的条件
*/
export const custom_condition_judg = (fieldValue, type, value) => {
// 处理 null 或 undefined 的情况
if (fieldValue == null) {
return true;
}
// 提前计算并缓存转换结果
const stringValue = String(fieldValue);
const valueStr = String(value);
const numberValue = Number(value);
switch (type) {
case 'contains':
case 'does-not-contain':
// 处理包含和不包含的逻辑, 如果值为空,直接返回为空
if (!isEmpty(valueStr)) {
const result = stringValue.includes(valueStr);
return type === 'contains' ? result : !result;
} else {
return true;
}
case 'is-empty':
case 'is-not-empty':
// 处理为空和不为空的逻辑
const is_Empty = ['', '{}', '[]'].includes(stringValue.trim()) || (Array.isArray(fieldValue) && fieldValue.length === 0);
return type === 'is-empty' ? is_Empty : !is_Empty;
case 'greater-than':
case 'less-than':
case 'equal':
// 根据字段值的类型,进行数字间的比较
if (typeof fieldValue === 'number') {
return compare_numbers(fieldValue, numberValue, type);
} else if (Array.isArray(fieldValue) || typeof fieldValue === 'string') {
// 如果字段值是数组或字符串,比较数组长度和指定值
const valueLength = fieldValue?.length || 0;
return compare_numbers(valueLength, numberValue, type);
} else if (typeof fieldValue === 'object') {
// 如果字段值是对象,比较对象的属性值
const numericFieldValue = Object.keys(fieldValue)?.length || 0;
return compare_numbers(numericFieldValue, numberValue, type);
}
default:
return true;
}
}
/**
* 比较两个数字的大小
* @param a 第一个数字
* @param b 第二个数字
* @param type 比较类型,如'greater-than', 'less-than', 'equal'等
* @returns 根据比较类型返回比较结果
*/
const compare_numbers = (a, b, type) => {
switch (type) {
case 'greater-than': return a > b;
case 'less-than': return a < b;
case 'equal': return a === b;
default: return false;
}
}
/**
* 根据新的样式对象计算指示器的位置样式
*
* 此函数根据指示器的新位置和当前位置以及底部距离来生成相应的CSS样式
* 它处理的是一个包含指示器位置信息的对象并返回一个字符串形式的CSS样式
*
* @param new_style 包含指示器新位置和当前位置及底部距离的样式对象
* @returns 返回计算出的指示器位置CSS样式字符串
*/
export const get_indicator_location = (new_style) => {
// 解构指示器的位置信息
const { indicator_new_location = '', indicator_location = '', indicator_bottom = 0 } = new_style;
let styles = '';
// 根据指示器的新位置是水平方向left或right还是垂直方向默认来决定如何设置样式
if (['left', 'right'].includes(indicator_new_location)) {
// 如果是水平方向根据指示器的当前位置设置top、center或bottom样式
if (indicator_location == 'flex-start') {
styles += `top: 0px;`;
} else if (indicator_location == 'center') {
styles += `top: 50%; transform: translateY(-50%);`;
} else {
styles += `bottom: 0px;`;
}
} else {
// 如果是垂直方向根据指示器的当前位置设置left、center或right样式
if (indicator_location == 'flex-start') {
styles += `left: 0px;`;
} else if (indicator_location == 'center') {
styles += `left: 50%; transform: translateX(-50%);`;
} else {
styles += `right: 0px;`;
}
}
// 如果有位置的处理,就使用指示器的位置处理,否则的话就用下边距处理
styles += `${ !isEmpty(indicator_new_location) ? `${indicator_new_location}: ${ indicator_bottom }px;` : `bottom: ${ indicator_bottom }px;` }`;
// 返回计算出的指示器位置样式
return styles;
}
/**
* 判断给定条件是否符合资格,主要用于自定义内部各个组件是否符合显示条件
* @param field_list 字段列表,包含各个字段的数据
* @param condition 条件数据,包括字段、类型和值
* @param props 额外属性,包含自定义组和数据源等信息
* @returns 返回一个布尔值,表示是否符合条件
*/
export const get_is_eligible = (field_list, condition, sourceList, isCustom, isCustomGroup, customGroupFieldId) => {
try {
// 条件加特殊标识,避免选择的时候出现重复的
let new_field = condition.field;
// 如果包含{|},则取第一个字段
if (condition.field.includes('{|}')) {
new_field = condition.field.split('{|}')[0];
}
// 获取对应条件字段的字段数据
let option = {};
if (field_list) {
// 判断是否是自定义组并且 自定义组选则了对应的数据源
if (isCustomGroup && !isEmpty(customGroupFieldId)) {
// 取出对应自定义组的内容
const group_option_list = field_list.find(item => item.field === customGroupFieldId);
// 取出自定义组内部数据源参数的详细数据
const new_field_list = group_option_list?.data || [];
// 通过对应条件,筛选出对应的数据
option = new_field_list.find(item => item.field === new_field);
} else {
option = field_list.find(item => item.field === new_field);
}
}
// 找不到对应的字段,就直接返回为成功,条件不存在
if (!isEmpty(option)) {
// 获取到字段的真实数据, option的使用主要是为了获取的他的中间参数和前缀后缀等拼接在一起
const field_value = custom_condition_data(new_field || '', option || {}, sourceList, isCustom);
// 判断条件字段是否为空并且是显示面板才会生效则直接返回true
if (!isEmpty(new_field) && !isEmpty(condition.type)) {
return custom_condition_judg(field_value, condition.type, condition.value);
} else {
return true;
}
} else {
return true;
}
} catch (error) {
return true; // 或者根据业务需求返回适当的默认值
}
}
/**
* 根据数据源ID和配置选项来处理和返回特定格式的数据
*
* @param data_source_id 数据源ID字符串可以包含多个用分号分隔的ID
* @param option 配置选项,包含数据处理的额外参数
* @param sourceList 数据源列表,用于查找和处理数据
* @param isCustom 是否为自定义模式,用于确定数据处理的方式
* @returns 返回处理后的数据字符串
*/
export const custom_condition_data = (data_source_id, option, sourceList, isCustom) => {
let data_value = '';
if (data_source_id.includes(';')) {
// 当数据源ID包含多个用分号分隔的ID时
// 取出所有的字段,使用;分割
const ids = data_source_id.split(';');
let text = '';
// 遍历每个ID处理数据并合并
ids.forEach((item, index) => {
text += data_handling(item, sourceList, isCustom) + (index != ids.length - 1 ? (option?.join || '') : '');
});
data_value = text;
} else {
// 不输入商品, 文章和品牌时,从外层处理数据
// 当数据源ID不包含分号时直接处理数据
data_value = data_handling(data_source_id, sourceList, isCustom);
}
// 如果数据是undefined或者null则设置为空字符串
if (data_value == null) {
data_value = '';
}
// 根据配置选项,添加前缀和后缀到处理后的数据
return Array.isArray(data_value) || typeof data_value === "object" ? data_value : ((option?.first || '') + data_value + (option?.last || ''));
}
/**
* 数据处理函数
* 该函数根据数据源ID和一个数据对象返回对应的图标路径
* 主要用于从复杂的数据结构中提取图标信息,根据是否是自定义图标,
* 从不同的数据层级中获取信息
*
* @param data_source_id 数据源ID用于定位图标在数据结构中的位置
* @param sourceList 包含图标数据的对象,可以是多层嵌套结构
* @param isCustom 布尔值,指示是否为自定义图标,影响数据获取的方式
* @returns 返回找到的图标路径,如果没有找到或数据为空,则返回空值
*/
const data_handling = (data_source_id, sourceList, isCustom) => {
// 不输入商品, 文章和品牌时,从外层处理数据
let new_data = get_nested_property(sourceList, data_source_id);
// 如果是商品,品牌,文章的图片, 其他的切换为从data中取数据
if (!isEmpty(sourceList.data) && isCustom) {
new_data = get_nested_property(sourceList.data, data_source_id);
}
return new_data;
}
/**
* 获取嵌套对象的属性值
*
* 该函数旨在通过指定的属性路径获取嵌套对象中的属性值它接受一个对象和一个属性路径字符串作为参数,
* 并返回对应路径的属性值如果输入的路径无效或对象中不存在该路径,则返回空字符串
*
* @param {Object} obj - 要从中获取属性的嵌套对象
* @param {string} path - 属性路径,使用点号分隔的字符串表示
* @returns {string} - 返回指定路径的属性值,如果路径无效则返回空字符串
*/
export function get_nested_property(obj, path) {
// 检查路径参数是否为字符串且非空,若不满足条件则返回空字符串
if (typeof path !== 'string' || !path) return '';
// 将属性路径字符串拆分为属性键数组
const keys = path.split('.');
// 使用reduce方法遍历属性键数组逐层访问对象属性
// 如果当前对象存在且拥有下一个属性键,则继续访问;否则返回空字符串
return keys.reduce((o, key) => (o != null && o[key] != null ? o[key] : ''), obj) ?? '';
}
/**
* 根据数据源链接ID和属性源列表生成自定义链接
*
* @param {string} data_source_link_id - 数据源链接ID可以是单个ID或多个ID以分号分隔
* @param {object} propSourceList - 包含数据源的属性列表
* @param {object} source_link_option - 链接生成的可选配置,包括首尾添加的字符串和连接符
* @returns {string} 生成的自定义链接URL
*/
export function get_custom_link(data_source_link_id, propSourceList, source_link_option) {
let url = '';
if (!data_source_link_id) {
return '';
}
// 判断数据源链接ID是否包含分号包含则表示有多个ID
if (data_source_link_id.includes(';')) {
// 分割数据源链接ID处理多个ID
const ids = data_source_link_id.split(';');
let source_url = '';
// 遍历每个ID获取对应的属性值并拼接成URL
ids.forEach((item, index) => {
// 判断数据源列表是否为空
if (!isEmpty(propSourceList.data)) {
// 从数据源列表的data属性中获取嵌套属性值并使用指定的连接符连接
source_url += get_nested_property(propSourceList.data, item) + (index != ids.length -1 ? (source_link_option?.join || '') : '');
} else {
// 直接从数据源列表中获取嵌套属性值,并使用指定的连接符连接
source_url += get_nested_property(propSourceList, item) + (index != ids.length -1 ? (source_link_option?.join || '') : '');
}
});
url = source_url;
} else {
// 处理单个ID的情况
if (!isEmpty(propSourceList.data)) {
// 从数据源列表的data属性中获取嵌套属性值作为URL
url = get_nested_property(propSourceList.data, data_source_link_id);
} else {
// 直接从数据源列表中获取嵌套属性值作为URL
url = get_nested_property(propSourceList, data_source_link_id);
}
}
// 返回最终的URL添加首尾的可选字符串
return (source_link_option?.first || '') + url + (source_link_option?.last || '');
}
/**
* 指示器的样式
*
* @param style_object 样式对象
* @returns 返回对应的值
*/
export function get_indicator_style(style_object) {
const { indicator_radius, indicator_style, indicator_size, color, indicator_new_location } = style_object;
let styles = '';
if (!isEmpty(indicator_radius)) {
styles += radius_computer(indicator_radius);
}
// 数字类型的指示器
if (indicator_style == 'num') {
styles += `color: ${color || '#DDDDDD'};`;
styles += `font-size: ${indicator_size * 2}rpx;`;
} else if (indicator_style == 'elliptic') {
// 宽的指示器按照宽高1:3 来计算
styles += `background: ${color || '#DDDDDD'};`;
if (['left', 'right'].includes(indicator_new_location)) {
styles += `width: ${indicator_size * 2 }rpx; height: ${indicator_size * 6}rpx;`;
} else {
styles += `width: ${indicator_size * 6}rpx; height: ${indicator_size * 2}rpx;`;
}
} else {
// 圆点指示器
styles += `background: ${color || '#DDDDDD'};`;
styles += `width: ${indicator_size * 2}rpx; height: ${indicator_size * 2}rpx;`;
}
return styles;
}
/**
* 指示器的位置处理
*
* @param style_object 样式对象
* @returns 返回对应的值
*/
export function get_indicator_location_style(style_object) {
const { indicator_new_location, indicator_location, indicator_bottom } = style_object;
let styles = '';
if (['left', 'right'].includes(indicator_new_location)) {
if (indicator_location == 'flex-start') {
styles += `top: 0px;`;
} else if (indicator_location == 'center') {
styles += `top: 50%; transform: translateY(-50%);`;
} else {
styles += `bottom: 0px;`;
}
} else {
if (indicator_location == 'flex-start') {
styles += `left: 0px;`;
} else if (indicator_location == 'center') {
styles += `left: 50%; transform: translateX(-50%);`;
} else {
styles += `right: 0px;`;
}
}
// 如果有位置的处理,就使用指示器的位置处理,否则的话就用下边距处理
styles += `${ !isEmpty(indicator_new_location) ? `${indicator_new_location}: ${ indicator_bottom }px;` : `bottom: ${ indicator_bottom }px;` }`;
return styles;
};
/**
* 判断对象数组等是否为空。
*/
export function isEmpty(value) {
return (
value === null ||
value === undefined ||
value === '' ||
value === NaN ||
(Array.isArray(value) && value.length === 0) ||
(typeof value === 'object' && Object.keys(value).length === 0)
)
}
/**
* 检查给定的参数是否为对象
*
* 此函数用于精确地验证一个变量是否为对象类型它通过以下步骤实现:
* 1. 特殊处理 `null` 值,因为 `null` 在 JavaScript 中被当作对象处理,但实质上它不是
* 2. 使用 `typeof` 操作符初步判断变量是否为对象
* 3. 使用 `Object.prototype.toString.call(obj)` 方法精确判断变量是否为普通的对象
*
* @param obj 未知类型的参数,待检查是否为对象
* @returns 如果参数是对象,则返回 true否则返回 false
*/
export function is_obj(obj) {
// 特殊处理 null值因为 typeof null 返回 "object",但 null 并不是我们要检查的对象
if (obj === null) return false;
// 使用 typeof 排除非对象类型
if (typeof obj !== 'object') return false;
// 确认是普通对象
return Object.prototype.toString.call(obj) === '[object Object]';
}
/**
* 渐变色的方法
* color_list: [] 渐变色的存储
* direction 渐变色的角度
* @param {string[], string} path
* @returns {string}
*/
export function gradient_computer(new_style, is_return_all = true) {
let color_list = new_style.color_list;
let direction = new_style.direction;
return gradient_handle(color_list, direction, is_return_all);
}
/**
* 根据给定的颜色列表和方向生成一个线性渐变的CSS样式字符串。
*
* @param color_list 颜色列表,包含渐变中的各个颜色值。
* @param direction 渐变的方向可以是角度或其他CSS支持的渐变方向。
* @param is_return_all 是否返回所有样式包括渐变类型、颜色列表和方向。默认为false只返回渐变样式。
* @returns 返回一个字符串,包含生成的线性渐变样式。
*/
export function gradient_handle(color_list, direction, is_return_all = true) {
let container_common_styles = ``;
if (color_list && color_list.length > 0) {
if (is_return_all) {
container_common_styles += `background:`;
}
container_common_styles += `linear-gradient(${direction || '180deg'},`;
const new_color_list = JSON.parse(JSON.stringify(color_list));
new_color_list.forEach((item, index) => {
container_common_styles += `${item.color ? item.color : 'rgb(255 255 255 / 0%)'}`;
if (color_list.length == 1) {
container_common_styles += ` ${item.color_percentage || 0}%, ${item.color ? item.color : 'rgb(255 255 255 / 0%)'} 100%`;
} else {
if (typeof item.color_percentage === 'number') {
if (index == color_list.length - 1) {
container_common_styles += ` ${item.color_percentage}%`;
} else {
container_common_styles += ` ${item.color_percentage}%,`;
}
} else {
if (index == color_list.length - 1) {
container_common_styles += ` 100%`;
} else if (index == 0) {
container_common_styles += ` 0%,`;
} else {
container_common_styles += ` ${(100 / color_list.length) * index}%,`;
}
}
}
});
container_common_styles += `)`;
if (is_return_all) {
container_common_styles += `;`;
}
}
return container_common_styles;
}
/**
* 设置内边距的方法
* new_style: 内边距的集合
* @param {string[], string} path
* @returns {string}
*/
export function padding_computer(new_style, scale = 1, is_custom = false, index) {
if (new_style) {
if (!is_custom) {
let padding_top = '';
if (index == 0) {
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
let sticky_top = 0;
// #ifdef MP
sticky_top = bar_height + 5 + 10;
// #endif
// #ifdef H5 || MP-TOUTIAO
sticky_top = bar_height + 7 + 10;
// #endif
// #ifdef APP
sticky_top = bar_height + 0 + 10;
// #endif
padding_top = `padding-top:calc(${new_style.padding_top * 2 || 0}rpx + ${sticky_top}px);`;
}
return `padding: ${new_style.padding_top * 2 || 0}rpx ${new_style.padding_right * 2 || 0}rpx ${new_style.padding_bottom * 2 || 0}rpx ${new_style.padding_left * 2 || 0}rpx;` + padding_top;
} else {
return `padding: ${new_style.padding_top * scale || 0}px ${new_style.padding_right * scale || 0}px ${new_style.padding_bottom * scale || 0}px ${new_style.padding_left * scale || 0}px;`;
}
} else {
return '';
}
}
/**
* 设置外边距的方法
* new_style: 外边距的集合
* @param {string[], string} path
* @returns {string}
*/
export function margin_computer(new_style) {
return `margin: ${new_style.margin_top * 2 || 0}rpx ${new_style.margin_right * 2 || 0}rpx ${new_style.margin_bottom * 2 || 0}rpx ${new_style.margin_left * 2 || 0}rpx;`;
}
/**
* 设置圆角的方法
* new_style: 外边距的集合
* @param {string[], string} path
* @returns {string}
*/
export function radius_computer(new_style, scale = 1, is_custom = false) {
if (new_style) {
if (!is_custom) {
return `border-radius: ${new_style.radius_top_left * 2 || 0}rpx ${new_style.radius_top_right * 2 || 0}rpx ${new_style.radius_bottom_right * 2 || 0}rpx ${new_style.radius_bottom_left * 2 || 0}rpx;`;
} else {
return `border-radius: ${new_style.radius_top_left * scale || 0}px ${new_style.radius_top_right * scale || 0}px ${new_style.radius_bottom_right * scale || 0}px ${new_style.radius_bottom_left * scale || 0}px;`;
}
} else {
return '';
}
}
/**
* 设置阴影样式
* new_style: 外边距的集合
* @param {string[], string} path
* @returns {string}
*/
export function box_shadow_computer(new_style) {
return `box-shadow: ${new_style.box_shadow_x * 2 || 0}rpx ${new_style.box_shadow_y * 2 || 0}rpx ${new_style.box_shadow_blur * 2 || 0}rpx ${new_style.box_shadow_spread * 2 || 0}rpx ${new_style.box_shadow_color || 'rgba(0,0,0,0)'};`;
}
/**
* 设置阴影样式
* new_style: 外边距的集合
* @param {string[], string} path
* @returns {string}
*/
export function background_computer(new_style) {
if (new_style.background_img.length > 0) {
let url_styke = '';
if (new_style.background_img_style == '1') {
url_styke = 'background-repeat: repeat;';
} else if (new_style.background_img_style == '2') {} else {
url_styke = `background-repeat: no-repeat;background-position: center;`;
}
switch (new_style.background_img_style) {
case '1':
url_styke = `background-repeat: no-repeat;background-position: bottom;background-size: 100% auto;`;
break;
case '2':
url_styke = `background-repeat: no-repeat;background-position: center;background-size: 100% auto;`;
break;
case '3':
url_styke = 'background-repeat: repeat;';
break;
case '4':
url_styke = 'background-size: cover;background-position: center;';
break;
default:
url_styke = `background-repeat: no-repeat;background-position: top;background-size: 100% auto;`;
break;
}
return `background-image:url(${new_style.background_img[0].url});${url_styke}`;
} else {
return '';
}
}
/**
* 计算并返回边框的样式字符串
*
* 此函数根据传入的新样式对象,决定是否显示边框以及边框的样式细节
* 如果边框需要显示,函数会构造相应的边框样式字符串,包括边框的宽度、样式和颜色;
* 如果边框不需要显示,则返回空字符串
*
* @param {Object} new_style - 新样式对象,包含边框的相关属性
* @returns {String} 边框样式字符串或空字符串
*/
export const border_computer = (new_style) => {
// 从新样式对象中解构边框的相关属性,并设置默认值
const { border_is_show = '0', border_color = '', border_style = 'solid', border_size = { padding: 0, padding_bottom: 0, padding_left: 0, padding_right: 0, padding_top: 0 } } = new_style;
// 根据边框是否需要显示的标志,决定是否构造并返回边框样式字符串
if (border_is_show == '1') {
return `border-width: ${border_size.padding_top}px ${border_size.padding_right}px ${border_size.padding_bottom}px ${border_size.padding_left}px;border-style: ${ border_style };border-color: ${border_color};`
}
// 如果边框不需要显示,返回空字符串
return '';
};
/**
* 计算并组合组件的常用样式。
*
* 该函数通过调用一系列特定样式的计算函数,来组装一个组件的常用样式字符串。
* 这些样式包括渐变色、内边距、外边距、圆角和阴影等,为组件提供了一套完整的外观定义。
*
* @param new_style 组件的新样式对象,包含了需要计算的样式属性。
* @param scale 一个缩放比例用于控制样式的缩放默认为1。
* @param scale 用于控制样式的缩放比例默认为1表示不进行缩放。
* @param is_custom 一个布尔值用于判断是否为自定义样式默认为false。
* @param index 用于标识组件的索引默认为0。
* @returns 返回一个字符串,包含了计算后的样式定义,可以被直接应用于组件的样式属性。
*/
export function common_styles_computer(new_style) {
return gradient_computer(new_style) + margin_computer(new_style) + radius_computer(new_style) + box_shadow_computer(new_style) + border_computer(new_style) + `overflow:hidden;`;
}
export function common_img_computer(new_style, index, bool) {
return padding_computer(new_style, 1, false, index, bool) + background_computer(new_style) + `overflow:hidden;box-sizing: border-box;`;
}
/**
* 生成一个随机数学字符串。
* @returns {string} 一个6位的36进制随机字符串。
*/
export function get_math() {
// 通过Math.random()生成随机数并转换为36进制的字符串
let randomString = Math.random().toString(36);
// 确保随机字符串至少有6位因为substring(2)可能会使短于6位的字符串产生错误。
// 如果字符串长度不足6位通过padStart将其前面填充为0直到长度达到6位。
randomString = randomString.length >= 6 ? randomString : randomString.padStart(6, '0');
// 截取掉随机字符串开头的'0.'部分获得最终的6位随机字符串。
return randomString.substring(2);
}
/**
* 将大小计算成百分比
*
* @param num 当前的大小或位置。
* @param size 容器的大小。
* @returns 计算后的百分比值含4位小数
*/
export const percentage_count = (num, container_size) => {
const marks = (num / container_size) * 100;
return marks.toFixed(4) + '%';
};
/**
* 计算当前偏移量
*
* @param size 当前的组件的大小宽或者高。
* @param location 容器的位置的偏移量。
* @param container_size 对应位置的容器的大小
* @returns 偏移量
*/
export const location_compute = (size, location, container_size) => {
if (size + location >= container_size) {
const deviation = container_size - size;
if (deviation >= 0) {
return deviation;
} else {
return 0;
}
} else {
return location;
}
};

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

@@ -0,0 +1,47 @@
export default {
data(){
return {
// 设置默认的分享参数、页面可自定义以下数据
// 如果页面不设置share就触发这个默认的分享
// 标题、关键字、描述、地址、参数、封面图片、视频
share_info: {
title: '',
kds: '',
desc: '',
path: '',
query: '',
img: '',
video: ''
}
}
},
// 分享给好友
onShareAppMessage() {
var app = getApp();
var share = app.globalData.share_content_handle(this.share_info || {});
var data = {
title: share.title,
desc: share.desc,
path: share.path + share.query
}
if(app.globalData.data.is_share_use_image == 1) {
data['imageUrl'] = share.img;
}
return data;
},
// 分享朋友圈
onShareTimeline() {
var app = getApp();
var share = app.globalData.share_content_handle(this.share_info || {});
var data = {
title: share.title,
query: ((share.query || null) != null && share.query.substr(0, 1) == '?') ? share.query.slice(1) : share.query
};
if(app.globalData.data.is_share_use_image == 1) {
data['imageUrl'] = share.img;
}
return data;
}
}

8
common/js/lib/base64.js Normal file
View File

@@ -0,0 +1,8 @@
/**
* Minified by jsDelivr using Terser v5.7.1.
* Original file: /npm/js-base64@3.7.2/base64.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(t,n){var r,e;"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(r=t.Base64,(e=n()).noConflict=function(){return t.Base64=r,e},t.Meteor&&(Base64=e),t.Base64=e)}("undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:this,(function(){"use strict";var t,n="3.7.2",r="function"==typeof atob,e="function"==typeof btoa,o="function"==typeof Buffer,u="function"==typeof TextDecoder?new TextDecoder:void 0,i="function"==typeof TextEncoder?new TextEncoder:void 0,f=Array.prototype.slice.call("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="),c=(t={},f.forEach((function(n,r){return t[n]=r})),t),a=/^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/,d=String.fromCharCode.bind(String),s="function"==typeof Uint8Array.from?Uint8Array.from.bind(Uint8Array):function(t,n){return void 0===n&&(n=function(t){return t}),new Uint8Array(Array.prototype.slice.call(t,0).map(n))},l=function(t){return t.replace(/=/g,"").replace(/[+\/]/g,(function(t){return"+"==t?"-":"_"}))},h=function(t){return t.replace(/[^A-Za-z0-9\+\/]/g,"")},p=function(t){for(var n,r,e,o,u="",i=t.length%3,c=0;c<t.length;){if((r=t.charCodeAt(c++))>255||(e=t.charCodeAt(c++))>255||(o=t.charCodeAt(c++))>255)throw new TypeError("invalid character found");u+=f[(n=r<<16|e<<8|o)>>18&63]+f[n>>12&63]+f[n>>6&63]+f[63&n]}return i?u.slice(0,i-3)+"===".substring(i):u},y=e?function(t){return btoa(t)}:o?function(t){return Buffer.from(t,"binary").toString("base64")}:p,A=o?function(t){return Buffer.from(t).toString("base64")}:function(t){for(var n=[],r=0,e=t.length;r<e;r+=4096)n.push(d.apply(null,t.subarray(r,r+4096)));return y(n.join(""))},b=function(t,n){return void 0===n&&(n=!1),n?l(A(t)):A(t)},g=function(t){if(t.length<2)return(n=t.charCodeAt(0))<128?t:n<2048?d(192|n>>>6)+d(128|63&n):d(224|n>>>12&15)+d(128|n>>>6&63)+d(128|63&n);var n=65536+1024*(t.charCodeAt(0)-55296)+(t.charCodeAt(1)-56320);return d(240|n>>>18&7)+d(128|n>>>12&63)+d(128|n>>>6&63)+d(128|63&n)},B=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,x=function(t){return t.replace(B,g)},C=o?function(t){return Buffer.from(t,"utf8").toString("base64")}:i?function(t){return A(i.encode(t))}:function(t){return y(x(t))},m=function(t,n){return void 0===n&&(n=!1),n?l(C(t)):C(t)},v=function(t){return m(t,!0)},U=/[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g,F=function(t){switch(t.length){case 4:var n=((7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3))-65536;return d(55296+(n>>>10))+d(56320+(1023&n));case 3:return d((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return d((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},w=function(t){return t.replace(U,F)},S=function(t){if(t=t.replace(/\s+/g,""),!a.test(t))throw new TypeError("malformed base64.");t+="==".slice(2-(3&t.length));for(var n,r,e,o="",u=0;u<t.length;)n=c[t.charAt(u++)]<<18|c[t.charAt(u++)]<<12|(r=c[t.charAt(u++)])<<6|(e=c[t.charAt(u++)]),o+=64===r?d(n>>16&255):64===e?d(n>>16&255,n>>8&255):d(n>>16&255,n>>8&255,255&n);return o},E=r?function(t){return atob(h(t))}:o?function(t){return Buffer.from(t,"base64").toString("binary")}:S,D=o?function(t){return s(Buffer.from(t,"base64"))}:function(t){return s(E(t),(function(t){return t.charCodeAt(0)}))},R=function(t){return D(T(t))},z=o?function(t){return Buffer.from(t,"base64").toString("utf8")}:u?function(t){return u.decode(D(t))}:function(t){return w(E(t))},T=function(t){return h(t.replace(/[-_]/g,(function(t){return"-"==t?"+":"/"})))},Z=function(t){return z(T(t))},j=function(t){return{value:t,enumerable:!1,writable:!0,configurable:!0}},I=function(){var t=function(t,n){return Object.defineProperty(String.prototype,t,j(n))};t("fromBase64",(function(){return Z(this)})),t("toBase64",(function(t){return m(this,t)})),t("toBase64URI",(function(){return m(this,!0)})),t("toBase64URL",(function(){return m(this,!0)})),t("toUint8Array",(function(){return R(this)}))},O=function(){var t=function(t,n){return Object.defineProperty(Uint8Array.prototype,t,j(n))};t("toBase64",(function(t){return b(this,t)})),t("toBase64URI",(function(){return b(this,!0)})),t("toBase64URL",(function(){return b(this,!0)}))},P={version:n,VERSION:"3.7.2",atob:E,atobPolyfill:S,btoa:y,btoaPolyfill:p,fromBase64:Z,toBase64:m,encode:m,encodeURI:v,encodeURL:v,utob:x,btou:w,decode:Z,isValid:function(t){if("string"!=typeof t)return!1;var n=t.replace(/\s+/g,"").replace(/={0,2}$/,"");return!/[^\s0-9a-zA-Z\+/]/.test(n)||!/[^\s0-9a-zA-Z\-_]/.test(n)},fromUint8Array:b,toUint8Array:R,extendString:I,extendUint8Array:O,extendBuiltins:function(){I(),O()},Base64:{}};return Object.keys(P).forEach((function(t){return P.Base64[t]=P[t]})),P}));
//# sourceMappingURL=/sm/79de78edcfa94236e4c8354f91262971e185c3633bb865b6fc17942e93a40207.map

View File

@@ -0,0 +1,71 @@
<template>
<view :class="theme_view">
<view v-if="(propConfig || null) != null && (propData || null) != null && propData.length > 0">
<block v-for="(floor, index) in propData" :key="index">
<block v-if="floor.goods_list.length > 0 && floor.home_data_location == propLocation">
<component-goods-list
:propData="floor"
propMoreUrlKey="url"
:propLabel="propLabel"
:propIsAutoPlay="(propConfig.is_home_auto_play || 0) == 1"
:propCurrencySymbol="propCurrencySymbol"
:propIsCartParaCurve="propIsCartParaCurve"
:propSource="propSource"
:propOpenCart="floor.style_type === '2' ? false : true"
></component-goods-list>
</block>
</block>
</view>
</view>
</template>
<script>
const app = getApp();
import componentGoodsList from '@/components/goods-list/goods-list';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
};
},
components: {
componentGoodsList,
},
props: {
propCurrencySymbol: {
type: String,
default: app.globalData.currency_symbol(),
},
propLocation: {
type: [String, Number],
default: 0,
},
propConfig: {
type: [String, Object],
default: null,
},
propData: {
type: Array,
default: [],
},
propLabel: {
type: [Array, Object, String],
default: null,
},
propIsCartParaCurve: {
type: Boolean,
default: false,
},
// 来源
propSource: {
type: String,
default: '',
},
propOpenCart: {
type: Boolean,
default: true,
},
},
methods: {},
};
</script>
<style></style>

View File

@@ -0,0 +1,242 @@
<template>
<view :class="theme_view">
<!-- 更新 -->
<view v-if="is_update_status && (update_data || null) != null" class="update-container pf left-0 top-0 wh-auto ht-auto">
<view class="update-content auto bg-white pr">
<image :src="update_alert_bg_images" mode="widthFix" class="update-alert-bg-images wh-auto"></image>
<view class="padding-top-xs padding-left-xl padding-right-xl padding-bottom-xl">
<view class="text-size-xl fw-b tc pa name">{{update_data.name}}</view>
<view class="text-size tc pa version">v{{update_data.version_new}}</view>
<scroll-view :scroll-y="true" class="content tl">
<block v-for="(item, index) in update_data.content" :key="index">
<view class="margin-bottom-sm text-size-xs">{{item}}</view>
</block>
</scroll-view>
<view class="margin-top-xl flex-row">
<button v-if="(update_data.is_force_update || 0) == 0" type="default" class="br-main bg-white cr-main round text-size-md" size="mini" @tap="update_close_event">{{$t('common.cancel')}}</button>
<button type="default" class="br-main bg-main cr-white round text-size-md" size="mini" @tap="to_update_event">{{$t('common.now_update_text')}}</button>
</view>
</view>
</view>
</view>
<!-- 评分 -->
<view v-if="is_star_status && (star_url || null) != null && (star_alert_images || null) != null" class="star-container pf left-0 top-0 wh-auto ht-auto tc">
<view class="star-content">
<image :src="star_alert_images" mode="widthFix" class="star-alert-images wh-auto" @tap="to_star_event"></image>
</view>
<view class="padding-sm margin-top-xl">
<view class="dis-inline-block" @tap="close_star_event">
<iconfont name="icon-close-o" size="30rpx" color="#ccc"></iconfont>
</view>
</view>
</view>
<!-- 关于我们中使用 -->
<!-- #ifdef APP -->
<view v-if="is_about_page && !propIsHideStar" class="margin-top">
<text class="cr-grey-9">{{app_version_info}}</text>
<block v-if="is_loading">
<text v-if="(update_data || null) == null" class="cr-grey-c margin-left-lg text-size-xs">{{$t('common.already_latest_text')}}</text>
<text v-else class="cr-blue margin-left-lg text-size-xs cp" @tap="update_event">{{$t('common.to_update_text')}}(v{{update_data.version_new}})</text>
<text v-if="(star_url || null) != null && (star_alert_images || null) != null" class="cr-blue margin-left-lg text-size-xs cp" @tap="star_event">{{$t('common.to_star_text')}}</text>
</block>
</view>
<!-- #endif -->
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
update_tips_cache_key: app.globalData.data.cache_app_update_tips_interval_time_key,
star_tips_cache_key: app.globalData.data.cache_app_star_tips_interval_time_key,
app_version_info: app.globalData.data.app_version_info,
is_about_page: false,
is_loading: false,
update_data: null,
// 更新提示
is_update_status: false,
update_tips_interval_time: 0,
update_alert_bg_images: null,
// 评分提示
is_star_status: false,
star_tips_await_time: 0,
star_tips_interval_time: 0,
star_alert_images: null,
star_url: null
};
},
props: {
propIsHideStar: {
type: Boolean,
default: false,
},
},
// 页面被展示
created: function () {
this.init();
},
methods: {
// 初始化、获取数据
init(is_init = 0) {
// #ifdef APP
// 是否关于我们页面
this.setData({
is_about_page: app.globalData.current_page(false) == 'pages/about/about',
});
// 请求接口获取版本数据
uni.request({
url: app.globalData.get_request_url('index', 'version', 'appadmin'),
method: 'POST',
data: {app_version: app.globalData.data.app_version_info},
dataType: 'json',
success: (res) => {
if(res.data.code == 0) {
var data = res.data.data;
var upd_data = {
is_loading: true,
update_data: data.update_data || null,
// 更新提示
update_alert_bg_images: data.update_alert_bg_images || null,
is_update_status: parseInt(data.is_update_status || 0) == 1,
update_tips_interval_time: parseInt(data.update_tips_interval_time || 600),
// 评分提示
is_star_status: parseInt(data.is_star_status || 0) == 1,
star_tips_await_time: parseInt(data.star_tips_await_time || 600),
star_tips_interval_time: parseInt(data.star_tips_interval_time || 1800),
star_alert_images: data.star_alert_images || null,
star_url: data.star_url || null,
};
// 当前时间
var current_time = Date.parse(new Date()) / 1000;
// 更新提示间隔时间
var update_tips_cache_time = parseInt(uni.getStorageSync(this.update_tips_cache_key) || 0);
if(update_tips_cache_time > 0 && current_time < update_tips_cache_time + upd_data.update_tips_interval_time) {
upd_data.is_update_status = false;
}
// 评分是否可以展示评分
var star_tips_cache_time = parseInt(uni.getStorageSync(this.star_tips_cache_key) || 0);
if(star_tips_cache_time > 0) {
upd_data.is_star_status = current_time > star_tips_cache_time;
}
// 首次则记录评分缓存
if(star_tips_cache_time == 0) {
uni.setStorageSync(this.star_tips_cache_key, (Date.parse(new Date()) / 1000)+upd_data.star_tips_await_time);
// 如果等待时间为0则不需要等待就提示评分
if(upd_data.star_tips_await_time == 0) {
upd_data.is_star_status = true;
}
}
// 如果已经展示更新弹窗则不展示评分弹窗
if(upd_data.is_update_status) {
upd_data.is_star_status = false;
}
// 关于我们页面则不直接展示
if(this.is_about_page) {
upd_data.is_update_status = false;
upd_data.is_star_status = false;
}
this.setData(upd_data);
}
},
fail: () => {
// 失败则再重试一次
if(is_init == 0) {
this.get_data(1);
}
}
});
// #endif
},
// 更新关闭
update_close_event(e) {
this.setData({
is_update_status: false
});
uni.setStorageSync(this.update_tips_cache_key, Date.parse(new Date()) / 1000);
},
// 打开更新事件
update_event(e) {
this.setData({
is_update_status: true
});
},
// 去更新事件
to_update_event(e) {
plus.runtime.openURL(this.update_data.update_url);
},
// 打开评分事件
star_event(e) {
this.setData({
is_star_status: true
});
},
// 去打分事件
to_star_event(e) {
// 先关闭评分
this.close_star_event();
// 打开地址
plus.runtime.openURL(this.star_url);
},
// 关闭评分事件
close_star_event(e) {
this.setData({
is_star_status: false
});
// 增加间隔时间,到时间后才会再提示
uni.setStorageSync(this.star_tips_cache_key, (Date.parse(new Date()) / 1000)+this.star_tips_interval_time);
}
}
};
</script>
<style scoped>
.update-container,
.star-container {
background-color: rgba(0, 0, 0, 0.6);
z-index: 100;
padding-top: 26vh;
}
.update-container .update-content {
width: 75vw;
padding-top: 100rpx;
border-radius: 20rpx;
}
.update-container .update-content .update-alert-bg-images {
margin-top: -180rpx;
}
.update-container .update-content .name {
left: 36rpx;
top: 60rpx;
}
.update-container .update-content .version {
left: 36rpx;
top: 140rpx;
}
.update-container .update-content .content {
max-height: 26vh;
}
.star-container .star-content {
border-radius: 20rpx;
}
.star-container .star-content .star-alert-images {
max-width: 460rpx;
}
</style>

View File

@@ -0,0 +1,103 @@
<template>
<view :class="theme_view">
<block v-if="(propData || null) != null && propData.length > 0">
<view v-for="(item, index) in propData" :key="index" class="ask-comment-item">
<view :data-value="item.url" @tap="url_event" class="flex-row cp">
<view class="title cr-white tc">{{$t('goods-list.goods-list.00n7i3')}}</view>
<view class="base-nav flex-1 flex-width margin-left-sm">
<view class="oh nav padding-bottom-sm">
<view class="flex-row jc-sb align-c">
<text class="va-m single-text flex-1 flex-width">{{ item.title || item.content }}</text>
<text class="cr-grey text-size-xs">{{$t('detail.detail.025362')}}{{ item.comments_count }}{{$t('ask-comments-goods.ask-comments-goods.xl51n6')}}</text>
</view>
<view v-if="(item.images || null) != null && item.images.length > 0" class="images oh margin-top-lg">
<block v-for="(iv, ix) in item.images" :key="ix">
<image class="br radius margin-right-sm" @tap="comment_images_show_event" :data-index="index" :data-ix="ix" :src="iv" mode="aspectFit"></image>
</block>
</view>
</view>
</view>
</view>
</view>
</block>
<block v-else>
<view class="cr-grey-d tc spacing-mb flex-row jc-c align-c">
<image :src="ask_static_url + 'no-ask.png'" mode="widthFix" class="no-ask margin-right-main" />{{$t('ask-comments-goods.ask-comments-goods.g6mc44')}}</view>
</block>
</view>
</template>
<script>
const app = getApp();
var ask_static_url = app.globalData.get_static_url('ask', true) + 'app/';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
ask_static_url:ask_static_url,
};
},
props: {
propData: {
type: Array,
default: () => {
return [];
},
},
},
created: function () {},
methods: {
// 评价图片预览
comment_images_show_event(e) {
var index = e.currentTarget.dataset.index;
var ix = e.currentTarget.dataset.ix;
uni.previewImage({
current: this.propData[index]["images"][ix],
urls: this.propData[index]["images"],
});
},
// url事件
url_event(e) {
app.globalData.url_event(e);
}
},
};
</script>
<style scoped>
/**
* 商品评价
*/
.title {
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
background: #fd9525;
border-radius: 4rpx;
}
.ask-comment-item {
padding-bottom: 10rpx;
margin-bottom: 20rpx;
}
.ask-comment-item .avatar {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
border: 1px solid #e2e2e2;
}
.ask-comment-item .base-nav {
border-bottom: 2rpx solid #f5f5f5;
}
.ask-comment-item:last-of-type {
margin-bottom: 0;
}
.ask-comment-item:last-of-type .base-nav {
border: 0;
}
.no-ask {
width: 174rpx;
}
</style>

View File

@@ -0,0 +1,55 @@
<template>
<view :class="theme_view">
<view v-if="propNumber != 0" class="am-badge">
<view :class="'am-badge-text ' + ((propNumber > 99) ? 'am-badge-text-max' : '')">
<text>{{(propNumber > 99) ? '99+' : propNumber}}</text>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
};
},
components: {},
props: {
propNumber: {
type: [Number,String],
default: 0
}
},
methods: {}
};
</script>
<style scoped>
.am-badge {
display: inline-block;
position: relative;
vertical-align: middle;
}
.am-badge-text {
display: inline-block;
position: absolute;
right: 0;
transform: translate(50%, -50%);
top: 0;
min-width: 28rpx;
padding: 0;
height: 28rpx;
line-height: 28rpx;
text-align: center;
background-color: #FF3B30;
border-radius: 40rpx;
color: #fff;
font-size: 20rpx;
padding: 2rpx 2rpx;
box-shadow: 0 0 10rpx rgb(0 0 0 / 30%);
}
.am-badge-text-max {
padding: 2rpx 4rpx;
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<view :class="theme_view">
<view v-if="((propData || null) != null)" class="plugins-binding-container">
<!-- 组合搭配 -->
<block v-if="((propData.binding_list || null) != null)">
<block v-for="(bv, bi) in propData.binding_list" :key="bi">
<view v-if="((bv.goods || null) != null) && bv.goods.length > 0" class="plugins-binding-list padding-horizontal-main padding-top-main border-radius-main oh spacing-mb">
<view class="spacing-nav-title oh">
<text class="text-wrapper">{{bv.title}}</text>
<view v-if="(bv.estimate_discount_price || 0) != 0" class="estimate-discount-price fr">
<text class="discount-icon cr-white text-size-xs">{{$t('detail.detail.6026t4')}}</text>
<text class="cr-green text-size-lg va-m">{{propCurrencySymbol}}{{bv.estimate_discount_price}}</text>
</view>
</view>
<view class="left-content fl">
<component-goods-list :propData="{style_type: 2, goods_list: bv.goods, multiple_items: 2}" :propOpenCart="false" :propLabel="propLabel" :propCurrencySymbol="propCurrencySymbol" :propIsAutoPlay="(propData.config.is_auto_play || 0) == 1"></component-goods-list>
</view>
<view class="right-content fr bs-bb padding-left-main tc">
<button type="default" size="mini" class="bg-main br-main cr-white text-size-xs round" :data-value="'/pages/plugins/binding/detail/detail?id='+bv.id" @tap="url_event">{{bv.buy_button_text}}</button>
<view class="sales-price margin-top-sm">{{propCurrencySymbol}}{{bv.estimate_price}}</view>
<view v-if="(bv.estimate_original_price || 0) != 0" class="original-price margin-top-sm">{{propCurrencySymbol}}{{bv.estimate_original_price}}</view>
</view>
</view>
</block>
</block>
<!-- 商品关联 -->
<view v-if="((propData.relevant_data || null) != null)">
<component-goods-list :propData="{title: propData.relevant_data.name, style_type: 2, goods_list: propData.relevant_data.data}" :propLabel="propLabel" :propCurrencySymbol="propCurrencySymbol" :propIsAutoPlay="(propData.config.is_auto_play_relevant || 0) == 1"></component-goods-list>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import componentGoodsList from "../goods-list/goods-list";
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
};
},
components: {
componentGoodsList
},
props: {
propCurrencySymbol: {
type: String,
default: app.globalData.currency_symbol()
},
propData: {
type: [Array,Object],
default: []
},
propLabel: {
type: [Array,Object,String],
default: null
}
},
methods: {
// url事件
url_event(e) {
app.globalData.url_event(e);
}
}
};
</script>
<style>
.plugins-binding-list {
background: linear-gradient(to right, rgb(255 235 220), rgb(241 235 255));
}
.plugins-binding-list .left-content {
width: 65%;
}
.plugins-binding-list .right-content {
width: 35%;
padding-top: 100rpx;
}
.plugins-binding-list .estimate-discount-price .discount-icon {
border-top-right-radius: 30rpx;
border-bottom-left-radius: 30rpx;
background-image: linear-gradient(45deg,#a3f9a3,#248828,#8bc34a,#d2374c,#9c27b0);
background-size: 400%;
animation: gradient 5s ease infinite;
padding: 0 16rpx;
}
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
</style>

View File

@@ -0,0 +1,173 @@
<template>
<view :class="theme_view">
<view v-if="(data_list || null) != null && data_list.length > 0" class="plugins-binding-data-list oh">
<block v-for="(item, index) in data_list" :key="index">
<view class="item border-radius-main bg-white padding-main oh pr spacing-mb">
<view class="oh flex-row" :data-value="item.url" @tap="url_event">
<image :src="item.images" mode="aspectFit" class="images dis-block border-radius-main"></image>
<view class="flex-1 flex-width flex-col jc-sb">
<view class="base-right bs-bb padding-left-main">
<view class="fw-b text-size-lg cr-base single-text">{{ item.title }}</view>
<view class="sales-price margin-top-main single-text">
<text class="text-size-xs">{{ propCurrencySymbol }}</text>
<text class="text-size-lg fw-b">{{ item.estimate_price }}</text>
</view>
<view v-if="(item.estimate_discount_price || 0) != 0" class="margin-top-sm single-text flex-row align-c">
<text class="discount-icon cr-white text-size-xs">{{$t('detail.detail.6026t4')}}</text>
<view class="cr-green single-text">
<text class="text-size-xs">{{ propCurrencySymbol }}</text>
<text class="text-size">{{ item.estimate_discount_price }}</text>
</view>
</view>
</view>
<button type="default" size="mini" class="br-main bg-main cr-white round buy-submit self-e margin-0 text-size-xs">{{ item.type_name }}{{$t('binding-list.binding-list.kh7951')}}</button>
</view>
</view>
<view class="binding-goods-list border-radius-main margin-top-main oh" :style="'height: ' + ((item.is_home_show_goods || 0) == 1 ? Math.ceil(item.goods.length / 2) * 134 + 12 : '0') + 'rpx'">
<view class="padding-horizontal-main padding-top-main padding-bottom-main oh">
<block v-for="(gv, gi) in item.goods" :key="gi">
<view class="goods-item oh margin-bottom-lg" :data-value="gv.goods_url" @tap="url_event">
<image :src="gv.images" mode="aspectFit" class="goods-images fl dis-block radius"></image>
<view class="goods-right fr bs-bb">
<view class="single-text text-size-sm">{{ gv.title }}</view>
<view v-if="(gv.show_field_price_status || 0) == 1" class="single-text">
<text class="sales-price va-m text-size-xss">{{ gv.show_price_symbol }}{{ gv.price }}</text>
<text class="cr-grey va-m text-size-xsss">{{ gv.show_price_unit }}</text>
</view>
<view v-if="(gv.discount_price || null) != null" class="single-text cr-green text-size-xss">{{$t('detail.detail.6026t4')}}{{ gv.show_price_symbol }}{{ gv.discount_price }}</view>
</view>
</view>
</block>
</view>
</view>
<view class="bg-white padding-top-main wh-auto bs-bb bottom-elastic" :class="(item.is_home_show_goods || 0) != 1 ? 'br-t-dashed' : ''" :data-index="index" @tap="item_more_goods_event">
<view class="flex-row jc-sb align-c">
<view class="cr-grey-9 text-size-xs">{{ (item.is_home_show_goods || 0) == 1 ? $t('binding-list.binding-list.2u4v35') : $t('binding-list.binding-list.91d60h') }}{{ item.type_name }}{{$t('recommend-list.recommend-list.x74z3o')}}</view>
<iconfont :name="(item.is_home_show_goods || 0) == 1 ? 'icon-arrow-top' : 'icon-arrow-bottom'" size="24rpx" color="#666"></iconfont>
</view>
</view>
</view>
</block>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
config: {},
data_list: [],
};
},
components: {},
props: {
// 价格符号
propCurrencySymbol: {
type: String,
default: app.globalData.currency_symbol()
},
propConfig: {
type: [String, Object],
default: null,
},
propData: {
type: Object,
default: () => {
return {};
},
}
},
// 属性值改变监听
watch: {
// 数据
propData(value, old_value) {
this.init();
}
},
// 页面被展示
created: function(e) {
this.init();
},
methods: {
// 初始化
init() {
var config = ((this.propConfig || null) == null ? app.globalData.get_config('plugins_base.binding.data') : this.propConfig) || {};
var data_list = ((this.propData || null) == null || (this.propData.data || null) == null || this.propData.data.length == 0) ? [] : this.propData.data;
this.setData({
config: config,
data_list: data_list,
});
},
// url事件
url_event(e) {
app.globalData.url_event(e);
},
// 商品展开关闭
item_more_goods_event(e) {
var index = e.currentTarget.dataset.index;
var temp_data = this.data_list;
temp_data[index]['is_home_show_goods'] = parseInt(temp_data[index]['is_home_show_goods'] || 0) == 1 ? 0 : 1;
this.setData({ data_list: temp_data });
},
},
};
</script>
<style scoped>
.plugins-binding-data-list .item .images {
width: 256rpx;
height: 256rpx !important;
}
.plugins-binding-data-list .item .base-right .discount-icon {
border-top-right-radius: 30rpx;
border-bottom-left-radius: 30rpx;
background-image: linear-gradient(45deg, #a3f9a3, #248828, #8bc34a, #d2374c, #9c27b0);
background-size: 400%;
animation: gradient 5s ease infinite;
padding: 0 16rpx;
}
.plugins-binding-data-list .item .buy-submit {
padding: 0 20rpx;
height: 46rpx;
line-height: 44rpx;
}
.plugins-binding-data-list .item .binding-goods-list {
background: #f8f8f8;
transition: height 0.25s ease-in-out;
}
.plugins-binding-data-list .item .binding-goods-list .goods-item {
width: calc(50% - 15rpx);
height: 102rpx;
}
.plugins-binding-data-list .item .binding-goods-list .goods-item:nth-child(odd) {
float: left;
}
.plugins-binding-data-list .item .binding-goods-list .goods-item:nth-child(even) {
float: right;
}
.plugins-binding-data-list .item .binding-goods-list .goods-item .goods-right {
width: calc(100% - 115rpx);
}
.plugins-binding-data-list .item .binding-goods-list .goods-item .goods-images {
width: 100rpx;
height: 100rpx !important;
}
.plugins-binding-data-list .item .bottom-elastic {
left: 0;
bottom: 0;
}
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
</style>

View File

@@ -0,0 +1,99 @@
<template>
<view :class="theme_view">
<view v-if="(propConfig || null) != null && (propData || null) != null && propData.length > 0">
<block v-for="(floor, index) in propData" :key="index">
<block v-if="floor.blog_list.length > 0 && floor.home_data_location == propLocation">
<view class="plugins-blog" :class="floor.style_type == 2 ? 'bg-white border-radius-main padding-main spacing-mb' : ''">
<view class="spacing-nav-title flex-row align-c jc-sb text-size-xs">
<view class="title-left">
<text class="text-wrapper" :class="floor.style_type == 2 ? '' : 'title-left-border'" :style="'color:' + (floor.color || '#333') + ';'">{{ floor.title }}</text>
<text v-if="(floor.vice_title || null) != null" class="vice-name margin-left-sm cr-grey-9">{{ floor.vice_title }}</text>
</view>
<text :data-value="floor.more_url" @tap="url_event" class="arrow-right padding-right cr-grey cp">{{$t('common.more')}}</text>
</view>
<view class="wh-auto oh pr">
<block v-if="floor.blog_list.length > 0">
<!-- 默认图文 -->
<block v-if="(floor.style_type || 0) == 0">
<view class="plugins-blog-list">
<view v-for="(item, index) in floor.blog_list" :key="index" class="item oh padding-main border-radius-main bg-white spacing-mb">
<view :data-value="item.url" @tap="url_event" class="cp">
<image class="blog-img fl radius" :src="item.cover" mode="aspectFill"></image>
<view class="base fr">
<view class="single-text text-size">{{ item.title }}</view>
<view class="cr-grey-9 margin-top-sm text-size-xs">{{ item.add_time_date_cn }}</view>
<view v-if="(item.describe || null) != null" class="cr-base multi-text margin-top-sm text-size-xs">{{item.describe}}</view>
</view>
</view>
</view>
</view>
</block>
<!-- 九方格 -->
<block v-else-if="floor.style_type == 1">
<view class="plugins-blog-grid-list">
<view v-for="(item, index) in floor.blog_list" :key="index" class="item oh border-radius-main bg-white spacing-mb">
<view :data-value="item.url" @tap="url_event" class="cp">
<image class="blog-img dis-block" :src="item.cover" mode="widthFix"></image>
<view class="base padding-horizontal-sm margin-top-sm">
<view class="goods-title multi-text margin-bottom-sm text-size-xs">{{ item.title }}</view>
<view class="cr-grey text-size-xs">{{ item.add_time_date_cn }}</view>
</view>
</view>
</view>
</view>
</block>
<!-- 滚动 -->
<view v-else-if="floor.style_type == 2" class="rolling-horizontal border-radius-main oh spacing-mb">
<view class="plugins-blog-rolling-list scroll-view-horizontal">
<swiper :vertical="false" :autoplay="(propConfig.is_home_hot_auto_play || 0) == 1" :circular="false" :display-multiple-items="floor.blog_list.length < 3 ? floor.blog_list.length : 3" interval="3000">
<block v-for="(item, index) in floor.blog_list" :key="index">
<swiper-item>
<view :data-value="item.url" @tap="url_event" class="item bg-white border-radius-main margin-right-main oh pr ht-auto pr cp">
<image class="blog-img dis-block wh-auto" :src="item.cover" mode="scaleToFill"></image>
<view class="blog-title pa single-text cr-white padding-sm text-size-xs">{{ item.title }}</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
</view>
</block>
</view>
</view>
</block>
</block>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
};
},
components: {},
props: {
propLocation: {
type: [String, Number],
default: 0,
},
propConfig: {
type: [String, Object],
default: null,
},
propData: {
type: Array,
default: [],
},
},
methods: {
// url事件
url_event(e) {
app.globalData.url_event(e);
}
}
};
</script>
<style></style>

View File

@@ -0,0 +1,56 @@
<template>
<view :class="theme_view">
<view v-if="(propStatus || false)" class="data-bottom-line">
<view class="bottom-exclude">
<view class="line-item left"></view>
<view class="line-item msg">{{propMsg || $t('bottom-line.bottom-line.44bct2')}}</view>
<view class="line-item right"></view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
};
},
components: {},
props: {
propStatus: Boolean,
propMsg: String
},
methods: {}
};
</script>
<style>
.data-bottom-line {
padding: 40rpx;
overflow: hidden;
}
.data-bottom-line .bottom-exclude {
padding-bottom: env(safe-area-inset-bottom);
}
.data-bottom-line .line-item {
width: 33.3%;
}
.data-bottom-line .line-item.left,
.data-bottom-line .line-item.right {
margin-top: 8px;
border-bottom: 1px solid #e1e1e1;
}
.data-bottom-line .line-item.msg {
color: #999;
text-align: center;
font-size: 24rpx;
}
.data-bottom-line .line-item.left,
.data-bottom-line .line-item.msg {
float: left;
}
.data-bottom-line .line-item.right {
float: right;
}
</style>

View File

@@ -0,0 +1,187 @@
<template>
<view :class="theme_view">
<view v-if="data.length > 0" class="plugins-ordergoodsform-buy oh margin-top-sm">
<view v-for="(item, index) in data" :key="index" class="item pr oh">
<view class="title dis-block single-text text-size-sm pa">{{item.title}}</view>
<view class="value dis-block pa">
<view v-if="propIsRead">{{item.content}}</view>
<input v-else :type="((item.element_arr || null) == null || (item.element_arr[1] || null) == null) ? 'text' : item.element_arr[1]"
:placeholder="(item.placeholder || null) == null ? item.title : item.placeholder"
:data-index="index"
:value="item.default_value || ''"
@blur="input_value_event"
@confirm="input_value_event"
placeholder-class="cr-grey"
:class="'radius text-size-sm padding-horizontal-sm '+((item.check_status === undefined || item.check_status === true) ? 'br' : 'br-red')"
/>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
data: [],
};
},
components: {},
props: {
propData: {
type: [Array],
default: []
},
propGoodsID: {
type: [Number,String],
default: 0
},
propIsRead: {
type: Boolean,
default: false
}
},
// 页面被展示
created: function () {
this.setData({
data: this.propData
});
},
methods: {
// 数据值事件
input_value_event(e) {
// 当前表单数据
var index = e.currentTarget.dataset.index;
var item = this.data[index];
item['default_value'] = e.detail.value.trim();
// 数据验证
var check = this.input_value_check(item, index);
// 保存数据
uni.request({
url: app.globalData.get_request_url('save', 'goods', 'ordergoodsform'),
method: 'POST',
data: {
form_id: item.form_id,
goods_id: this.propGoodsID,
title: item.title,
content: check.value
},
dataType: 'json',
success: (res) => {},
fail: (res) => {},
});
},
// 数据值验证
input_value_check(item, index) {
// 是否需要校验
var is_required = parseInt(item.is_required || 0);
var type = ((item.element_arr || null) == null || (item.element_arr[1] || null) == null) ? 'text' : item.element_arr[1];
var value = item.default_value;
var msg = item.validation_msg || item.error_message || this.$t('user-address-save.user-address-save.wkfi45');
var status = null;
// 强制填写数据,但是数据没有则错误
if(status === null && is_required == 1 && value === '')
{
status = false;
}
// 有数据则验证正确格式
if(status === null && value !== '')
{
// 根据类型验证数据
switch(type)
{
// 文本
case 'text' :
var len = value.length;
var minlength = parseInt(item.minlength || 0);
var maxlength = parseInt(item.maxlength || 0);
if((minlength > 0 && len < minlength) || (maxlength > 0 && len > maxlength))
{
status = false;
}
break;
// 整数
case 'number' :
var val = parseInt(value || 0);
var min = parseInt(item.minlength || 0);
var max = parseInt(item.maxlength || 0);
if((min > 0 && val < min) || (max > 0 && val > max))
{
value = (min > 0 && val < min) ? min : max;
status = false;
}
break;
}
// 是否有正则
if(status === null) {
var pattern = (((item.element_arr || null) == null) || (item.element_arr[2] || null) == null) ? null : item.element_arr[2];
if(pattern != null)
{
var regex = new RegExp(pattern);
if(!regex.test(value))
{
status = false;
}
}
}
}
// 是否提示错误
if(status === false) {
app.globalData.showToast(msg);
}
// 数据赋值
var temp_data = this.data;
item['default_value'] = value;
temp_data[index] = item;
temp_data[index]['check_status'] = (status === null) ? true : status;
this.setData({
data: temp_data
});
// 验证信息返回
return {status: status, value: value};
},
// 数据验证
data_check() {
var status = true;
for(var i in this.data) {
var res = this.input_value_check(this.data[i], i);
if(res.status === false) {
status = false;
break;
}
}
return status;
}
}
};
</script>
<style>
.plugins-ordergoodsform-buy .item {
height: 72rpx;
}
.plugins-ordergoodsform-buy .item .title {
width: 134rpx;
left: 0;
bottom: 8rpx;
}
.plugins-ordergoodsform-buy .item .value {
width: calc(100% - 140rpx);
left: 140rpx;
bottom: 0;
}
.plugins-ordergoodsform-buy .item .value input {
height: 50rpx;
line-height: 50rpx;
}
</style>

View File

@@ -0,0 +1,125 @@
<template>
<view :class="theme_view">
<view v-if="cart_icon_data != null && (cart_icon_data.status || 0) == 1" class="cart-para-curve-container pf round" :style="cart_icon_data.style">
<image v-if="(cart_icon_data.icon || null) != null" class="cart-para-curve-icon round br" :src="cart_icon_data.icon"></image>
<view v-else class="cart-para-curve-icon bg-red padding dis-inline-block round br"></view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
cart_icon_data: null
};
},
components: {},
props: {
propBtnHeight: {
type: Number,
default: 30
},
propBtnWidth: {
type: Number,
default: 30
},
propCart: {
type: String,
default: ''
},
},
methods: {
// 初始购物车对象、当前点击对象、图标、支持tabbar位置
init(cart, pos, icon = '', tabbar_pos = null) {
if((pos || null) != null) {
var self = this;
var btn_size = this.propBtnHeight;
var btn_width = this.propBtnWidth;
// 未指定购物车对象则读取tabbar数据自动计算购物车位置
if((cart || null) == null || (cart[0] || null) == null) {
var info = uni.getSystemInfoSync();
// 当前页面
var page = app.globalData.current_page().split('?');
switch(page[0]) {
// 商品搜索
case 'pages/goods-search/goods-search' :
var cart_top = 20;
var cart_width = 35;
var cart_left = info.screenWidth-20;
break;
// 默认购物车
default :
// 无购物车菜单则结束执行
var tabbar = app.globalData.app_tabbar_pages();
if(tabbar_pos === null) {
tabbar_pos = tabbar.indexOf('/pages/cart/cart');
if(tabbar_pos == -1) {
return false;
}
}
// 计算购物车菜单位置
var tabbar_count = tabbar.length;
var cart_top = info.screenHeight;
var cart_width = info.screenWidth/tabbar_count;
var cart_left = cart_width*tabbar_pos;
}
} else {
var temp = cart[0];
var cart_width = temp.width;
var cart_left = temp.left;
var cart_top = temp.top;
}
/* #ifndef MP-ALIPAY */
var left = pos.changedTouches[0].clientX + btn_width/2 - btn_size/2;
var top = pos.changedTouches[0].clientY - btn_size;
/* #endif */
/* #ifdef MP-ALIPAY */
var left = pos.detail.clientX + btn_width/2 - btn_size/2;
var top = pos.detail.clientY - btn_size;
/* #endif */
var x = cart_left + cart_width/2 - btn_size/2 - left;
var y = cart_top - btn_size - top;
if(self.cart_icon_data == null || (self.cart_icon_data.status || 0) == 0) {
self.setData({cart_icon_data: {
status: 1,
style: `--left:${left}px;--top:${top}px;--x:${x}px;--y:${y}px;`,
icon: icon,
}});
setTimeout(function(){
self.setData({ cart_icon_data: {status: 0}});
}, 495);
}
}
}
}
};
</script>
<style>
@keyframes moveY {
to {
transform: translateY(var(--y));
}
}
@keyframes moveX {
to {
transform: translateX(var(--x));
}
}
.cart-para-curve-container {
width: 60rpx;
height: 60rpx;
z-index: 10;
left: var(--left);
top: var(--top);
--duration: 0.5s;
animation: moveY var(--duration) cubic-bezier(0.5, -0.25, 1, 1);
}
.cart-para-curve-container .cart-para-curve-icon {
max-width: 100%;
max-height: 100%;
animation: moveX var(--duration) linear;
}
</style>

1690
components/cart/cart.vue Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
<template>
<view :class="theme_view">
<view v-if="popup_status && (propData || null) != null">
<view class="plugins-categorylimit-warm-tips-mask wh-auto ht-auto pf"></view>
<view class="plugins-categorylimit-warm-tips-content pf bg-white border-radius-main padding-xxl tc">
<image :src="propData.warm_tips_alert_images" class="dis-block wh-auto radius" mode="aspectFit"></image>
<view v-if="(propData.warm_tips_alert_msg || null) != null" class="margin-top-lg cr-base">{{propData.warm_tips_alert_msg}}</view>
<button type="button" class="bg-main br-main cr-white round text-size-md margin-top-xxxl" @tap="close_event">{{$t('common.confirm')}}</button>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
popup_status: true,
};
},
props: {
propData: {
type: [String,Object],
default: ''
}
},
methods: {
// 关闭弹窗
close_event(e) {
this.setData({
popup_status: false
});
}
}
};
</script>
<style scoped>
.plugins-categorylimit-warm-tips-mask {
left: 0;
top: 0;
background: rgb(0, 0, 0, 0.6);
z-index: 10;
}
.plugins-categorylimit-warm-tips-content {
z-index: 11;
width: calc(100% - 240rpx);
left: 50%;
top: 50%;
height: -webkit-max-content;
height: max-content;
transform: translate(-50%, -50%);
}
</style>

View File

@@ -0,0 +1,169 @@
<template>
<view :class="theme_view">
<view v-if="propIsShowAddressChoice" class="choice-location pr wh-auto oh" :style="propLocationContainerStyle" @tap.stop="choose_user_location">
<view class="flex-row gap-2 align-c oh" :style="propLocationImgContainerStyle">
<view v-if="propIsLeftIconArrow" class="dis-inline-block va-m lh">
<block v-if="(propLeftImgValue || null) != null && propLeftImgValue.length > 0">
<image :src="propLeftImgValue[0].url" class="dis-block" mode="heightFix"></image>
</block>
<block v-else>
<iconfont :name="propLeftIconValue" :size="propIconLocationSize" propClass="lh" :color="propIconLocationColor || propBaseColor" :propContainerDisplay="propContainerDisplay"></iconfont>
</block>
</view>
<view :class="'va-m dis-inline-block margin-left-xs single-text text' + (propType == 'header' ? ' text-size-md' : ' text-size-xs')" :style="'max-width:' + propTextMaxWidth + ';color:' + (propTextColor || propBaseColor) + ';'">{{ location.text || '' }}</view>
<view v-if="propIsRightIconArrow" class="va-m lh dis-inline-block margin-left-xs">
<block v-if="(propRightImgValue || null) != null && propRightImgValue.length > 0">
<image :src="propRightImgValue[0].url" class="dis-block" mode="heightFix"></image>
</block>
<block v-else>
<iconfont :name="propRightIconValue" :size="propIconArrowSize" propClass="lh-xs" :color="propIconArrowColor || propBaseColor" :propContainerDisplay="propContainerDisplay"></iconfont>
</block>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
location: {},
cloice_location_timer: null,
};
},
props: {
propIsShowAddressChoice: {
type: Boolean,
default: true,
},
propBaseColor: {
type: String,
default: '#fff',
},
propTextDefaultName: {
type: String,
default: '',
},
propTextColor: {
type: String,
default: '',
},
propTextMaxWidth: {
type: String,
default: '100%',
},
propIconLocationColor: {
type: String,
default: '',
},
propIconLocationSize: {
type: String,
default: '28rpx',
},
propIconArrowColor: {
type: String,
default: '',
},
propIconArrowSize: {
type: String,
default: '24rpx',
},
propIsLeftIconArrow: {
type: Boolean,
default: true,
},
propLeftImgValue: {
type: [Array,String],
default: '',
},
propLeftIconValue: {
type: String,
default: 'icon-location',
},
propIsRightIconArrow: {
type: Boolean,
default: true,
},
propRightImgValue: {
type: [Array,String],
default: '',
},
propRightIconValue: {
type: String,
default: 'icon-arrow-bottom',
},
propType: {
type: String,
default: 'header',
},
propLocationContainerStyle: {
type: String,
default: '',
},
propLocationImgContainerStyle: {
type: String,
default: '',
},
propContainerDisplay: {
type: String,
default: 'inline-block',
}
},
// 页面被展示
created: function () {
this.init();
},
methods: {
// 初始化
init() {
let location = app.globalData.choice_user_location_init();
if ((this.propTextDefaultName || null) != null) {
var default_name = this.$t('shopxo-uniapp.app.4v6q86');
if (location.text == default_name) {
location.text = this.propTextDefaultName;
}
}
this.setData({
location: location,
});
},
// 选择位置监听
choose_user_location(e) {
// 定时任务
clearInterval(this.cloice_location_timer);
var self = this;
var timer = setInterval(function () {
var result = app.globalData.choice_user_location_init() || null;
if (result != null && (result.status == 1 || result.status == 3)) {
self.setData({
location: result,
});
clearInterval(self.cloice_location_timer);
// 回调事件
self.$emit('onBack', result);
}
}, 1000);
this.setData({
cloice_location_timer: timer,
});
// 进入位置选择
app.globalData.choose_user_location_event();
},
},
};
</script>
<style scoped>
.choice-location {
height: 56rpx;
line-height: 56rpx;
}
.dis-block {
width: 100%;
height: 56rpx;
}
</style>

View File

@@ -0,0 +1,250 @@
<template>
<view :class="theme_view">
<!-- 底部菜单 -->
<block v-if="is_tabbar">
<component-diy-footer :propKey="key" :propValue="app_tabbar" @onFooterHeight="footer_height_value_event"></component-diy-footer>
<view v-if="propIsFooterSeat && footer_height_value > 0" :style="'height:'+footer_height_value+'rpx;'"></view>
</block>
<!-- 微信隐私提示弹窗 -->
<view v-if="is_show_privacy" class="agreement-page bs-bb pf wh-auto ht-auto left-0 top-0 z-i-deep-must">
<view class="agreement-content border-radius-main bg-white">
<view class="tc">
<image class="logo circle auto dis-block margin-bottom-lg br" :src="logo" mode="widthFix"></image>
<view class="cr-base fw-b text-size-lg">{{ title }}{{$t('common.warm_tips')}}</view>
</view>
<view class="margin-top-lg text-size-sm cr-base content-desc">
<block v-if="(privacy_content || null) == null">{{$t('agreement.agreement.w38e3v')}}{{ title }}{{$t('agreement.agreement.hjn568')}}</block>
<block v-else>{{ privacy_content }}</block>
</view>
<view class="cr-blue margin-top-lg">
<view>
<text @tap="agreement_event" data-value="userregister">{{ title }}{{$t('agreement.agreement.iy7863')}}</text>
</view>
<view class="margin-top-sm">
<text @tap="agreement_event" data-value="userprivacy">{{ title }}{{$t('agreement.agreement.jwi8n1')}}</text>
</view>
</view>
<view class="buttom tc margin-top-xxxl padding-top-lg">
<button type="default" size="mini" class="btn br-grey cr-base bg-white text-size-sm round margin-right-xxxl" @tap="exit_event">{{$t('agreement.agreement.062co8')}}</button>
<button type="default" size="mini" class="btn br-main cr-white bg-main text-size-sm round margin-left-xxxl" open-type="agreePrivacyAuthorization" @agreeprivacyauthorization="agree_privacy_auth_event">{{$t('agreement.agreement.60t34e')}}</button>
</view>
</view>
</view>
<!-- app管理 -->
<component-app-admin ref="app_admin" :propIsHideStar="true"></component-app-admin>
<!-- 用户基础 -->
<component-user-base ref="user_base" :propIsGrayscale="propIsGrayscale"></component-user-base>
<!-- 弹屏广告 -->
<component-popupscreen ref="popupscreen" :propIsGrayscale="propIsGrayscale"></component-popupscreen>
</view>
</template>
<script>
const app = getApp();
import componentDiyFooter from '@/components/diy/footer';
import componentAppAdmin from '@/components/app-admin/app-admin';
import componentUserBase from '@/components/user-base/user-base';
import componentPopupscreen from '@/components/popupscreen/popupscreen';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
logo: app.globalData.get_application_logo_square(),
title: app.globalData.get_application_title(),
is_show_privacy: false,
privacy_content: null,
key: '',
is_tabbar: false,
app_tabbar: null,
footer_height_value: 0,
};
},
props: {
// 是否灰度
propIsGrayscale: {
type: Boolean,
default: false,
},
// 是否显示底部菜单占位
propIsFooterSeat: {
type: Boolean,
default: true,
},
// 是否引入app管理
propIsAppAdmin: {
type: Boolean,
default: true,
},
// 是否引入用户基础信息提示
propIsUserBase: {
type: Boolean,
default: true,
},
// 是否引入弹屏广告
propIsPopupscreen: {
type: Boolean,
default: true,
},
},
components: {
componentDiyFooter,
componentAppAdmin,
componentUserBase,
componentPopupscreen
},
// 页面被展示
created: function () {
// 初始化配置
this.init_config();
},
methods: {
// 显示响应方法
on_show(params = {}) {
//隐藏系统tabbar
if(app.globalData.data.is_use_native_tabbar != 1) {
app.globalData.system_hide_tabbar();
}
// 初始化配置
this.init_config(false, params);
// 系统底部菜单
this.footer_init();
},
// 初始化配置
init_config(status = false, params = {}) {
if ((status || false) == true) {
// 初始化数据
this.init(params);
} else {
app.globalData.is_config(this, 'init_config', params);
}
},
// 初始化数据
init(params = {}) {
// 系统底部菜单
this.footer_init();
// app管理
if (this.propIsAppAdmin && (this.$refs.app_admin || null) != null) {
this.$refs.app_admin.init(params);
}
// 用户头像和昵称设置提示
if (this.propIsUserBase && (this.$refs.user_base || null) != null) {
this.$refs.user_base.init(params);
}
// 弹屏广告
if (this.propIsPopupscreen && (this.$refs.popupscreen || null) != null) {
this.$refs.popupscreen.init(params);
}
// #ifdef MP-WEIXIN
// 微信协议验证
if (app.globalData.data.is_weixin_privacy_setting == 1) {
uni.getPrivacySetting({
success: (res) => {
if (res.needAuthorization) {
this.setData({
is_show_privacy: true,
privacy_content: app.globalData.get_config('config.common_app_mini_weixin_privacy_content', null),
});
}
}
});
}
// #endif
},
// 底部菜单初始化
footer_init(status = 0) {
var is_use_native_tabbar = app.globalData.data.is_use_native_tabbar == 1;
var upd_data = {
is_tabbar: is_use_native_tabbar ? false : app.globalData.is_tabbar_pages()
};
if(upd_data['is_tabbar']) {
upd_data['key'] = Math.random();
upd_data['app_tabbar'] = app.globalData.get_config('app_tabbar') || null;
}
this.setData(upd_data);
// 如果没有菜单数据则读取一次
if(!is_use_native_tabbar && status == 0 && (upd_data['app_tabbar'] || null) == null) {
app.globalData.init_config(0, this, 'footer_init', 1);
}
},
// 底部菜单高度回调事件
footer_height_value_event(value) {
this.setData({
footer_height_value: (value*2)+20
});
this.$emit('onFooterHeight', value);
// 存储底部菜单高度
app.globalData.app_system_tabbar_height_save(value);
},
// 协议事件
agreement_event(e) {
var value = e.currentTarget.dataset.value || null;
if (value == null) {
app.globalData.showToast(this.$t('login.login.4wc3hr'));
return false;
}
// 是否存在协议 url 地址
var key = 'agreement_' + value + '_url';
var url = app.globalData.get_config('config.' + key) || null;
if (url == null) {
app.globalData.showToast(this.$t('login.login.x0nxxf'));
return false;
}
// 打开 webview
app.globalData.open_web_view(url);
},
// 授权回调
agree_privacy_auth_event() {
this.setData({
is_show_privacy: false
});
},
// 退出小程序
exit_event(e) {
uni.exitMiniProgram();
},
}
};
</script>
<style scoped>
.agreement-page {
background-color: rgba(0, 0, 0, 0.6);
height: 100vh;
padding: 40rpx;
}
.agreement-content {
padding: 40rpx;
position: absolute;
top: 15%;
width: calc(100% - 160rpx);
}
.agreement-content .logo {
width: 160rpx;
height: 160rpx;
}
.agreement-content .content-desc {
line-height: 46rpx;
max-height: calc(30vh);
overflow-y: auto;
}
.agreement-content .buttom .btn {
min-width: 200rpx;
}
</style>

View File

@@ -0,0 +1,33 @@
<template>
<view :class="theme_view">
<view class="copyright">
<view class="text">{{application_title}} {{version}}</view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
application_title: app.globalData.get_application_title(),
version: app.globalData.data.version
};
},
components: {},
props: {},
methods: {}
};
</script>
<style>
.copyright {
color: #cfcfcf;
text-align: center;
padding: 20rpx 0;
}
.copyright .text {
font-size: 26rpx;
font-weight: 400;
}
</style>

View File

@@ -0,0 +1,211 @@
<template>
<view :class="theme_view">
<view class="countdown" v-if="is_show && !is_end">
<block v-if="propMsecShow">
<view class="fr time" :style="time_style">{{ msec }}</view>
<view class="fr ds" :style="ds_style">{{ propSecondDs }}</view>
</block>
<view class="fr time" :style="time_style">{{ second }}</view>
<view class="fr ds" :style="ds_style">{{ propMinuteDs }}</view>
<view class="fr time" :style="time_style">{{ minute }}</view>
<view class="fr ds" :style="ds_style">{{ propHourDs }}</view>
<view class="fr time" :style="time_style">{{ hour }}</view>
</view>
<view v-if="is_show && is_end" class="timer-title">{{ propMsg || this.$t('index.index.443683') }}</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
hour: '00',
minute: '00',
second: '00',
msec: 0,
is_show: true,
is_end: false,
timer: null,
timers: null,
};
},
components: {},
props: {
propHour: {
type: [String, Number],
default: '00',
},
propMinute: {
type: [String, Number],
default: '00',
},
propSecond: {
type: [String, Number],
default: '00',
},
propEndShow: {
type: Boolean,
default: false,
},
propMsecShow: {
type: Boolean,
default: false,
},
propMsg: {
type: String,
default: '',
},
propHourDs: {
type: String,
default: ':',
},
propMinuteDs: {
type: String,
default: ':',
},
propSecondDs: {
type: String,
default: ':',
},
propTimePadding: {
type: [Number, String],
default: 0,
},
propTimeSize: {
type: [Number, String],
default: 24,
},
propTimeBackgroundColor: {
type: String,
default: 'linear-gradient(180deg, #FF601B 0%, #FE1B33 100%);',
},
propTimeColor: {
type: String,
default: '#FFF',
},
propTimeWeight: {
type: [Number, String],
default: '400',
},
propDsColor: {
type: String,
default: '#4B5459',
},
propDsSize: {
type: [Number, String],
default: 24,
},
propDsWeight: {
type: [Number, String],
default: '400',
},
},
computed: {
time_style() {
return 'padding: 0 ' + this.propTimePadding + 'rpx;background:' + this.propTimeBackgroundColor + ';color:' + this.propTimeColor + ';font-size:' + this.propTimeSize + 'rpx;font-weight:' + this.propTimeWeight;
},
ds_style() {
return 'color:' + this.propDsColor + ';font-size:' + this.propDsSize + 'rpx;font-weight:' + this.propDsWeight;
},
},
created: function (e) {
// 参数处理
this.hour = this.propHour;
this.minute = this.propMinute;
this.second = this.propSecond;
// 定时处理
this.countdown();
},
// #ifndef VUE2
destroyed() {
clearInterval(this.timer);
clearInterval(this.timers);
},
// #endif
// #ifdef VUE3
unmounted() {
clearInterval(this.timer);
clearInterval(this.timers);
},
// #endif
methods: {
// 倒计时处理
countdown() {
// 销毁之前的任务
clearInterval(this.timer);
clearInterval(this.timers);
// 秒
var self = this;
var hour = parseInt(self.hour);
var minute = parseInt(self.minute);
var second = parseInt(self.second);
self.timer = setInterval(function () {
if (second <= 0) {
if (minute > 0) {
minute--;
second = 59;
} else if (hour > 0) {
hour--;
minute = 59;
second = 59;
}
} else {
second--;
}
self.hour = hour < 10 ? '0' + hour : hour;
self.minute = minute < 10 ? '0' + minute : minute;
self.second = second < 10 ? '0' + second : second;
if (self.propHour <= 0 && self.propMinute <= 0 && self.propSecond <= 0) {
// 停止时间
clearInterval(self.timer);
clearInterval(self.timers);
self.is_end = true;
// 活动已结束、是否结束还展示
if (!self.propEndShow) {
self.is_show = false;
}
}
}, 1000);
// 毫秒
var count = 0;
self.timers = setInterval(function () {
count++;
self.msec = count;
if (count > 9) {
count = 0;
}
if (!self.is_show) {
clearInterval(self.timers);
}
}, 100);
},
},
};
</script>
<style scoped>
.countdown {
line-height: 38rpx;
}
.countdown .timer-title {
color: #666;
margin-right: 10rpx;
}
.countdown .time {
padding: 0;
-moz-border-radius: 8rpx;
border-radius: 8rpx;
color: #fff;
min-width: 40rpx;
text-align: center;
}
.countdown .ds {
color: #4b5459;
padding: 0 8rpx;
}
</style>

View File

@@ -0,0 +1,230 @@
<template>
<view :class="theme_view">
<view class="padding-bottom padding-horizontal-main">
<view class="coupon-card oh pr flex-row">
<view class="card-left flex-col jc-sa align-c" :class="propStatusType > 3 ? 'failure cr-grey-9' : 'cr-white'">
<view class="price">
<text v-if="propData.type == '0'" class="symbol text-size">{{ currency_symbol }}</text>
<text class="num text-size-xxl">{{ propData.discount_value }}</text>
<text v-if="propData.type !== '0'" class="unit text-size-md">{{ propData.type_unit }}</text>
</view>
<text v-if="(propData.desc || null) != null" class="desc text-size-xs single-text">{{ propData.desc }}</text>
</view>
<view class="card-right flex-1 flex-width flex-row jc-sb align-c" :class="propStatusType > 3 ? 'failure cr-grey-9' : ''">
<view class="card-info flex-1 flex-width padding-right-main" :class="propStatusType > 3 ? 'failure cr-grey-9' : 'cr-black'">
<view class="title text-size-lg single-text" :class="propData.time_start">{{ propData.use_limit_type_name }}</view>
<view v-if="propStartTime && propEndTime" class="date text-size-xs cr-grey-9 single-text padding-top-sm">{{ propStartTime }}-{{ propEndTime }}</view>
<view v-if="propIsProgress && propData.process_data" class="progress padding-top-sm flex-row align-c">
<block v-if="propData.process_data.type == 0">
<text class="text-size-xs cr-grey-9"> {{ propData.process_data.msg }} </text>
</block>
<block v-else>
<progress class="flex-1" :percent="propData.process_data.value" stroke-width="6" activeColor="#FF7004" backgroundColor="#fff" border-radius="3" />
<view class="percent text-size-xss cr-grey-9 padding-left-main"> {{ propData.process_data.msg }} </view>
</block>
</view>
<view v-if="propData.expire_tips" class="padding-top-sm text-size-xs cr-red">{{ propData.expire_tips }}</view>
</view>
<view class="card-type">
<!-- 按钮状态 0-领取1-已领取2-已抢完3-去使用,4-已使用5-已过期 -->
<view v-if="propStatusType == 0" class="card-btn dis-inline-block cr-white" @tap="receive">{{ propStatusOperableName || this.$t('coupon-card.coupon-card.m9316y') }}</view>
<view v-else-if="propStatusType == 1" class="card-btn dis-inline-block cr-red br-red received">{{ propStatusOperableName || this.$t('coupon-card.coupon-card.m9316y') }}</view>
<view v-else-if="propStatusType == 2" class="card-btn dis-inline-block cr-white robbed">{{ propStatusOperableName || this.$t('coupon-card.coupon-card.m9316y') }}</view>
<view v-else-if="propStatusType == 3" :data-value="home_page_url" @tap="url_event" class="cp">
<view class="card-btn dis-inline-block cr-white">
{{ propStatusOperableName || this.$t('coupon-card.coupon-card.m9316y') }}
</view>
</view>
<view v-else-if="propStatusType == 4" class="card-image pa top-0 right-0">
<image class="image" :src="coupon_static_url + 'coupon-used.png'" mode="scaleToFill"></image>
</view>
<view v-else-if="propStatusType == 5" class="card-image pa top-0 right-0">
<image class="image" :src="coupon_static_url + 'coupon-expire.png'" mode="scaleToFill"></image>
</view>
<view v-else @tap="receive">{{$t('coupon-card.coupon-card.j318xx')}}</view>
</view>
</view>
<view class="card-circle-top" :style="{ background: `${propBg}` }"></view>
<view class="card-circle-bottom" :style="{ background: `${propBg}` }"></view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
var coupon_static_url = app.globalData.get_static_url('coupon', true);
var tabbar_pages = app.globalData.app_tabbar_pages();
export default {
name: 'coupon-card',
props: {
propData: {
type: Object,
default: () => {
return {
// id: "0",
// // 领取数量
// already_send_count: 0,
// // 总数
// process_data: {
// type: 1, // 0 无限制
// value: 20,
// msg: "已领20%",
// },
// // 日期有效日期 // 2023.08.30-2023.09.1
// date: "",
// expire_tips: "",
// time_start: "",
// time_end: "",
};
},
},
// 监听
propRandom: {
type: Number,
default: 0,
},
// 半圆背景
propBg: {
type: String,
default: '#fff',
},
// 进度条
propIsProgress: {
type: Boolean,
default: false,
},
// 是否可重复领取
propRepeatedClaim: {
type: Boolean,
default: false,
},
// 是否可点击
propDisabled: {
type: Boolean,
default: false,
},
// 下标
propIndex: {
type: [Number,String],
default: 0,
},
// 按钮状态 0-领取1-已领取2-已抢完3-去使用,4-已使用5-已过期
propStatusType: {
type: Number,
default: 0,
},
// 按钮名称: 领取 已领取 已抢完 去使用
propStatusOperableName: {
type: String,
default: '',
},
// 优惠券有效期
propStartTime:{
type: String,
default: '',
},
propEndTime:{
type: String,
default: '',
}
},
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
coupon_static_url: coupon_static_url + 'app/',
// 符号
currency_symbol: app.globalData.currency_symbol(),
// 首页地址
home_page_url: tabbar_pages[0],
};
},
methods: {
// 领取
receive(e) {
this.$emit('call-back', this.propIndex, this.propData.id);
},
// url事件
url_event(e) {
app.globalData.url_event(e);
}
},
};
</script>
<style scoped>
.coupon-card {
border-radius: 24rpx;
height: 208rpx;
}
.card-left {
width: 176rpx;
padding: 24rpx 12rpx;
background: linear-gradient(95deg, #ff994b 0%, #ff6e00 100%);
}
.card-left.failure {
background: linear-gradient(95deg, #eeeeee 0%, #fafafa 100%);
}
.card-right {
padding: 32rpx 24rpx 32rpx 46rpx;
background-color: #ffe4d1;
}
.card-right.failure {
background: linear-gradient(95deg, #f8f8f8 0%, #e0dede 100%);
}
.card-info.failure {
padding-right: 116rpx;
}
.card-btn {
width: 116rpx;
text-align: center;
padding: 6rpx 0;
background: linear-gradient(93deg, #ff9747 0%, #ff6e01 100%);
border-radius: 13px;
}
.robbed {
background: #fbd3b7;
}
.received {
border-radius: 13px;
background: transparent;
}
::v-deep .uni-progress-bar,
::v-deep .uni-progress-inner-bar {
border-radius: 6rpx;
}
.card-circle-top,
.card-circle-bottom {
width: 40rpx;
height: 40rpx;
background-color: #fff;
border-radius: 50%;
position: absolute;
left: 180rpx;
z-index: 1;
}
.card-circle-top {
top: -20rpx;
}
.card-circle-bottom {
bottom: -20rpx;
}
.card-image .image {
width: 136rpx;
height: 108rpx;
}
</style>

View File

@@ -0,0 +1,430 @@
<template>
<!-- 文章列表 -->
<view class="oh" :style="style_container">
<view class="oh" :style="style_img_container">
<view class="pr oh" :style="style">
<view v-if="!['4'].includes(article_theme)" class="flex-wrap" :class="article_theme_class" :style="article_theme !== '3' ? article_spacing : ''">
<view v-for="(item, index) in data_list" :key="index" class="item oh" :style="article_style" :data-value="item.data.url" @tap="url_event">
<view :class="article_theme == '0' ? 'flex-row oh' : 'flex-col oh ht-auto'" :style="article_img_style">
<template v-if="article_theme !== '3'">
<view class="oh pr flex-row">
<template v-if="item.new_cover.length > 0">
<image :src="item.new_cover[0].url" class="img" :style="img_radius + img_size" mode="aspectFill" />
</template>
<template v-else>
<image :src="item.data.cover" class="img" :style="img_radius + img_size" mode="aspectFill" />
</template>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</template>
<view v-if="field_show.includes('0') || field_show.includes('1') || field_show.includes('2') || field_show.includes('3')" class="jc-sb flex-1" :class="article_theme == '3' ? 'flex-row align-c' : 'flex-col'" :style="article_theme !== '0' ? content_padding : ''">
<view class="flex-col" :class="article_theme == '3' ? 'flex-1 flex-width' : ''" :style="'gap:' + name_desc_space + 'px;'">
<view v-if="field_show.includes('3')" class="title" :class="article_theme == '3' ? 'text-line-1' : 'text-line-2'" :style="article_name">{{ item.new_title ? item.new_title : item.data.title }}</view>
<view v-if="field_show.includes('2')" :class="'desc ' + field_desc_row == '2' ? 'text-line-2' : 'text-line-1'" :style="article_desc">{{ item.data.describe || '' }}</view>
</view>
<view class="flex-row jc-sb gap-8" :class="article_theme == '3' ? 'margin-left' : 'align-e margin-top'">
<view :style="article_date">{{ field_show.includes('0') ? item.data.add_time : '' }}</view>
<view v-show="field_show.includes('1')" class="flex-row align-c gap-3" :style="article_page_view">
<iconfont name="icon-eye" propContainerDisplay="flex"></iconfont>
<view>
{{ item.data.access_count ? item.data.access_count : '' }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view v-else class="oh" :class="article_theme_class">
<swiper class="swiper" circular :autoplay="is_roll ? true : false" :interval="interval_time" :next-margin="next_margin" :display-multiple-items="slides_per_group" :style="'height:' + carousel_height_computer">
<swiper-item v-for="(item1, index1) in article_carousel_list" :key="index1">
<view class="flex-row ht-auto" :style="article_spacing">
<view v-for="(item, index) in item1.carousel_list" :key="index" class="item oh ht-auto" :style="article_style" :data-value="item.data.url" @tap="url_event">
<view class="oh flex-col ht-auto" :style="article_img_style">
<view class="oh pr wh-auto ht-auto flex-row">
<template v-if="item.new_cover.length > 0">
<image :src="item.new_cover[0].url" class="img" :style="img_radius + 'height:100%;'" mode="aspectFill" />
</template>
<template v-else>
<image :src="item.data.cover" class="img" :style="img_radius + 'height:100%;'" mode="aspectFill" />
</template>
<template v-if="field_show.includes('3') && name_float == '1'">
<view class="text-line-1" :style="article_name + float_name_style">{{ item.new_title ? item.new_title : item.data.title }}</view>
</template>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
<view v-if="field_show.includes('0') || field_show.includes('1') || field_show.includes('2') || (field_show.includes('3') && name_float == '0')" class="jc-sb flex-1 flex-col" :style="article_theme !== '0' ? content_padding : ''">
<view class="flex-col" :style="'gap:' + name_desc_space + 'px;'">
<view v-if="field_show.includes('3') && name_float == '0'" class="title text-line-2" :style="article_name + article_name_height_computer">{{ item.new_title ? item.new_title : item.data.title }}</view>
<view v-if="field_show.includes('2')" :class="'desc ' + field_desc_row == '2' ? 'text-line-2' : 'text-line-1'" :style="article_desc">{{ item.data.describe || '' }}</view>
</view>
<view :class="'flex-row jc-sb gap-8 align-e' + ((field_show.includes('3') && name_float == '0') || field_show.includes('2') ? ' margin-top' : '')">
<view :style="article_date">{{ field_show.includes('0') ? item.data.add_time : '' }}</view>
<view v-show="field_show.includes('1')" class="flex-row align-c gap-3" :style="article_page_view">
<iconfont name="icon-eye" propContainerDisplay="flex"></iconfont>
<view>
{{ item.data.access_count ? item.data.access_count : '' }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { isEmpty, common_styles_computer, common_img_computer, padding_computer, radius_computer, get_math, gradient_handle, background_computer, gradient_computer, margin_computer, box_shadow_computer, border_computer, old_margin } from '@/common/js/common/common.js';
import subscriptIndex from '@/components/diy/modules/subscript/index.vue';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
subscriptIndex,
},
props: {
propValue: {
type: Object,
default: () => {},
},
// 是否使用公共样式
propIsCommonStyle: {
type: Boolean,
default: true,
},
// 关键key
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
// 数据
data_list: [],
// 风格
article_theme: '0',
// 是否显示
field_show: ['0', '1'],
// 文章
article_name: '',
// 描述
article_desc: '',
// 日期
article_date: '',
// 浏览量
article_page_view: '',
// 内容圆角
content_radius: '',
// 图片圆角
img_radius: '',
// 内间距
content_padding: '',
// 内容间距
content_spacing: '',
// 文章间距
article_spacing: '',
// article_item_height: '',
article_style: '',
article_img_style: '',
// 轮播图定时轮播
interval_time: 2000,
// 轮播图是否滚动
is_roll: 1,
article_theme_class: '',
// 轮播高度
carousel_height_computer: '',
// 文章内容高度
article_name_height_computer: '',
// 文章名称浮动样式
float_name_style: '',
name_float: '0',
// 图片大小
img_size: '',
// 文章轮播数据
article_carousel_list: [],
// 文章描述间距
name_desc_space: 0,
// 一行显示的数量
slides_per_group: 1,
next_margin: '0rpx',
field_desc_row: '1',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
// 描述样式
const desc_size = new_style.desc_size;
let desc_style = 'font-size:' + desc_size + 'px;line-height:' + desc_size + 'px;height:' + desc_size + 'px;color:' + new_style.desc_color + ';';
if (new_content.field_desc_row == '2') {
desc_style = 'font-size:' + desc_size + 'px;line-height:' + (desc_size > 0 ? desc_size + 3 : 0 ) + 'px;height:'+ (desc_size > 0 ? (desc_size + 3) * 2 : 0) + 'px;color:' + new_style.desc_color + ';';
}
this.setData({
field_desc_row: new_content.field_desc_row,
name_float: !isEmpty(new_content.name_float) ? new_content.name_float : '0',
// 判断是自动还是手动
data_list:
new_content.data_type == '0'
? new_content.data_list
: new_content.data_auto_list && new_content.data_auto_list.length > 0
? new_content.data_auto_list.map((item) => ({
id: get_math(),
new_title: '',
new_cover: [],
data: item,
}))
: [],
article_theme_class: this.article_theme_class_computer(new_content.theme),
article_theme: new_content.theme,
field_show: new_content.field_show,
// 样式
article_name: 'font-size:' + new_style.name_size + 'px;' + 'font-weight:' + new_style.name_weight + ';' + 'color:' + new_style.name_color + ';',
article_desc: desc_style,
article_date: 'font-size:' + new_style.time_size + 'px;' + 'font-weight:' + new_style.time_weight + ';' + 'color:' + new_style.time_color + ';',
article_page_view: 'font-size:' + new_style.page_view_size + 'px;' + 'font-weight:' + new_style.page_view_weight + ';' + 'color:' + new_style.page_view_color + ';',
content_radius: radius_computer(new_style.content_radius),
img_radius: radius_computer(new_style.img_radius),
// 内间距
content_padding: padding_computer(new_style.padding),
// 内容间距
content_spacing: `gap: ${new_style.content_spacing}px;`,
// 文章间距
article_spacing: `gap: ${new_style.article_spacing}px;`,
// 描述间距
name_desc_space: parseInt(new_style.name_desc_space),
next_margin: new_style.rolling_fashion == 'translation' ? '-' + new_style.article_spacing_margin + 'px' : '0rpx',
// 文章内容高度
slides_per_group: new_style.rolling_fashion == 'translation' ? Number(new_content.carousel_col) + 1 : 1,
});
// 默认数据
const product_style_list = [
{ name: '单列展示', value: '0', width:110, height: 83 },
{ name: '两列展示(纵向)', value: '1', width:180, height: 180 },
{ name: '大图展示', value: '2', width:0, height: 180 },
{ name: '无图模式', value: '3', width:0, height: 0 },
{ name: '左右滑动展示', value: '4', width:0, height: 0 },
];
const scale = sys_width / 390;
let img_style = ``;
if (['0'].includes(new_content.theme)) {
// 图片宽度
if (typeof new_style.content_img_width == 'number') {
img_style += `width: ${ new_style.content_img_width * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_content.theme);
if (list.length > 0) {
img_style += `width: ${ list[0].width * scale }px;`;
} else {
img_style += 'width: auto;';
}
}
}
if (!['3', '4'].includes(new_content.theme)) {
// 图片宽度
if (typeof new_style.content_img_height == 'number') {
img_style += `height: ${ new_style.content_img_height * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_content.theme);
if (list.length > 0) {
img_style += `height: ${ list[0].height * scale }px;`;
} else {
img_style += 'height: auto;';
}
}
}
// 背景图的处理
const article_data = {
background_img_style: new_style?.article_background_img_style || '',
background_img: new_style?.article_background_img || '',
}
const article_margin = new_style.value?.margin || old_margin;
const margin_width = article_margin.margin_left + article_margin.margin_right;
// 渐变效果
const all_style = gradient_handle(new_style?.article_color_list || [], new_style?.article_direction || '') + margin_computer(article_margin) + box_shadow_computer(new_style) + border_computer(new_style);
// 文章样式
if (this.article_theme == '0') {
this.setData({
img_size: img_style,
article_style: this.content_radius + all_style,
article_img_style: this.content_spacing + this.content_padding + background_computer(article_data)
});
} else if (this.article_theme == '1') {
this.setData({
img_size: img_style,
article_style: `width: calc(50% - ${new_style.article_spacing + (margin_width * 2) / 2}px);` + this.content_radius + all_style,
article_img_style: background_computer(article_data)
});
} else if (this.article_theme == '2') {
this.setData({
img_size: img_style,
article_style: this.content_radius + all_style,
article_img_style: background_computer(article_data)
});
} else if (this.article_theme == '3') {
this.setData({
style: `padding: 0 ${new_style.content_spacing}px;background:#fff;` + this.content_radius,
});
} else if (this.article_theme == '4') {
// 更新轮播图的key确保更换时能重新更新轮播图
const temp_carousel_col = new_content.carousel_col || '1';
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = temp_carousel_col !== '0' ? ((new_style.article_spacing * temp_carousel_col - 1) + (margin_width * temp_carousel_col)) / temp_carousel_col : '0';
const multicolumn_columns_width = new_style.rolling_fashion == 'translation' ? `margin-right: ${ new_style.article_spacing + article_margin.margin_right }px;width:100%;` : `width:calc(${100 / (Number(temp_carousel_col) + 1)}% - ${gap * 2}rpx);min-width:calc(${100 / (Number(temp_carousel_col) + 1)}% - ${gap * 2}rpx);`;
const { name_bg_color_list = [], name_bg_direction = '180deg', name_bg_radius, name_bg_padding, name_bg_margin } = new_style;
const data = {
color_list: name_bg_color_list,
direction: name_bg_direction,
}
let location = 'position:absolute;bottom:0;left:0;right:0;'
// 轮播宽度
this.setData({
// 滚动时间
interval_time: (new_style.interval_time || 2) * 1000,
// 是否滚动修改
is_roll: new_style.is_roll,
// article_item_height: `height: ${new_style.article_height }px`,
article_style: this.content_radius + all_style + multicolumn_columns_width,
// 轮播高度
carousel_height_computer: new_style.article_height * scale + 'px',
// 文章内容高度
article_name_height_computer: `height:${new_style.name_size * 2.4 * 2}rpx;line-height:${new_style.name_size * 1.2 * 2}rpx;`,
float_name_style: gradient_computer(data) + (!isEmpty(name_bg_radius) ? radius_computer(name_bg_radius) : '') + (!isEmpty(name_bg_padding) ? padding_computer(name_bg_padding) : '' ) + (!isEmpty(name_bg_padding) ? margin_computer(name_bg_margin) : '') + location,
article_img_style: background_computer(article_data)
});
// 文章轮播数据
const cloneList = JSON.parse(JSON.stringify(this.data_list));
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (new_style.rolling_fashion != 'translation') {
if (cloneList.length > 0) {
// 每页显示的数量
const num = Number(temp_carousel_col) + 1;
// 存储数据显示
let nav_list = [];
// 拆分的数量
const split_num = Math.ceil(cloneList.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({ carousel_list: cloneList.slice(i * num, (i + 1) * num) });
}
this.setData({
article_carousel_list: nav_list,
});
} else {
// 否则的话,就返回全部的信息
this.setData({
article_carousel_list: [{ carousel_list: cloneList }],
});
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
carousel_list: [item],
});
});
this.setData({
article_carousel_list: nav_list,
});
}
}
if (this.propIsCommonStyle) {
this.setData({
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
}
},
// 文章主题样式
article_theme_class_computer(theme) {
switch (theme) {
case '0':
return 'style1 flex-col';
case '1':
return 'style2 flex-row flex-wrap';
case '2':
return 'style3 flex-col';
case '3':
return 'style4 flex-col';
default:
return 'style5';
}
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style lang="scss" scoped>
.style1 {
.item {
max-width: 100%;
}
}
.style2 {
.item {
.img {
width: 100%;
}
}
}
.style3 {
.item {
width: 100%;
.img {
width: 100%;
}
}
}
.style4 {
.item {
width: 100%;
&:not(:last-child) {
border-bottom: 2rpx solid #eee;
}
}
}
.style5 {
.item {
width: 100%;
.img {
width: 100%;
}
}
}
</style>

View File

@@ -0,0 +1,264 @@
<template>
<!-- 文章列表 -->
<view class="article-tabs ou" :class="'article-tabs-' + propKey" :style="style_container">
<view class="ou" :style="style_img_container">
<componentDiyModulesTabsView :propKey="propKey" :propValue="article_tabs" :propIsTop="top_up == '1'" :propTop="sticky_top" :propStyle="tabs_style" :propsTabsContainer="tabs_container" :propsTabsImgContainer="tabs_img_container" :propCustomNavHeight="propIsTabsUseSafeDistance ? (propCustomNavHeight * 2 + 'rpx') : '0rpx'" :propTabsBackground="tabs_background" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" @onTabsTap="tabs_click_event"></componentDiyModulesTabsView>
<view :style="article_margin_top">
<view :style="article_container">
<view :style="article_img_container">
<componentDiyArticleList :propKey="diy_key" :propValue="article_tabs" :propIsCommonStyle="false"></componentDiyArticleList>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, margin_computer, background_computer, gradient_computer, radius_computer, isEmpty, box_shadow_computer, border_computer, old_border_and_box_shadow, old_margin, old_padding } from '@/common/js/common/common.js';
import componentDiyModulesTabsView from '@/components/diy/modules/tabs-view';
import componentDiyArticleList from '@/components/diy/article-list'; // 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
props: {
propValue: {
type: Object,
default: () => {},
},
// 距离顶部高度
propTop: {
type: Number,
default: 0,
},
// 自定义导航栏高度
propCustomNavHeight: {
type: Number,
default: 33,
},
// 滚动距离
propScrollTop: {
type: Number,
default: 0,
},
// 顶部导航是否开启沉浸模式
propIsImmersionModel: {
type: Boolean,
default: false,
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
// 选项卡是否使用安全距离
propIsTabsUseSafeDistance: {
type: Boolean,
default: true
}
},
components: {
componentDiyModulesTabsView,
componentDiyArticleList,
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
article_tabs: {},
// 是否滑动置顶
top_up: '0',
tabs_style: '',
tabs_top: 0,
tabs_background: 'background:transparent',
custom_nav_height: 33,
diy_key: '',
// 选项卡背景设置
article_margin_top: '',
tabs_container: '',
tabs_img_container: '',
tabs_sliding_fixed_bg: '',
// 商品区域背景设置
article_container: '',
article_img_container: '',
// #ifdef MP
nav_safe_space: bar_height + 5,
// #endif
// #ifdef H5 || MP-TOUTIAO
nav_safe_space: bar_height + 7,
// #endif
// #ifdef APP
nav_safe_space: bar_height + 0,
// #endif
// 选项卡默认数据
tabs_index: 0,
sticky_top: 0,
};
},
watch: {
// 监听滚动距离
propScrollTop(newVal) {
if (newVal + this.sticky_top + this.custom_nav_height > this.tabs_top + this.nav_safe_space && this.top_up == '1') {
let new_style = this.propValue.style || {};
let tabs_bg = new_style.common_style.color_list;
let new_tabs_background = '';
if (tabs_bg.length > 0 && (tabs_bg[0].color || null) != null) {
new_tabs_background = gradient_computer(new_style.common_style);
}
let new_tabs_background_img = background_computer(new_style.common_style);
if (new_tabs_background_img.length > 0) {
new_tabs_background_img += 'background-position: top left;';
}
this.tabs_background = (new_tabs_background.length > 0 ? new_tabs_background : 'background:#fff;') + new_tabs_background_img;
} else {
this.tabs_background = 'background:transparent';
}
},
propTop(val) {
this.init();
},
propKey(val) {
this.setData({
diy_key: val,
});
// 初始化
this.init();
},
},
created() {
this.init();
},
mounted() {
this.$nextTick(() => {
const self = this;
setTimeout(() => {
self.getTop();
});
// #ifdef H5 || MP-TOUTIAO
// 获取自定义导航栏高度
this.setData({
custom_nav_height: this.propCustomNavHeight,
});
// #endif
});
},
methods: {
// 初始化数据
init() {
let new_content = this.propValue.content || {};
let new_style = this.propValue.style || {};
let new_data = JSON.parse(JSON.stringify(this.propValue));
const new_tabs_data = new_data.content.tabs_list[this.tabs_index] || {};
new_data.content.theme = new_data.content.article_theme;
new_data.content.data_type = new_tabs_data.data_type;
new_data.content.category = new_tabs_data.category;
new_data.content.carousel_col = new_data.content.article_carousel_col;
new_data.content.data_list = new_tabs_data.data_list;
new_data.content.data_auto_list = new_tabs_data.data_auto_list;
new_data.content.data_ids = new_tabs_data.data_ids;
new_data.content.number = new_tabs_data.number;
new_data.content.sort = new_tabs_data.sort;
new_data.content.sort_rules = new_tabs_data.sort_rules;
new_data.content.field_show = new_data.content.field_show;
new_data.content.is_cover = new_tabs_data.is_cover;
// 公共样式
const common_style = new_style.common_style;
let tabs_style_obj = {
padding_top: common_style.padding_top - this.propCustomNavHeight < 0 ? 0 : common_style.padding_top - this.propCustomNavHeight,
padding_left: common_style.padding_left,
padding_right: common_style.padding_right,
};
let new_tabs_style = padding_computer(tabs_style_obj) + margin_computer(tabs_style_obj) + `position:relative;left: -${tabs_style_obj.padding_left * 2}rpx;right: -${tabs_style_obj.padding_right * 2}rpx;width:100%;`;
// 如果是历史数据的话,就执行默认添加下边距
if (isEmpty(new_style.tabs_padding)) {
new_tabs_style += 'padding-bottom: 20rpx;';
}
const { tabs_bg_color_list = [], tabs_bg_direction = '', tabs_bg_background_img_style = '', tabs_bg_background_img = [], tabs_radius = old_radius, tabs_padding = old_padding, article_content_color_list = [], article_content_direction = '', article_content_background_img_style = '', article_content_background_img = [], article_content_margin = old_margin, article_content_padding = old_padding, article_content_radius = old_radius } = new_style;
// 选项卡背景设置
const tabs_data = {
color_list: tabs_bg_color_list,
direction: tabs_bg_direction,
background_img_style: tabs_bg_background_img_style,
background_img: tabs_bg_background_img,
};
// 文章区域背景设置
const article_content_data = {
color_list: article_content_color_list,
direction: article_content_direction,
background_img_style: article_content_background_img_style,
background_img: article_content_background_img,
};
const article_content = new_style?.article_content || old_border_and_box_shadow;
const tabs_content = new_style?.tabs_content || old_border_and_box_shadow;
this.setData({
top_up: new_content.tabs_top_up,
sticky_top: this.propTop + (new_style?.tabs_margin?.margin_top || 0),
article_tabs: new_data,
style_container: common_styles_computer(common_style),
style_img_container: common_img_computer(common_style, this.propIndex),
tabs_style: new_tabs_style,
article_margin_top: 'margin-top:' + (new_style?.article_content_spacing || 0) * 2 + 'rpx',
tabs_sliding_fixed_bg: gradient_computer(tabs_data),
tabs_container: gradient_computer(tabs_data) + radius_computer(tabs_radius) + margin_computer(new_style?.tabs_margin || old_margin) + border_computer(tabs_content) + box_shadow_computer(tabs_content) + 'overflow: hidden;',
tabs_img_container: background_computer(tabs_data) + padding_computer(tabs_padding) + 'box-sizing: border-box;overflow: hidden;',
article_container: gradient_computer(article_content_data) + margin_computer(article_content_margin) + radius_computer(article_content_radius) + box_shadow_computer(article_content) + border_computer(article_content) + 'overflow: hidden;',
article_img_container: background_computer(article_content_data) + padding_computer(article_content_padding) + 'box-sizing: border-box;overflow: hidden;',
});
},
// tabs切换事件
tabs_click_event(index) {
let new_data = JSON.parse(JSON.stringify(this.propValue));
new_data.content.theme = new_data.content.article_theme;
new_data.content.data_type = new_data.content.tabs_list[index].data_type;
new_data.content.category = new_data.content.tabs_list[index].category;
new_data.content.carousel_col = new_data.content.article_carousel_col;
new_data.content.data_list = new_data.content.tabs_list[index].data_list;
new_data.content.data_auto_list = new_data.content.tabs_list[index].data_auto_list;
new_data.content.data_ids = new_data.content.tabs_list[index].data_ids;
new_data.content.number = new_data.content.tabs_list[index].number;
new_data.content.sort = new_data.content.tabs_list[index].sort;
new_data.content.sort_rules = new_data.content.tabs_list[index].sort_rules;
new_data.content.field_show = new_data.content.field_show;
new_data.content.is_cover = new_data.content.tabs_list[index].is_cover;
this.setData({
tabs_index: index,
article_tabs: new_data,
diy_key: Math.random(),
});
},
// 获取商品距离顶部的距离
getTop() {
const query = uni.createSelectorQuery().in(this);
query
.select('.article-tabs-' + this.propKey)
.boundingClientRect((res) => {
if ((res || null) != null) {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_style = new_data.style || {};
this.setData({
tabs_top: res.top - (new_style.common_style?.margin_top || 0) + (new_style?.tabs_margin?.margin_top || 0),
});
}
})
.exec();
},
},
};
</script>
<style lang="scss" scoped>
.tabs-container {
position: relative;
width: 100%;
left: 0;
right: 0;
}
</style>

View File

@@ -0,0 +1,64 @@
<template>
<view :style="style_container">
<view :style="style_img_container">
<view :style="style"></view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
init() {
const { height } = this.propValue.content;
const { line_color, common_style } = this.propValue.style;
this.setData({
style: `height: ${height * 2}rpx;background: ${line_color || 'transparent'};`,
style_container: common_styles_computer(common_style),
style_img_container: common_img_computer(common_style, this.propIndex),
});
},
},
};
</script>
<style scoped lang="scss">
.right-0 {
top: 50%;
transform: translateY(-50%);
}
</style>

View File

@@ -0,0 +1,64 @@
<template>
<!-- 横线 -->
<view :style="style_container">
<view :style="style_img_container">
<view :style="style"></view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
// key
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
// 边框设置
let border_content = `border-bottom-style: ${new_content?.styles || 'solid'};`;
// 边框颜色设置
let border_style = `border-bottom-width: ${new_style.line_width * 2 || 2}rpx; border-bottom-color: ${new_style.line_color || 'rgba(204, 204, 204, 1)'};`;
this.setData({
style: border_content + border_style,
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
},
};
</script>
<style></style>

371
components/diy/carousel.vue Normal file
View File

@@ -0,0 +1,371 @@
<template>
<view class="pr" :style="style_container + swiper_bg_style">
<view class="pa top-0 wh-auto ht-auto" :style="swiper_bg_img_style"></view>
<view class="pr" :style="style_img_container + (!isEmpty(swiper_bg_img_style) ? swiper_bg_img_style_null : '')">
<swiper circular="true" :autoplay="form.is_roll == '1'" :interval="form.interval_time * 1000" :display-multiple-items="slides_per_group" :duration="500" :style="{ height: swiper_height }" :previous-margin="previousMargin" :next-margin="nextMargin" @change="slideChange">
<block v-if="form.carousel_type == 'card'">
<swiper-item v-for="(item, index) in new_list" :key="index">
<view class="flex-row align-c wt-auto ht-auto" :data-value="item.carousel_link.page" @tap="url_open">
<view class="swiper-item" :style="img_style" :class="['scale-defalt', { 'scale-1': animationData === index }]">
<view class="wh-auto ht-auto">
<imageEmpty :propImageSrc="item.carousel_img[0]" :propStyle="img_style" :propImgFit="img_fit" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
</view>
<view v-if="new_style.video_is_show == '1' && item.carousel_video.length > 0" :class="{ 'x-middle': new_style.video_location == 'center', 'right-0': new_style.video_location == 'flex-end' }" class="video-class pa oh" :style="{'bottom': new_style.video_bottom * 2 + 'rpx'}">
<view class="flex-row gap-5 align-c" :style="video_style" :data-value="item.carousel_video" @tap.stop="video_play">
<block v-if="new_style.video_type == 'img'">
<view class="video_img">
<imageEmpty :propImageSrc="new_style.video_img[0]" propImgFit="aspectFill" propErrorStyle="width: 28rpx;height: 28rpx;"></imageEmpty>
</view>
</block>
<block v-else>
<iconfont :name="!isEmpty(new_style.video_icon_class) ? 'icon-' + new_style.video_icon_class : 'icon-bofang'" size="'28rpx'" :color="new_style.video_icon_color" propContainerDisplay="flex"></iconfont>
</block>
<text v-if="!isEmpty(item.video_title)" :style="{ color: new_style.video_title_color, 'font-size': new_style.video_title_size * 2 + 'rpx', 'text-wrap': 'nowrap' }">{{ item.video_title }}</text>
</view>
</view>
</view>
</swiper-item>
</block>
<block v-else>
<swiper-item v-for="(item, index) in new_list" :key="index">
<view class="ht-auto" :style="['oneDragOne', 'twoDragOne'].includes(form.carousel_type) ? 'padding-right:' + new_style.image_spacing * 2 + 'rpx;' : ''" :data-value="item.carousel_link.page" @tap="url_open">
<view class="wh-auto ht-auto pr" :style="img_style">
<imageEmpty :propImageSrc="item.carousel_img[0]" :propStyle="img_style" :propImgFit="img_fit" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<view v-if="new_style.video_is_show == '1' && item.carousel_video.length > 0" :class="{ 'x-middle': new_style.video_location == 'center', 'right-0': new_style.video_location == 'flex-end' }" class="video-class pa oh" :style="{'bottom': new_style.video_bottom * 2 + 'rpx'}">
<view class="flex-row gap-5 align-c" :style="video_style" :data-value="item.carousel_video" @tap.stop="video_play">
<block v-if="new_style.video_type == 'img'">
<view class="video_img">
<imageEmpty :propImageSrc="new_style.video_img[0]" propImgFit="aspectFill" propErrorStyle="width: 28rpx;height: 28rpx;"></imageEmpty>
</view>
</block>
<block v-else>
<iconfont :name="!isEmpty(new_style.video_icon_class) ? 'icon-' + new_style.video_icon_class : 'icon-bofang'" size="'28rpx'" :color="new_style.video_icon_color" propContainerDisplay="flex"></iconfont>
</block>
<text v-if="!isEmpty(item.video_title)" :style="{ color: new_style.video_title_color, 'font-size': new_style.video_title_size * 2 + 'rpx', 'text-wrap': 'nowrap' }">{{ item.video_title }}</text>
</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
<view v-if="new_style.is_show == '1'" :class="['left', 'right'].includes(new_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="indicator_location_style">
<template v-if="new_style.indicator_style == 'num'">
<view :style="indicator_style" class="dot-item">
<text :style="{ color: new_style.actived_color }">{{ actived_index + 1 }}</text>
<text>/{{ form.carousel_list.length }}</text>
</view>
</template>
<template v-else>
<view v-for="(item, index2) in form.carousel_list" :key="index2" :style="indicator_style + (actived_index == index2 ? 'background:' + new_style.actived_color : '')" class="dot-item" />
</template>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, radius_computer, isEmpty, gradient_computer, padding_computer, get_indicator_location_style, get_indicator_style, background_computer } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
imageEmpty,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propIsCommon: {
type: Boolean,
default: true,
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propOuterContainerPadding: {
type: Number,
default: 0,
}
},
data() {
return {
form: {},
new_style: {},
// 通用样式显示
style_container: '',
style_img_container: '',
// 图片的设置
img_style: '',
// 指示器的样式
indicator_style: '',
seat_list: [],
new_list: [],
// 指示器选中的位置
actived_index: 0,
interval_types: '',
img_fit: '',
dot_style: '',
video_style: '',
popup_width: '0rpx',
popup_height: '0rpx',
// 样式二的处理
animation: '',
animationData: 0,
previousMargin: '0rpx',
nextMargin: '0rpx',
slides_per_group: 1,
// hackReset: true,
// 轮播图的高度
swiper_height: 50,
// 轮播时的背景样式
swiper_bg_style: '',
swiper_bg_img_style: '',
swiper_bg_img_style_null: `background-image: url('')`
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
// 获取当前手机的宽度
const { windowWidth } = uni.getSystemInfoSync();
// 将80%的宽度分成16份
const block = (windowWidth * 0.8) / 16;
const { common_style, actived_color } = new_style;
// scaleToFill 对应 fill aspectFit 对应 contain aspectFill 对应 cover
let fit = '';
if (new_form.img_fit == 'contain') {
fit = 'aspectFit';
} else if (new_form.img_fit == 'fill') {
fit = 'scaleToFill';
} else if (new_form.img_fit == 'cover') {
fit = 'aspectFill';
}
const { margin_left, margin_right, padding_left, padding_right } = new_style.common_style;
const width = sys_width - margin_left - margin_right - padding_left - padding_right - this.propOuterContainerPadding;
const scale = width / 390;
this.setData({
form: new_form,
new_style: new_style,
seat_list: this.get_seat_list(new_form),
new_list: new_form.carousel_list.concat(this.get_seat_list(new_form)),
popup_width: block * 16 * 2 + 'rpx', // 视频的宽度依照16:9比例来算
popup_height: block * 9 * 2 + 'rpx', // 视频的高度
style_container: this.propIsCommon ? common_styles_computer(common_style) : '', // 公共样式显示
style_img_container: this.propIsCommon ? common_img_computer(common_style, this.propIndex) : '', // 公共样式显示
img_style: radius_computer(new_style), // 图片的设置
indicator_style: get_indicator_style(new_style), // 指示器的样式
indicator_location_style: get_indicator_location_style(new_style),
dot_style: `bottom: ${ new_style.indicator_bottom * scale }px;`, // 指示器位置
img_fit: fit, // 图片风格 默认为aspectFill
video_style: this.get_video_style(new_style), // 视频播放按钮显示逻辑
swiper_height: new_form.height * scale + 'px', // 轮播图高度
swiper_bg_style: this.get_swiper_bg_style(new_form, 0),
swiper_bg_img_style: this.get_swiper_bg_img_style(new_form, 0),
});
// 风格二显示逻辑
if (new_form.carousel_type == 'card') {
// this.$nextTick(() => {
this.setData({
previousMargin: '41px',
nextMargin: '41px',
animationData: 0,
});
// });
} else if (new_form.carousel_type != 'inherit') {
// 风格三,四显示逻辑
// this.$nextTick(() => {
this.setData({
nextMargin: '50px',
slides_per_group: new_form.carousel_type == 'twoDragOne' ? 2 : 1,
});
// });
}
},
get_swiper_bg_style(form, actived_index) {
if (!this.propIsCommon) {
return '';
}
const style = form?.carousel_list?.[actived_index]?.style;
if (style && !isEmpty(style.color_list)) {
const color_list = style.color_list;
const list = color_list.filter((item) => !isEmpty(item.color));
if (list.length > 0) {
try {
return gradient_computer(style);
} catch (error) {
return '';
}
}
return '';
}
return '';
},
get_swiper_bg_img_style(form, actived_index) {
if (!this.propIsCommon) {
return '';
}
const { carousel_img, style = {} } = form?.carousel_list[actived_index] || {};
// 如果是自定义的图片 判断图片是否存在
if (!isEmpty(carousel_img) && style?.background_type == 'carousel') {
// 如果是使用轮播图,判断轮播图是否存在
const data = {
background_img: carousel_img,
background_img_style: style?.background_img_style || '2',
}
return background_computer(data) + (style.is_background_img_blur == '1' ? `filter: blur(14px);opacity: 0.6;` : '');
} else if (!isEmpty(style?.background_img)) {
return background_computer(style) + (style.is_background_img_blur == '1' ? `filter: blur(14px);opacity: 0.6;` : '');
}
return '';
},
get_seat_list(form) {
if (form.carousel_list.length > 3) {
return [];
} else {
let seat_list = [];
const list = JSON.parse(JSON.stringify(form.carousel_list));
switch (list.length) {
case 1:
seat_list = [...list, ...list, ...list];
break;
case 2:
seat_list.push(...list);
break;
case 3:
seat_list.push(...list);
break;
default:
break;
}
return seat_list;
}
},
slideChange(e) {
let actived_index = e.detail.current;
if (e.detail.current > this.form.carousel_list.length - 1) {
const seat_length = this.seat_list.length;
if (this.form.carousel_list.length > 1) {
actived_index = actived_index - seat_length;
} else {
actived_index = 0;
}
}
if (!this.propIsCommon) {
this.$emit('slideChange', actived_index);
}
this.setData({
animationData: e.detail.current,
actived_index: actived_index,
swiper_bg_style: this.get_swiper_bg_style(this.form, actived_index),
swiper_bg_img_style: this.get_swiper_bg_img_style(this.form, actived_index),
});
},
get_video_style(new_style) {
const { video_radius, video_color_list, video_direction, video_title_color, video_padding } = new_style;
let style = ``;
if (!isEmpty(video_radius)) {
style += radius_computer(video_radius);
}
const data = {
color_list: video_color_list,
direction: video_direction,
};
style += gradient_computer(data) + padding_computer(video_padding) + `color: ${video_title_color};box-sizing: border-box;`;
return style;
},
video_play(e) {
const list = e.currentTarget.dataset.value;
if (!isEmpty(list)) {
this.$emit('onVideoPlay', list[0].url, this.popup_width, this.popup_height);
}
},
url_open(link) {
app.globalData.url_event(link);
},
},
};
</script>
<style scoped lang="scss">
.dot-center {
left: 50%;
transform: translateX(-50%);
}
.dot-right {
right: 0;
}
.dot {
z-index: 1;
padding-left: 20rpx;
padding-right: 20rpx;
.dot-item {
margin: 0 6rpx;
}
}
.swiper-container {
display: flex;
align-items: center;
}
.swiper-item {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
margin-left: auto;
margin-right: auto;
height: 90%;
width: 90%;
text-align: center;
}
.scale-defalt {
position: relative;
border-radius: 20rpx;
transform: scale(1);
transition: -webkit-transform 400ms linear, transform 400ms linear;
transform-origin: 50% 50% 0px;
&.scale-1 {
transform: scale(1.1);
}
}
.video_img {
max-width: 120rpx;
height: 28rpx;
}
.video-class {
max-width: 100%;
padding-right: 20rpx;
padding-left: 20rpx;
}
.x-middle {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
</style>

1013
components/diy/coupon.vue Normal file

File diff suppressed because it is too large Load Diff

337
components/diy/custom.vue Normal file
View File

@@ -0,0 +1,337 @@
<template>
<view :style="style_container">
<view :style="style_img_container">
<view :style="style_content_container">
<view class="w h pr" :style="style_content_img_container">
<template v-if="!isEmpty(form.data_source) && form.data_source_is_loop !== '0'">
<template v-if="data_source_content_list.length > 0 && form.data_source_direction == 'vertical'">
<view class="flex-row flex-wrap" :style="'row-gap:' + new_style.row_gap + 'px;column-gap:' + new_style.column_gap + 'px;'">
<view v-for="(item, index) in data_source_content_list" :key="index" class="ht-auto" :style="gap_width">
<view v-for="(item1, index1) in item.split_list" :key="index1">
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propFieldList="field_list" :propDataHeight="form.height" :propScale="scale" :propDataIndex="index" :propDataSplitIndex="index1" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</view>
</view>
</template>
<view v-else-if="data_source_content_list.length > 0 && ['vertical-scroll', 'horizontal'].includes(form.data_source_direction)" class="oh ht-auto">
<swiper class="w flex" circular="true" :vertical="form.data_source_direction != 'horizontal'" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_view" :style="{ width: '100%', height: swiper_height + 'px' }" @change="slideChange">
<swiper-item v-for="(item, index) in data_source_content_list" :key="index">
<view :class="form.data_source_direction != 'horizontal' ? 'ht-auto ' : 'flex-row'" :style="form.data_source_direction == 'horizontal' ? 'column-gap:' + new_style.column_gap + 'px;' : ''">
<view v-for="(item1, index1) in item.split_list" :key="index1" class="wh-auto ht-auto" :style="style_chunk_container + swiper_width + (form.data_source_direction == 'horizontal' ? gap_width : 'margin-bottom:' + content_outer_spacing_magin)">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propFieldList="field_list" :propScale="scale" :propDataIndex="index" :propDataSplitIndex="index1" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</swiper-item>
</swiper>
<view v-if="new_style.is_show == '1' && data_source_content_list.length > 1" :class="['left', 'right'].includes(new_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="indicator_location_style">
<block v-if="new_style.indicator_style == 'num'">
<view :style="indicator_style" class="dot-item">
<text :style="{ color: new_style.actived_color }">{{ actived_index + 1 }}</text>
<text>/{{ data_source_content_list.length }}</text>
</view>
</block>
<block v-else>
<view v-for="(item, index) in data_source_content_list" :key="index" :style="indicator_style + (actived_index == index ? 'background:' + new_style.actived_color : '')" class="dot-item" />
</block>
</view>
</view>
<template v-else>
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propConfigLoop="form.data_source_is_loop || '1'" :propFieldList="field_list" :propDataHeight="form.height" :propScale="scale" @url_event="url_event"></dataRendering>
</view>
</view>
</template>
</template>
<template v-else-if="!isEmpty(form.data_source) && form.data_source_is_loop == '0'">
<view class="h" :style="style_chunk_container">
<view class="w h oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propSourceList="{}" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propFieldList="field_list" :propDataHeight="form.height" :propScale="scale" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</template>
<template v-else>
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propConfigLoop="form.data_source_is_loop || '1'" :propFieldList="field_list" :propDataHeight="form.height" :propScale="scale" @url_event="url_event"></dataRendering>
</view>
</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer, percentage_count, isEmpty, get_indicator_style, get_indicator_location_style, border_width } from '@/common/js/common/common.js';
import dataRendering from '@/components/diy/modules/custom/data-rendering.vue';
const app = getApp();
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
dataRendering
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propIsCommonStyle: {
type: Boolean,
default: true,
},
propOuterContainerPadding: {
type: Number,
default: 0,
}
},
data() {
return {
form: {},
new_style: {},
scale: 1,
style_container: '',
style_img_container: '',
div_width: 0,
div_height: 0,
custom_list_length: 0,
source_list: {
// 存放手动输入的id
data_ids: [],
// 手动输入
data_list: [],
// 自动
data_auto_list: [],
},
data_source_content_list: [],
data_source: '',
// 内容样式
style_content_container: '',
style_content_img_container: '',
// 数据样式
style_chunk_container: '',
style_chunk_img_container: '',
// 指示器选中的下标
actived_index: 0,
// 轮播高度
swiper_height: 0,
swiper_width: 'width: 100%;',
// 指示器样式
indicator_location_style: '',
indicator_style: '',
slides_per_view: 1,
show_data: { data_key: 'id', data_name: 'name' },
old_data_style: {
color_list: [{ color: 'rgb(244, 252, 255)', color_percentage: undefined }],
direction: '180deg',
background_img_style: '2',
background_img: [],
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
},
content_outer_spacing_magin: '0rpx',
gap_width: '',
field_list: [],
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
percentage_count,
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
// 不包含新创建的数组时,将历史数据放到手动添加数组中
if (!Object.keys(new_form.data_source_content).includes('data_auto_list') && !Object.keys(new_form.data_source_content).includes('data_list')) {
//深拷贝一下,保留历史数据
const data = JSON.parse(JSON.stringify(new_form.data_source_content));
new_form.data_source_content = this.source_list;
// 如果老数组中有数据,将数据放到新数组中
if (!isEmpty(data)) {
new_form.data_source_content.data_list = [ data ];
}
}
// 数据来源的内容
let list = [];
if (new_form.is_custom_data == '1') {
if (Number(new_form.data_source_content.data_type) === 0) {
list = new_form.data_source_content?.data_list || [];
} else {
list = !isEmpty(new_form.data_source_content) ?
new_form.data_source_content.data_auto_list.map(item => ({
id: Math.random(),
new_cover: [],
new_title: '',
data: item,
})) : [];
}
} else {
list = new_form.data_source_content?.data_list || [];
}
// 数组处理
const new_list = list.length > 0 ? this.get_list(list, new_form, new_style) : [];
// 初始化数据
const { common_style, data_content_style, data_style } = new_style;
// 外层左右间距
const outer_spacing = (common_style?.margin_left || 0) + (common_style?.margin_right || 0) + (common_style?.padding_left || 0) + (common_style?.padding_right || 0) + border_width(common_style);
// 内容左右间距
const content_spacing = (data_content_style?.margin_left || 0) + (data_content_style?.margin_right || 0) + (data_content_style?.padding_left || 0) + (data_content_style?.padding_right || 0) + border_width(data_content_style);
// 数据左右间距
const internal_spacing = (data_style?.margin_left || 0) + (data_style?.margin_right || 0) + (data_style?.padding_left || 0) + (data_style?.padding_right || 0) + border_width(data_style);
// 一行显示的数量
const carousel_col = Number(new_form.data_source_carousel_col);
// 数据间距
const data_spacing = ['vertical', 'horizontal'].includes(new_form.data_source_direction) ? new_style.column_gap * (carousel_col - 1) : 0;
// 自定义组件宽度
const width = sys_width - outer_spacing - content_spacing - internal_spacing - data_spacing - this.propOuterContainerPadding;
const new_data_style = !isEmpty(new_style.data_style) ? new_style.data_style : this.old_data_style;
const new_data_content_style = !isEmpty(new_style.data_content_style)? new_style.data_content_style : this.old_data_style;
// 判断是平移还是整屏滚动
const { padding_top = 0, padding_bottom = 0, margin_bottom = 0, margin_top = 0 } = new_data_style;
let swiper_height = 0;
const scale_number = width / 390;
const new_scale = scale_number > 0 ? scale_number : 0;
// 间距
const space_between = new_form.data_source_direction == 'horizontal' ? new_style.column_gap : new_style.row_gap;
let col = Number(new_form.data_source_carousel_col);
// 轮播图高度控制
if (new_form.data_source_direction == 'horizontal') {
swiper_height = new_form.height * new_scale + padding_top + padding_bottom + margin_bottom + margin_top;
} else {
// 商品数量大于列数的时候,高度是列数,否则是当前的数量
col = new_list.length > carousel_col ? carousel_col : new_list.length;
swiper_height = (new_form.height * new_scale + padding_top + padding_bottom + margin_bottom + margin_top) * col + ((Number(new_form.data_source_carousel_col) - 1) * space_between);
}
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.column_gap * (carousel_col - 1)) / carousel_col;
// 横向的时候,根据选择的行数和每行显示的个数来区分具体是显示多少个
const swiper_width = (new_form.data_source_direction == 'horizontal' && new_style.rolling_fashion != 'translation') ? `width: ${ 100 / carousel_col }%;`: 'width: 100%;';
this.setData({
form: new_form,
new_style: new_style,
div_width: width,
scale: new_scale,
custom_list_length: new_form.custom_list.length - 1,
style_container: this.propIsCommonStyle ? common_styles_computer(new_style.common_style) + 'box-sizing: border-box;' : '', // 用于样式显示
style_img_container: this.propIsCommonStyle ? common_img_computer(new_style.common_style, this.propIndex) : '',
style_content_container: common_styles_computer(new_data_content_style) + 'box-sizing: border-box;', // 用于样式显示
style_content_img_container: common_img_computer(new_data_content_style),
style_chunk_container: common_styles_computer(new_data_style) + 'box-sizing: border-box;', // 用于样式显示
style_chunk_img_container: common_img_computer(new_data_style),
style_chunk_width: width,
div_height: new_form.height,
data_source_content_list: new_list,
data_source: !isEmpty(new_form.data_source)? new_form.data_source : '',
indicator_style: get_indicator_style(new_style), // 指示器的样式
indicator_location_style: get_indicator_location_style(new_style),
swiper_height: swiper_height,
swiper_width: swiper_width,
content_outer_spacing_magin: space_between + 'px',
gap_width: `width: calc(${100 / carousel_col}% - ${gap}px);`,
slides_per_view: new_style.rolling_fashion == 'translation' ? (new_form.data_source_direction != 'horizontal' ? col : new_form.data_source_carousel_col ) : 1,
show_data: new_form?.show_data || { data_key: 'id', data_name: 'name' },
field_list: new_form?.field_list || [],
});
},
get_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation' && form.data_source_direction != 'vertical') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.data_source_carousel_col;
// 存储数据显示
let nav_list = [];
// 拆分的数量
const split_num = Math.ceil(cloneList.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({
split_list: cloneList.slice(i * num, (i + 1) * num),
});
}
return nav_list;
} else {
// 否则的话,就返回全部的信息
return [
{
split_list: cloneList,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
slideChange(e) {
this.setData({
actived_index: e.detail.current,
});
},
url_event(e, index, split_index) {
if (this.data_source == 'goods' && this.data_source_content_list.length > 0) {
const list = this.data_source_content_list[index];
if (!isEmpty(list) && !isEmpty(list.split_list[split_index])) {
const new_list = list.split_list[split_index];
if (!isEmpty(new_list)) {
// 缓存商品数据
app.globalData.goods_data_cache_handle(new_list.data.id, new_list.data);
}
}
}
app.globalData.url_open(e);
},
},
};
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,434 @@
<template>
<view class="img-magic" :style="'height:' + container_size + ';' + style_container">
<view class="magic-container w h pr" :style="style_img_container">
<view class="pr" :style="'width:calc(100% + ' + outer_spacing + ');height:calc(100% + ' + outer_spacing + ');margin:-' + spacing + ';'">
<!-- 风格9 -->
<template v-if="form.style_actived == 7">
<view class="flex-row align-c jc-c style-size flex-wrap">
<view v-for="(item, index) in data_magic_list" :key="index" :style="'margin:' + spacing + ';' + ([0, 1].includes(index) ? 'width:calc(50% - ' + outer_spacing + ');height:calc(50% - ' + outer_spacing + ')' : 'width:calc((100% / 3) - ' + outer_spacing + ');height:calc(50% - ' + outer_spacing + ')')" class="style9">
<view class="w h flex-row" :style="item.data_style.background_style + content_radius">
<view class="re flex-1 oh" :style="item.data_style.background_img_style">
<template v-if="item.data_content.data_type == 'goods'">
<view class="w h flex-col" :style="'gap:'+ item.data_style.title_data_gap * 2 + 'rpx;'">
<view v-if="(!isEmpty(item.data_content.heading_title) || !isEmpty(item.data_content.subtitle)) && [0, 1].includes(index)" :class="'tl' + (item.data_style.title_line == '1' ? ' flex-row align-c' : ' flex-col')" :style="'gap:' + item.data_style.title_gap * 2 + 'rpx;'">
<template v-if="item.data_content.heading_title_type && item.data_content.heading_title_type == 'image'">
<view v-if="item.data_content.heading_title_img.length > 0" class="re oh" :style="'width:100%;height:' + (!isEmpty(item.data_style.heading_img_height) ? item.data_style.heading_img_height * magic_scale : 0) + 'px'">
<image :src="item.data_content.heading_title_img[0].url" class="ht-auto max-w" mode="heightFix" />
</view>
</template>
<template v-else>
<view class="ma-0 w text-word-break text-line-1 flex-basis-shrink" :style="item.data_style.heading_style">{{ item.data_content.heading_title || '' }}</view>
</template>
<template v-if="item.data_content.subtitle_title_type && item.data_content.subtitle_title_type == 'image'">
<view v-if="item.data_content.subtitle_title_img.length > 0" class="re oh" :style="'width:100%;height:' + (!isEmpty(item.data_style.subtitle_img_height) ? item.data_style.subtitle_img_height * magic_scale : 0) + 'px'">
<image :src="item.data_content.subtitle_title_img[0].url" class="ht-auto max-w" mode="heightFix" />
</view>
</template>
<template v-else>
<view class="ma-0 w text-word-break text-line-1 flex-basis-shrink" :style="item.data_style.subtitle_style">{{ item.data_content.subtitle || '' }}</view>
</template>
</view>
<view class="w h flex-1 oh flex-row">
<magic-carousel class="flex-1" :propKey="propKey + index" :propValue="item" :propGoodStyle="item.data_style" :propActived="form.style_actived" propType="product" :propDataIndex="index" @onCarouselChange="carousel_change"></magic-carousel>
</view>
</view>
</template>
<template v-else-if="item.data_content.data_type == 'images'">
<view class="w h flex-1 oh flex-row">
<magic-carousel class="flex-1" :propKey="propKey + index" :propValue="item" propType="img" :propActived="form.style_actived" :propDataIndex="index" @onCarouselChange="carousel_change"></magic-carousel>
</view>
</template>
<template v-else-if="item.data_content.data_type == 'custom'">
<customIndex :propKey="propKey + index" :propValue="item" :propMagicScale="magic_scale" :propDataSpacing="new_style.image_spacing" :propDataIndex="index" @onCarouselChange="carousel_change"></customIndex>
</template>
<template v-else>
<videoIndex :propKey="propKey + index" :propValue="item.data_content" :propDataStyle="item.data_style"></videoIndex>
</template>
<view v-if="item.data_style.is_show == '1' && item.data_content.list.length > 1" :class="['left', 'right'].includes(item.data_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="item.data_style.indicator_location_style">
<template v-if="item.data_style.indicator_style == 'num'">
<view :style="item.data_style.indicator_styles" class="dot-item">
<text class="num-active" :style="{ color: item.data_style.actived_color }">{{ item.actived_index + 1 }}</text
><text>/{{ item.data_content.list.length }}</text>
</view>
</template>
<template v-else>
<view v-for="(item3, index3) in item.data_content.list" :key="index3" :style="item.data_style.indicator_styles + style_actived_color(item, index3)" class="dot-item" />
</template>
</view>
</view>
</view>
</view>
</view>
</template>
<template v-else>
<view v-for="(item, index) in data_magic_list" :key="index" class="cr-main cube-selected" :style="selected_style(item) + ';margin:' + spacing + ';'">
<view class="w h flex-row" :style="item.data_style.background_style + content_radius">
<view class="re flex-1 oh" :style="item.data_style.background_img_style">
<template v-if="item.data_content.data_type == 'goods'">
<view class="w h flex-col" :style="'gap:'+ item.data_style.title_data_gap * 2 + 'rpx;'">
<view v-if="!isEmpty(item.data_content.heading_title) || !isEmpty(item.data_content.subtitle)" :class="'tl' + (item.data_style.title_line == '1' ? ' flex-row align-c' : ' flex-col')" :style="'gap:' + item.data_style.title_gap * 2 + 'rpx;'">
<template v-if="item.data_content.heading_title_type && item.data_content.heading_title_type == 'image'">
<view v-if="item.data_content.heading_title_img.length > 0" class="re oh" :style="'width:100%;height:' + (!isEmpty(item.data_style.heading_img_height) ? item.data_style.heading_img_height * magic_scale : 0) + 'px'">
<image :src="item.data_content.heading_title_img[0].url" class="ht-auto max-w" mode="heightFix" />
</view>
</template>
<template v-else>
<view class="ma-0 w text-word-break text-line-1 flex-basis-shrink" :style="item.data_style.heading_style">{{ item.data_content.heading_title || '' }}</view>
</template>
<template v-if="item.data_content.subtitle_title_type && item.data_content.subtitle_title_type == 'image'">
<view v-if="item.data_content.subtitle_title_img.length > 0" class="re oh" :style="'width:100%;height:' + (!isEmpty(item.data_style.subtitle_img_height) ? item.data_style.subtitle_img_height * magic_scale : 0) + 'px'">
<image :src="item.data_content.subtitle_title_img[0].url" class="ht-auto max-w" mode="heightFix" />
</view>
</template>
<template v-else>
<view class="ma-0 w text-word-break text-line-1 flex-basis-shrink" :style="item.data_style.subtitle_style">{{ item.data_content.subtitle || '' }}</view>
</template>
</view>
<view class="w h oh flex-row">
<magic-carousel class="flex-1" :propKey="propKey + index" :propValue="item" :propGoodStyle="item.data_style" propType="product" :propActived="form.style_actived" :propDataIndex="index" @onCarouselChange="carousel_change"></magic-carousel>
</view>
</view>
</template>
<template v-else-if="item.data_content.data_type == 'images'">
<view class="w h oh flex-row">
<magic-carousel class="flex-1" :propKey="propKey + index" :propValue="item" propType="img" :propActived="form.style_actived" :propDataIndex="index" @onCarouselChange="carousel_change"></magic-carousel>
</view>
</template>
<template v-else-if="item.data_content.data_type == 'custom'">
<customIndex :propKey="propKey + index" :propValue="item" :propMagicScale="magic_scale" :propDataSpacing="new_style.image_spacing" :propDataIndex="index" @onCarouselChange="carousel_change"></customIndex>
</template>
<template v-else>
<videoIndex :propKey="propKey + index" :propValue="item.data_content" :propDataStyle="item.data_style"></videoIndex>
</template>
<view v-if="item.data_style.is_show == '1' && item.data_content.list.length > 1" :class="['left', 'right'].includes(item.data_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="item.data_style.indicator_location_style">
<template v-if="item.data_style.indicator_style == 'num'">
<view :style="item.data_style.indicator_styles" class="dot-item">
<text class="num-active" :style="{ color: item.data_style.actived_color }">{{ item.actived_index + 1 }}</text>
<text>/{{ item.data_content.list.length }}</text>
</view>
</template>
<template v-else>
<view v-for="(item3, index3) in item.data_content.list" :key="index3" :style="item.data_style.indicator_styles + style_actived_color(item, index3)" class="dot-item" />
</template>
</view>
</view>
</view>
</view>
</template>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import magicCarousel from '@/components/diy/modules/data-magic/magic-carousel.vue';
import customIndex from '@/components/diy/modules/data-magic/custom/index.vue';
import videoIndex from '@/components/diy/modules/data-magic/video/index.vue';
import { background_computer, common_styles_computer, common_img_computer, gradient_computer, radius_computer, percentage_count, isEmpty, padding_computer, margin_computer, old_border_and_box_shadow, old_margin, old_padding, box_shadow_computer, border_computer, get_indicator_location_style, get_indicator_style, border_width } from '@/common/js/common/common.js';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
magicCarousel,
customIndex,
videoIndex
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propOuterContainerPadding: {
type: Number,
default: 0,
}
},
data() {
return {
form: {},
new_style: {},
outer_spacing: '',
// 图片间距设置
spacing: '',
// 内容圆角设置
content_radius: '',
// 图片圆角设置
// content_img_radius: '',
data_magic_list: [],
cubeCellWidth: 0,
container_size: 0,
style_container: '',
style_img_container: '',
div_width: 0,
magic_scale: 1,
// is_unlimited_size: false,
};
},
computed: {
// 根据当前页面大小计算成百分比
selected_style() {
return (item) => {
return `overflow: hidden;width: calc(${this.percentage(this.getSelectedWidth(item))} - ${this.outer_spacing} ); height: calc(${this.percentage(this.getSelectedHeight(item))} - ${this.outer_spacing} ); top: ${this.percentage(this.getSelectedTop(item))}; left: ${this.percentage(this.getSelectedLeft(item))};`;
};
},
style_actived_color() {
return (item, index) => {
return item.actived_index == index ? `background: ${item.data_style.actived_color};` : '';
};
},
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
const container_height = !isEmpty(new_form.container_height) ? new_form.container_height : sys_width;
const density = !isEmpty(new_form.magic_cube_density) ? new_form.magic_cube_density : 4;
const { margin_left, margin_right } = new_style.common_style;
const width = sys_width - margin_left - margin_right - border_width(new_style.common_style) - this.propOuterContainerPadding;
this.setData({
form: new_form,
new_style: new_style,
outer_spacing: new_style.image_spacing * 2 + 'rpx',
spacing: new_style.image_spacing + 'rpx',
content_radius: radius_computer(new_style.data_radius),
// content_img_radius: radius_computer(new_style.img_radius),
data_magic_list: this.get_data_magic_list(new_form.data_magic_list, new_style),
style_container: common_styles_computer(new_style.common_style) + 'box-sizing: border-box;', // 用于样式显示
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
magic_scale: width / 390,
div_width: sys_width,
cubeCellWidth: sys_width / density,
container_size: container_height * (width / 390) + 'px',
});
},
get_data_magic_list(data, new_style) {
data.forEach((item) => {
const data_content = item.data_content;
const data_style = item.data_style;
item.actived_index = 0;
// 指示器样式
data_style.indicator_styles = get_indicator_style(data_style);
data_style.indicator_location_style = get_indicator_location_style(data_style);
// 获取当前的margin
const chunk_margin = data_style?.chunk_margin || old_margin;
// 计算左右间距
const left_right_width_margin = (chunk_margin?.margin_left || 0) + (chunk_margin?.margin_right || 0);
// 计算上下间距
const top_bottom_height_margin = (chunk_margin?.margin_top || 0) + (chunk_margin?.margin_bottom || 0);
data_style.background_style = gradient_computer(data_style) + margin_computer(data_style?.chunk_margin || old_margin) + `width: calc(100% - ${ left_right_width_margin }px);height:calc(100% - ${ top_bottom_height_margin }px);` + box_shadow_computer(data_style?.data_common_style || old_border_and_box_shadow) + border_computer(data_style?.data_common_style || old_border_and_box_shadow);
data_style.background_img_style = background_computer(data_style) + padding_computer(data_style?.chunk_padding || old_padding);
// 商品价格处理
data_style.goods_price_symbol_style = this.goods_trends_config(data_style, 'price_symbol');
data_style.goods_price_unit_style = this.goods_trends_config(data_style, 'price_unit');
let fit = '';
if (data_content.img_fit == 'contain') {
fit = 'aspectFit';
} else if (data_content.img_fit =='fill') {
fit = 'scaleToFill';
} else if (data_content.img_fit == 'cover') {
fit = 'aspectFill';
}
data_content.fit = fit;
// 商品名称和价格样式
data_style.goods_title_style = this.goods_trends_config(data_style, 'title') + `line-height: ${ (item.data_style.goods_title_size + 3) * 2 }rpx;height: ${ (item.data_style.goods_title_size + 3) * 2 }rpx;`;
data_style.goods_price_style = this.goods_trends_config(data_style, 'price') + `line-height: ${ item.data_style.goods_price_size * 2 }rpx;height: ${ (item.data_style.goods_title_size + 3) * 2 }rpx;`;
const radius = !isEmpty(data_style.img_radius) ? data_style.img_radius : { radius: 4, radius_top_left: 4, radius_top_right: 4, radius_bottom_left: 4, radius_bottom_right: 4 };
data_style.get_img_radius = radius_computer(radius);
data_style.chunk_padding_data = padding_computer(data_style.chunk_padding) + 'box-sizing: border-box;';
data_style.heading_style = this.trends_config(data_style, 'heading');
data_style.subtitle_style = this.trends_config(data_style, 'subtitle');
if (data_content.data_type == 'goods') {
data_content.list = this.commodity_list(data_content.goods_list, data_content.goods_num, data_content, data_style);
} else if (data_content.data_type == 'custom' && ['vertical-scroll', 'horizontal'].includes(data_content.data_source_direction)) {
// 是自定义并且是轮播状态的时候,添加数据
const list = this.data_source_content_list(data_content);
const carousel_col = data_content?.data_source_carousel_col || 1;
const num = new_style.rolling_fashion == 'translation' ? list.length : Math.ceil(list.length / carousel_col);
data_content.list = Array(num);
} else {
data_content.list = data_content.images_list;
}
});
return data;
},
// 数据来源的内容
data_source_content_list(data_content){
if (data_content.is_custom_data == '1') {
if (Number(data_content.data_source_content.data_type) === 0) {
return data_content.data_source_content.data_list;
} else {
return data_content.data_source_content.data_auto_list.map((item) => ({
id: Math.random(),
new_cover: [],
new_title: '',
data: item,
}));
}
} else {
return data_content.data_source_content.data_list;
}
},
/*
** 组装产品的数据
** @param {Array} list 商品列表
** @param {Number} num 显示数量
** @return {Array}
*/
commodity_list(list, num, data_content, data_style) {
if (list.length > 0) {
// 深拷贝一下,确保不会出现问题
const goods_list = JSON.parse(JSON.stringify(list)).map((item) => ({
...item.data,
title: !isEmpty(item.new_title) ? item.new_title : item.data.title,
new_cover: item.new_cover,
}));
// 存储数据显示
let nav_list = [];
// 如果是滑动,需要根据每行显示的个数来区分来拆分数据 translation 表示的是平移
if (data_style.rolling_fashion != 'translation') {
// 拆分的数量
const split_num = Math.ceil(goods_list.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({ split_list: goods_list.slice(i * num, (i + 1) * num) });
}
return nav_list;
} else {
return this.rotation_calculation(goods_list, num, data_content, data_style);
}
} else {
return [];
}
},
rotation_calculation(list, num, data_content, data_style) {
// 存储数据显示
let nav_list = [];
const goods_outerflex = data_content.goods_outerflex;
const rotation_direction = data_style.rotation_direction;
// 如果是商品是横排的,轮播也是横排的,就不对商品进行拆分/如果商品是竖排的,轮播也是竖排的,不对商品进行拆分
if ((goods_outerflex == 'row' && rotation_direction == 'horizontal') || (goods_outerflex == 'col' && rotation_direction == 'vertical')) {
list.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
} else {
// 拆分的数量
const split_num = Math.ceil(list.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({ split_list: list.slice(i * num, (i + 1) * num) });
}
return nav_list;
}
return nav_list;
},
getSelectedWidth(item) {
return (item.end.x - item.start.x + 1) * this.cubeCellWidth;
},
//计算选中层的高度。
getSelectedHeight(item) {
return (item.end.y - item.start.y + 1) * this.cubeCellWidth;
},
//计算选中层的右边距离。
getSelectedTop(item) {
return (item.start.y - 1) * this.cubeCellWidth;
},
//计算选中层的左边距离。
getSelectedLeft(item) {
return (item.start.x - 1) * this.cubeCellWidth;
},
// 计算成百分比
percentage(num) {
return percentage_count(num, this.div_width);
},
goods_trends_config(style, key) {
return this.text_style(style[`goods_${key}_typeface`], style[`goods_${key}_size`], style[`goods_${key}_color`]);
},
// 根据传递的参数,从对象中取值
trends_config(style, key) {
return this.text_style(style[`${key}_typeface`], style[`${key}_size`], style[`${key}_color`]);
},
text_style(typeface, size, color) {
return `font-weight:${typeface}; font-size: ${size * 2}rpx; color: ${color};`;
},
carousel_change(actived_index, index) {
if (this.data_magic_list[index]) {
this.data_magic_list[index].actived_index = actived_index;
}
},
},
};
</script>
<style lang="scss" scoped>
// 图片魔方是一个正方形,根据宽度计算高度
.img-magic {
overflow: hidden;
}
.cube-selected {
position: absolute;
text-align: center;
box-sizing: border-box;
}
.text-line-1 {
word-break: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
}
.style-size {
height: 100%;
width: 100%;
.style9 {
position: relative;
overflow: hidden;
}
}
.dot-center {
left: 50%;
transform: translateX(-50%);
}
.dot-right {
right: 0;
}
.dot {
z-index: 3;
.dot-item {
margin: 0 6rpx;
}
}
.gap-20 {
gap: 40rpx;
}
.w {
width: 100%;
}
.h {
height: 100%;
}
.flex-basis-shrink {
flex-basis: content;
flex-shrink: 1;
}
</style>

View File

@@ -0,0 +1,334 @@
<template>
<view class="data-tabs ou" :class="'data-tabs-' + propKey" :style="style_container">
<view class="ou" :style="style_img_container">
<componentDiyModulesTabsView :propKey="propKey" :propValue="data_tabs" :propIsTop="top_up == '1'" :propTop="sticky_top" :propStyle="tabs_style" :propsTabsContainer="tabs_container" :propsTabsImgContainer="tabs_img_container" :propCustomNavHeight="propIsTabsUseSafeDistance ? (propCustomNavHeight * 2 + 'rpx') : '0rpx'" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" :propTabsBackground="tabs_background" @onTabsTap="tabs_click_event"></componentDiyModulesTabsView>
<view :style="data_margin_top">
<view :style="data_container">
<view :style="data_img_container">
<template v-if="tabs_data_type == 'goods'">
<view class="oh" :style="data_content_container">
<view class="oh" :style="data_content_img_container">
<componentGoodsList ref="diy_goods_list" :propKey="diy_key" :propDiyIndex="propDiyIndex" :propValue="tabs_list" :propIsCommonStyle="false" @goods_buy_event="goods_buy_event"></componentGoodsList>
</view>
</view>
</template>
<template v-else-if="tabs_data_type == 'article'">
<view class="oh" :style="data_content_container">
<view class="oh" :style="data_content_img_container">
<componentArticleList :propKey="diy_key" :propValue="tabs_list" :propIsCommonStyle="false"></componentArticleList>
</view>
</view>
</template>
<template v-else-if="tabs_data_type == 'custom'">
<componentCustomList :propKey="diy_key" :propValue="tabs_list" :propOuterContainerPadding="outer_container_width" :propIsCommonStyle="false"></componentCustomList>
</template>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, margin_computer, background_computer, gradient_computer, isEmpty, old_border_and_box_shadow, old_margin, old_radius, old_padding, border_computer, box_shadow_computer, radius_computer, get_math } from '@/common/js/common/common.js';
import componentDiyModulesTabsView from '@/components/diy/modules/tabs-view';
import componentGoodsList from '@/components/diy/goods-list';
import componentArticleList from '@/components/diy/article-list';
import componentCustomList from '@/components/diy/custom';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
components: {
componentDiyModulesTabsView,
componentGoodsList,
componentArticleList,
componentCustomList,
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propTop: {
type: Number,
default: 0,
},
propCustomNavHeight: {
type: Number,
default: 33,
},
propScrollTop: {
type: Number,
default: 0,
},
// 顶部导航是否开启沉浸模式
propIsImmersionModel: {
type: Boolean,
default: false,
},
propKey: {
type: [String, Number],
default: '',
},
propDiyIndex: {
type: Number,
default: 0,
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 0,
},
// 选项卡是否使用安全距离
propIsTabsUseSafeDistance: {
type: Boolean,
default: true
}
},
data() {
return {
style_container: '',
style_img_container: '',
data_tabs: {},
tabs_list: {},
// 是否滑动置顶
top_up: '0',
tabs_top: 0,
tabs_background: 'background:transparent',
custom_nav_height: 33,
diy_key: '',
// 选项卡背景设置
tabs_container: '',
tabs_img_container: '',
// 商品区域背景设置
data_margin_top: '',
data_container: '',
data_img_container: '',
// #ifdef MP
nav_safe_space: bar_height + 5,
// #endif
// #ifdef H5 || MP-TOUTIAO
nav_safe_space: bar_height + 7,
// #endif
// #ifdef APP
nav_safe_space: bar_height + 0,
// #endif
tabs_style: '',
// 选项卡默认数据
tabs_index: 0,
sticky_top: 0,
tabs_data_type: 'goods',
outer_container_width: 0,
// 数据样式
data_content_container: '',
data_content_img_container: '',
tabs_sliding_fixed_bg: '',
// 滑动固定的背景
tabs_sliding_fixed_bg: '',
data_content_style: {
color_list: [{ color: '', color_percentage: undefined }],
direction: '180deg',
background_img_style: '2',
background_img: [],
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
// 边框样式
border_is_show: '0',
border_color: '#FF3F3F',
border_style: 'solid',
border_size: {
padding: 1,
padding_top: 1,
padding_right: 1,
padding_bottom: 1,
padding_left: 1,
},
// 阴影
box_shadow_color: '',
box_shadow_x: 0,
box_shadow_y: 0,
box_shadow_blur: 0,
box_shadow_spread: 0,
}
};
},
watch: {
propScrollTop(newVal) {
if (newVal + this.sticky_top + this.custom_nav_height > this.tabs_top + this.nav_safe_space && this.top_up == '1') {
let new_style = this.propValue.style || {};
let tabs_bg = new_style.common_style.color_list;
let new_tabs_background = '';
if (tabs_bg.length > 0 && (tabs_bg[0].color || null) != null) {
new_tabs_background = gradient_computer(new_style.common_style);
}
let new_tabs_background_img = background_computer(new_style.common_style);
if (new_tabs_background_img.length > 0) {
new_tabs_background_img += 'background-position: top left;';
}
this.tabs_background = (new_tabs_background.length > 0 ? new_tabs_background : 'background:#fff;') + new_tabs_background_img;
} else {
this.tabs_background = 'background:transparent';
}
},
propTop(val) {
this.init();
},
propKey(val) {
// 初始化
this.setData({
diy_key: val,
});
this.init();
},
},
created() {
this.init();
},
mounted() {
this.$nextTick(() => {
const self = this;
setTimeout(() => {
self.getTop();
});
// #ifdef H5 || MP-TOUTIAO
// 获取自定义导航栏高度
this.setData({
custom_nav_height: this.propCustomNavHeight,
});
// #endif
});
},
methods: {
init() {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_content = new_data.content || {};
const new_style = new_data.style || {};
// 公共样式
const common_style = new_style.common_style;
let tabs_style_obj = {
padding_top: (common_style.padding_top - this.propCustomNavHeight) < 0 ? 0 : common_style.padding_top - this.propCustomNavHeight,
padding_left: common_style.padding_left,
padding_right: common_style.padding_right,
};
let new_tabs_style = padding_computer(tabs_style_obj) + `position:relative;left: -${tabs_style_obj.padding_left * 2}rpx;right: -${tabs_style_obj.padding_right * 2}rpx;width:100%;`;
// 如果是历史数据的话,就执行默认添加下边距
if (isEmpty(new_style.tabs_padding)) {
new_tabs_style += 'padding-bottom: 20rpx;';
}
const { tabs_bg_color_list = [], tabs_bg_direction = '', tabs_bg_background_img_style = '', tabs_bg_background_img = [], tabs_radius = old_radius, tabs_padding = old_padding, data_content_color_list = [], data_content_direction = '', data_content_background_img_style = '', data_content_background_img = [], data_content_margin = old_margin, data_content_padding = old_padding, data_content_radius = old_radius } = new_style;
// 选项卡背景设置
const tabs_data = {
color_list: tabs_bg_color_list,
direction: tabs_bg_direction,
background_img_style: tabs_bg_background_img_style,
background_img: tabs_bg_background_img,
};
// 商品区域背景设置
const data_content_data = {
color_list: data_content_color_list,
direction: data_content_direction,
background_img_style: data_content_background_img_style,
background_img: data_content_background_img,
};
const data_content = new_style?.data_content || old_border_and_box_shadow;
const tabs_content = new_style?.tabs_content || old_border_and_box_shadow;
//显示的数据处理
this.tabs_click_event(0);
this.setData({
top_up: new_content.tabs_top_up,
sticky_top: this.propTop + (new_style?.tabs_margin?.margin_top || 0),
data_tabs: new_data,
// 自定义需要做等比缩放,因此宽度需要减去 外层通用的宽度和内容区域的宽度
outer_container_width: common_style.margin_left + common_style.margin_right + common_style.padding_left + common_style.padding_right + new_style.data_content_margin.margin_left + new_style.data_content_margin.margin_right + new_style.data_content_padding.padding_left + new_style.data_content_padding.padding_right,
style_container: common_styles_computer(common_style),
style_img_container: common_img_computer(common_style, this.propIndex),
tabs_style: new_tabs_style,
tabs_sliding_fixed_bg: gradient_computer(tabs_data),
tabs_container: gradient_computer(tabs_data) + radius_computer(tabs_radius) + margin_computer(new_style?.tabs_margin || old_margin) + border_computer(tabs_content) + box_shadow_computer(tabs_content) + 'overflow: hidden;',
tabs_img_container: background_computer(tabs_data) + padding_computer(tabs_padding) + 'box-sizing: border-box;overflow: hidden;',
data_margin_top: 'margin-top:' + (new_style?.data_content_spacing || 0) * 2 + 'rpx;',
data_container: gradient_computer(data_content_data) + margin_computer(data_content_margin) + radius_computer(data_content_radius) + box_shadow_computer(data_content) + border_computer(data_content) + 'overflow: hidden;',
data_img_container: background_computer(data_content_data) + padding_computer(data_content_padding) + 'box-sizing: border-box;overflow: hidden;',
});
},
tabs_click_event(index) {
let new_data = JSON.parse(JSON.stringify(this.propValue));
// 显示的数据处理
const tabs_data_list = new_data.content.tabs_list[index] || {};
const tabs_data_type = tabs_data_list?.tabs_data_type || '';
let tabs_list = {};
// 内容样式
let data_content_container = '';
let data_content_img_container = '';
if (tabs_data_type === 'goods') {
tabs_list = tabs_data_list.goods_config;
const new_style = tabs_data_list.goods_config.style;
// 内容样式
data_content_container = common_styles_computer(new_style?.data_content_style || this.data_content_style);
data_content_img_container = common_img_computer(new_style?.data_content_style || this.data_content_style);
} else if (tabs_data_type === 'article') {
tabs_list = tabs_data_list.article_config;
const new_style = tabs_data_list.article_config.style;
// 内容样式
data_content_container = common_styles_computer(new_style?.data_content_style || this.data_content_style);
data_content_img_container = common_img_computer(new_style?.data_content_style || this.data_content_style);
} else if (tabs_data_type === 'custom') {
tabs_list = tabs_data_list.custom_config;
}
this.setData({
tabs_data_type: tabs_data_type,
tabs_index: index,
tabs_list: tabs_list,
data_content_container: data_content_container,
data_content_img_container: data_content_img_container,
diy_key: get_math(),
});
},
// 获取商品距离顶部的距离
getTop() {
const query = uni.createSelectorQuery().in(this);
query
.select('.data-tabs-' + this.propKey)
.boundingClientRect((res) => {
if ((res || null) != null) {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_style = new_data.style || {};
this.setData({
tabs_top: res.top - (new_style.common_style?.margin_top || 0),
});
}
})
.exec();
},
goods_buy_event(index, goods = {}, params = {}, back_data = null) {
this.$emit('goods_buy_event', index, goods, params, back_data);
},
goods_cart_back_event(e) {
if ((this.$refs.diy_goods_list || null) != null) {
this.$refs.diy_goods_list.goods_cart_back_event(e);
}
},
},
};
</script>
<style scoped lang="scss"></style>

784
components/diy/diy.vue Normal file
View File

@@ -0,0 +1,784 @@
<template>
<view :style="page_style">
<view :style="page_img_style">
<scroll-view :scroll-y="true" class="ht" @scroll="on_scroll_event" @scrolltolower="on_scroll_lower_event" @scrolltoupper="on_scroll_upper_event" lower-threshold="60" scroll-with-animation="true">
<!-- 头部小程序兼容 -->
<view class="pr header">
<componentDiyHeader :propKey="header_data.id" :propValue="header_data.com_data" :propScrollTop="head_scroll_top" @onImmersionModelCallBack="immersion_model_call_back" @onLocationBack="choice_location_back"></componentDiyHeader>
</view>
<view :style="content_padding">
<view class="content flex-col" :style="'padding-top:calc(' + (temp_is_header_top ? temp_header_top + temp_sticky_top + 'px)' : '0px)')">
<view v-for="item in tabs_data" :key="item.key">
<template v-if="item.is_enable == '1'">
<componentDiyTabs v-if="item.key == 'tabs'" :propIndex="is_immersive_style_and_general_safe_distance_value ? item.index : -1" :propContentPadding="content_padding" :propValue="item.com_data" :propTop="get_tabs_data_prop_top" :propStickyTop="get_tabs_data_prop_sticky_top" :propIsImmersionModel="is_immersion_model && is_the_safe_distance_enabled && item.com_data.content.tabs_top_up == '1'" :propNavIsTop="is_header_top" :propTabsIsTop="true" @onComputerHeight="tabs_height_event" @onTabsTap="tabs_click_event"></componentDiyTabs>
<componentDiyTabsCarousel v-else-if="item.key == 'tabs-carousel'" :propIndex="is_immersive_style_and_general_safe_distance_value ? item.index : -1" :propContentPadding="content_padding" :propValue="item.com_data" :propTop="get_tabs_data_prop_top" :propStickyTop="get_tabs_data_prop_sticky_top" :propIsImmersionModel="is_immersion_model && is_the_safe_distance_enabled" :propScrollTop="scroll_top" :propTabsIsTop="true" @onComputerHeight="tabs_height_event" @onTabsTap="tabs_click_event" @onVideoPlay="video_play"></componentDiyTabsCarousel>
</template>
</view>
<template v-if="is_tabs_type">
<template v-if="diy_data.length > 0">
<view v-for="(item, index) in diy_data" :key="index" :style="'margin-top:' + (['float-window'].includes(item.key) ? '0rpx;z-index:1' : -(item.com_data.style.common_style.floating_up * 2 || 0) + 'rpx;z-index:' + (!isEmpty(item.com_data.style.common_style.module_z_index) ? item.com_data.style.common_style.module_z_index : 0))">
<!-- 基础组件 -->
<template v-if="item.is_enable == '1'">
<componentDiySearch v-if="item.key == 'search'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiySearch>
<componentDiyCarousel v-else-if="item.key == 'carousel'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data" @onVideoPlay="video_play"></componentDiyCarousel>
<componentDiyNavGroup v-else-if="item.key == 'nav-group'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyNavGroup>
<componentDiyUserInfo v-else-if="item.key == 'user-info'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyUserInfo>
<componentDiyNotice v-else-if="item.key == 'notice'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyNotice>
<componentDiyVideo v-else-if="item.key == 'video'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyVideo>
<componentDiyArticleList v-else-if="item.key == 'article-list'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyArticleList>
<componentDiyArticleTabs v-else-if="item.key == 'article-tabs'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data" :propTop="get_diy_prop_top(item.com_data)" :propScrollTop="scroll_top" :propCustomNavHeight="get_diy_custom_nav_height(item.com_data)" :propIsTabsUseSafeDistance="getPropIsTabsUseSafeDistance"></componentDiyArticleTabs>
<componentDataTabs v-else-if="item.key == 'data-tabs'" :ref="'diy_goods_buy' + index" :propIndex="get_prop_index(item)" :propDiyIndex="index" :propKey="item.id + index" :propValue="item.com_data" :propTop="get_diy_prop_top(item.com_data)" :propScrollTop="scroll_top" :propCustomNavHeight="get_diy_custom_nav_height(item.com_data)" :propIsTabsUseSafeDistance="getPropIsTabsUseSafeDistance" @goods_buy_event="goods_buy_event"></componentDataTabs>
<componentDiyGoodsTabs v-else-if="item.key == 'goods-tabs'" :ref="'diy_goods_buy' + index" :propIndex="get_prop_index(item)" :propDiyIndex="index" :propKey="item.id + index" :propValue="item.com_data" :propTop="get_diy_prop_top(item.com_data)" :propScrollTop="scroll_top" :propCustomNavHeight="get_diy_custom_nav_height(item.com_data)" :propIsTabsUseSafeDistance="getPropIsTabsUseSafeDistance" @goods_buy_event="goods_buy_event"></componentDiyGoodsTabs>
<componentDiyGoodsList v-else-if="item.key == 'goods-list'" :ref="'diy_goods_buy' + index" :propIndex="get_prop_index(item)" :propDiyIndex="index" :propKey="item.id + index" :propValue="item.com_data" @goods_buy_event="goods_buy_event"></componentDiyGoodsList>
<componentDiyDataMagic v-else-if="item.key == 'data-magic'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyDataMagic>
<componentDiyCustom v-else-if="item.key == 'custom'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyCustom>
<componentDiyImgMagic v-else-if="item.key == 'img-magic'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyImgMagic>
<componentDiyHotZone v-else-if="item.key == 'hot-zone'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyHotZone>
<componentDiySeckill v-else-if="item.key == 'seckill'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiySeckill>
<!-- 插件 -->
<componentDiyCoupon v-else-if="item.key == 'coupon'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyCoupon>
<!-- 工具组件 -->
<componentDiyFloatWindow v-else-if="item.key == 'float-window'" :propKey="item.id + index" :propValue="item.com_data"></componentDiyFloatWindow>
<componentDiyTitle v-else-if="item.key == 'title'" :propKey="item.id + index" :propIndex="get_prop_index(item)" :propValue="item.com_data"></componentDiyTitle>
<componentDiyAuxiliaryLine v-else-if="item.key == 'row-line'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyAuxiliaryLine>
<componentDiyRichText v-else-if="item.key == 'rich-text'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyRichText>
<componentDiyAuxiliaryBlank v-else-if="item.key == 'auxiliary-blank'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyAuxiliaryBlank>
</template>
</view>
</template>
<!-- diy底部卡槽 -->
<slot name="diy-bottom-content"></slot>
<slot name="diy-bottom-common"></slot>
</template>
<template v-else>
<!-- 商品九宫格列表 -->
<view v-if="goods_list.length > 0" class="padding-horizontal-main padding-top-main oh">
<component-goods-list :propData="{ style_type: goods_show_type_value, goods_list: goods_list, random: random_value }" :propLabel="plugins_label_data" :propCurrencySymbol="currency_symbol"></component-goods-list>
</view>
<view v-else class="pr">
<!-- 提示信息 -->
<component-no-data :propStatus="goods_list_loding_status" :propMsg="goods_list_loding_msg" propLoadingLogoTop="30%"></component-no-data>
</view>
<!-- diy底部卡槽 -->
<template v-if="goods_bottom_line_status">
<slot name="diy-bottom-content"></slot>
</template>
<slot name="diy-bottom-common"></slot>
</template>
<view class="z-i-deep">
<!-- 商品购买 -->
<component-goods-buy ref="goods_buy" v-on:CartSuccessEvent="goods_cart_back_event"></component-goods-buy>
<!-- 视频播放 -->
<uni-popup ref="popup" type="center" border-radius="20rpx" :mask-click="false">
<view class="flex-col align-c jc-c gap-10">
<video :src="video_src" id="carousel_video" :autoplay="true" :controls="true" show-fullscreen-btn class="radius-md" :style="{ width: popup_width, height: popup_height }"></video>
<iconfont name="icon-qiandao-tancguanbi" size="56rpx" color="#ccc" propContainerDisplay="flex" @tap="video_close"></iconfont>
</view>
</uni-popup>
</view>
</view>
</view>
<!-- 当前diy页面底部菜单非公共底部菜单 -->
<block v-if="is_show_footer">
<componentDiyFooter :propKey="footer_data.id" :propValue="footer_data.com_data" @onFooterHeight="footer_height_value_event"></componentDiyFooter>
<view v-if="footer_height_value > 0" :style="'height:' + footer_height_value + 'rpx;'"></view>
</block>
<!-- 底部卡槽 -->
<slot name="bottom"></slot>
</scroll-view>
</view>
</view>
</template>
<script>
const app = getApp();
import { isEmpty, common_styles_computer, background_computer, padding_computer } from '@/common/js/common/common.js';
import componentDiyHeader from '@/components/diy/header';
import componentDiyFooter from '@/components/diy/footer';
import componentDiyTabs from '@/components/diy/tabs';
import componentDiySearch from '@/components/diy/search';
import componentDiyCarousel from '@/components/diy/carousel';
import componentDiyUserInfo from '@/components/diy/user-info';
import componentDiyNotice from '@/components/diy/notice';
import componentDiyVideo from '@/components/diy/video';
import componentDiyArticleList from '@/components/diy/article-list';
import componentDiyArticleTabs from '@/components/diy/article-tabs';
import componentDiyHotZone from '@/components/diy/hot-zone';
import componentDiyCoupon from '@/components/diy/coupon';
import componentDiyFloatWindow from '@/components/diy/float-window';
import componentDiyTitle from '@/components/diy/title';
import componentDiyAuxiliaryLine from '@/components/diy/auxiliary-line';
import componentDiyRichText from '@/components/diy/rich-text';
import componentDiyAuxiliaryBlank from '@/components/diy/auxiliary-blank';
import componentDiyNavGroup from '@/components/diy/nav-group';
import componentDiyGoodsList from '@/components/diy/goods-list';
import componentDiyGoodsTabs from '@/components/diy/goods-tabs';
import componentDiyDataMagic from '@/components/diy/data-magic';
import componentDiyCustom from '@/components/diy/custom';
import componentDiyImgMagic from '@/components/diy/img-magic';
import componentDiySeckill from '@/components/diy/seckill';
import componentDiyTabsCarousel from '@/components/diy/tabs-carousel';
import componentDataTabs from '@/components/diy/data-tabs';
import componentGoodsList from '@/components/goods-list/goods-list';
import componentNoData from '@/components/no-data/no-data';
import componentBottomLine from '@/components/bottom-line/bottom-line';
import componentGoodsBuy from '@/components/goods-buy/goods-buy';
import componentSearch from '@/components/search/search';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
name: 'diy',
props: {
propValue: {
type: Object,
default: () => ({}),
},
propDataId: {
type: [String, Number],
default: '',
},
propKey: {
type: [String, Number],
default: 0,
},
},
components: {
componentDiyHeader,
componentDiyFooter,
componentDiyTabs,
componentDiySearch,
componentDiyCarousel,
componentDiyUserInfo,
componentDiyNotice,
componentDiyVideo,
componentDiyArticleList,
componentDiyArticleTabs,
componentDiyHotZone,
componentDiyCoupon,
componentDiyAuxiliaryLine,
componentDiyRichText,
componentDiyFloatWindow,
componentDiyTitle,
componentDiyAuxiliaryBlank,
componentDiyNavGroup,
componentDiyGoodsList,
componentDiyGoodsTabs,
componentDiyDataMagic,
componentDiyCustom,
componentDiyImgMagic,
componentDiySeckill,
componentDiyTabsCarousel,
componentDataTabs,
componentGoodsList,
componentNoData,
componentBottomLine,
componentGoodsBuy,
componentSearch,
},
data() {
return {
// 基础配置
currency_symbol: app.globalData.currency_symbol(),
// 是否有选项卡
is_tabs: false,
// 是否是模块数据或者是九宫格商品分类样式数据, 默认模块数据
is_tabs_type: true,
// 是否开启沉浸模式
is_immersion_model: false,
// 5,7,0 是误差,, 10 是下边距66是高度bar_height是不同小程序下的导航栏距离顶部的高度
// #ifdef MP
sticky_top: bar_height + 5 + 10,
// #endif
// #ifdef H5 || MP-TOUTIAO
sticky_top: bar_height + 7 + 10,
// #endif
// #ifdef APP
sticky_top: bar_height + 0 + 10,
// #endif
header_top: '',
temp_sticky_top: 0,
temp_header_top: '0px',
is_header_top: false,
temp_is_header_top: false,
scroll_top: 0,
// 选项卡高度
tabs_height: 0,
header_data: {},
footer_data: {},
// 选项卡数据
tabs_data: {},
diy_data: [],
page_style: '',
page_img_style: '',
is_show_footer: false,
tabs_home_id: this.propDataId,
// 商品列表
goods_list: [],
goods_total: 0,
goods_page_total: 0,
goods_page: 1,
// 数据展示样式0图文、1九方格
goods_show_type_value: 1,
// 增加随机数,避免无法监听数据列表内部数据更新
random_value: 0,
// 标签插件
plugins_label_data: null,
goods_list_loding_status: 1,
goods_list_loding_msg: '',
goods_bottom_line_status: false,
// 判断数据是否在加载中
data_is_loading: 0,
// 缓存key
cache_key: app.globalData.data.cache_diy_data_key,
// 底部导航高度
footer_height_value: 0,
// 商品ref索引
goods_index: 0,
// 视频播放逻辑
video_src: '',
popup_width: '0rpx',
popup_height: '0rpx',
// 顶部导航是否换行
is_search_alone_row: false,
data_alone_row_space: 0,
content_padding: '',
outer_container_padding: 0,
// 滚动延迟器
head_scroll_top: 0,
scroll_throttle_timeout: null,
// 是否开启安全距离
is_the_safe_distance_enabled: false,
// 是否开启置顶
is_tabs_data_topped: false,
};
},
computed: {
get_tabs_data_prop_top() {
// 开启了沉浸式时的处理
if (this.is_immersion_model) {
// 并且开启了安全距离
return this.is_the_safe_distance_enabled ? this.temp_header_top : 0;
} else {
return this.temp_header_top;
}
},
get_tabs_data_prop_sticky_top() {
// 开启了沉浸式时的处理
if (this.is_immersion_model) {
// 并且开启了安全距离
return this.is_the_safe_distance_enabled ? this.temp_sticky_top : 0;
} else {
return this.temp_sticky_top;
}
},
get_prop_index() {
return (item) => {
return this.is_the_safe_distance_enabled && this.tabs_data.length == 0 ? item.index : -1;
}
},
get_diy_prop_top() {
return (item) => {
// 不开启沉浸模式时的处理
if (!this.is_immersion_model) {
return this.temp_sticky_top + this.tabs_height;
} else {
// 开启沉浸模式且开启选项卡置顶时
if (this.is_tabs_data_topped) {
return this.tabs_height;
} else {
// 开启安全距离
let is_general_safe_distance_num = this.temp_sticky_top;
// #ifdef H5 || MP-TOUTIAO
is_general_safe_distance_num = this.is_header_top ? this.temp_sticky_top : 0
// #endif
if (this.is_the_safe_distance_enabled) {
return is_general_safe_distance_num;
} else {
if (item?.content?.is_general_safe_distance == '1') {
return is_general_safe_distance_num;
} else {
return 0;
}
}
}
}
}
},
get_diy_custom_nav_height() {
return (item) => {
let header_height = (this.is_search_alone_row ? 66 + this.data_alone_row_space : 33);
// #ifdef H5 || MP-TOUTIAO
header_height = this.is_header_top ? header_height : 0;
// #endif
// 不开启沉浸模式时的处理
if (!this.is_immersion_model) {
return header_height;
} else {
// 开启沉浸模式且开启选项卡置顶时
if (this.is_tabs_data_topped) {
return 0;
} else {
// 开启沉浸模式时并且开启安全距离
if (this.is_the_safe_distance_enabled) {
return this.is_search_alone_row ? 66 + this.data_alone_row_space : 33;
} else {
if (item?.content?.is_general_safe_distance == '1') {
return header_height;
} else {
return 0;
}
}
}
}
}
},
getPropIsTabsUseSafeDistance() {
let is_tabs_use_safe_distance = this.is_immersion_model;
// #ifdef H5 || MP-TOUTIAO
is_tabs_use_safe_distance = this.is_immersion_model && this.is_header_top;
// #endif
return is_tabs_use_safe_distance || !this.is_immersion_model;
}
},
watch: {
propKey(val) {
// 如果当前存在别的diy或者商品分类tabs则不更新数据
if ((this.tabs_id || null) == null) {
// 初始化
this.init();
}
},
},
created() {
// 初始化配置
this.init_config();
// 初始化
this.init();
},
methods: {
isEmpty,
// 初始化配置
init_config(status) {
if ((status || false) == true) {
// 是否显示底部菜单如果当前地址已经存在系统底部菜单中则不显示当前diy页面自定义的底部菜单
var is_show_footer = parseInt(this.propValue.header.com_data.content.bottom_navigation_show || 0) == 1;
var is_tabbar = app.globalData.is_tabbar_pages();
this.setData({
is_show_footer: is_show_footer && !is_tabbar,
});
// diy页面不显示底部菜单则设置底部菜单高度为0
if(!this.is_show_footer) {
// 存储diy页面底部菜单高度
if(app.globalData.current_page(false) == 'pages/diy/diy') {
app.globalData.app_diy_tabbar_height_save(0);
}
}
} else {
app.globalData.is_config(this, 'init_config');
}
},
// 初始化
init() {
const { header = {}, diy_data = [], tabs_data = [] } = this.propValue;
// 头部的样式
let header_style = header.com_data.style;
let new_diy_index = 0;
let new_tabs_data = [];
let new_diy_data = [];
if (tabs_data.length > 0) {
tabs_data.forEach((item) => {
// 修改item的内容
item = this.get_index_content(new_diy_index, header, header_style, item);
new_tabs_data.push(item);
new_diy_index++;
});
new_diy_data = diy_data;
} else {
new_tabs_data = tabs_data;
// 过滤数据
diy_data.forEach((item) => {
// 判断是否是商品列表
if (item.com_name == 'float-window') {
item.index = -1;
} else {
// 修改item的内容
item = this.get_index_content(new_diy_index, header, header_style, item);
new_diy_data.push(item);
new_diy_index++;
}
});
}
const { padding_right = 0, padding_left = 0 } = header_style.common_style;
const new_is_search_alone_row = header.com_data.content.data_alone_row_value.length > 0 ? true : false;
const new_data_alone_row_space = parseInt(header_style.data_alone_row_space || 0);
// tabs选项卡数据过滤
this.setData({
header_data: header,
footer_data: this.propValue.footer,
diy_data: new_diy_data,
tabs_data: new_tabs_data,
page_style: common_styles_computer(header_style.common_style),
page_img_style: background_computer(header_style.common_style),
// 内间距
content_padding: `padding: 0px ${padding_right}px 0px ${padding_left}px;` + 'box-sizing:border-box;',
outer_container_padding: padding_right + padding_left,
// 判断顶部导航是否置顶
is_header_top: parseInt(header_style.up_slide_display) == 1 ? true : false,
is_tabs_data_topped: new_tabs_data[0]?.com_data?.content?.tabs_top_up == '1' || false,
temp_sticky_top: this.sticky_top,
temp_header_top: (new_is_search_alone_row ? 66 + new_data_alone_row_space : 33),
header_top: (new_is_search_alone_row ? 66 + new_data_alone_row_space : 33),
is_immersion_model: header_style.header_background_type !== 'color_image' && header_style.immersive_style == '1',
// 顶部导航高度是否变化--------------------------------------------------
is_search_alone_row: new_is_search_alone_row,
data_alone_row_space: new_data_alone_row_space,
is_immersive_style_and_general_safe_distance_value: header_style.immersive_style == '1' && header_style.general_safe_distance_value == '1',
is_the_safe_distance_enabled: header_style.immersive_style == '1' && header_style.general_safe_distance_value == '1',// diy_data是否开启安全距离
});
// 缓存数据
uni.setStorageSync(this.cache_key + this.tabs_home_id, diy_data);
},
// 顶部导航沉浸模式回调
// immersion_model_call_back(bool) {
// this.setData({
// is_immersion_model: bool,
// });
// },
get_index_content(new_diy_index, header, header_style, item) {
item.index = new_diy_index;
if (new_diy_index == 0) {
// 判断是否开启沉浸模式和是否开启安全距离 如果为true则除了选项卡和选项卡轮播外 第一个组件则加上安全距离样式的padding_top加上顶部导航的高度和安全距离的高度
if (header_style.immersive_style == '1' && header_style.general_safe_distance_value == '1') {
let new_data = JSON.parse(JSON.stringify(item));
// 顶部导航的高度
let header_top_height = (header.com_data.content.data_alone_row_value.length > 0 ? parseInt(header.com_data.style.data_alone_row_space || 5) : 0) + 33 + (header.com_data.content.data_alone_row_value.length > 0 ? 33 : 0);
new_data.com_data.style.common_style.padding_top = parseInt(new_data.com_data.style.common_style.padding_top) + header_top_height;
return new_data;
}
return item;
}
return item;
},
// 选项卡回调更新数据
tabs_click_event(tabs_id, bool, params = {}) {
let new_data = [];
this.setData({
is_tabs_type: bool,
tabs_id: tabs_id,
});
let new_params = {
...params,
id: tabs_id,
};
if (tabs_id) {
new_data = uni.getStorageSync(this.cache_key + tabs_id) || [];
if (new_data.length > 0) {
// 先使用缓存数据展示
this.setData({
diy_data: new_data,
});
// 已有本地缓存则直接取远程有效数据(默认首次取的是远程缓存数据)
new_params['is_cache'] = 0;
}
// diy数据
if (bool) {
uni.showLoading({
title: this.$t('common.loading_in_text'),
mask: true,
});
uni.request({
url: app.globalData.get_request_url('index', 'diy'),
method: 'POST',
data: new_params,
dataType: 'json',
success: (res) => {
uni.hideLoading();
// 数据处理
let data = res.data.data.data;
if (res.data.code == 0) {
new_data = data?.config.diy_data || [];
uni.setStorageSync(this.cache_key + tabs_id, new_data);
this.setData({
diy_data: new_data,
});
// 是否需要重新加载数据
if (parseInt(data.is_result_data_cache || 0) == 1) {
this.tabs_click_event(tabs_id, bool, { is_cache: 0 });
}
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
uni.hideLoading();
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
} else {
this.setData({
goods_page: 1,
goods_list: [],
goods_list_loding_status: 1,
goods_bottom_line_status: false,
});
this.get_goods_list(1);
}
} else {
if (tabs_id == '') {
new_data = uni.getStorageSync(this.cache_key + this.tabs_home_id) || [];
}
// 先使用缓存数据展示
this.setData({
diy_data: new_data,
});
}
},
// 选项卡高度
tabs_height_event(height) {
let new_tabs_height = 0;
// 判断是否有选项卡切选项卡数组数据内的字段is_enable值是否为1
if (this.tabs_data.length > 0) {
this.tabs_data.forEach((item, index) => {
if (item.is_enable == '1') {
new_tabs_height = height;
}
});
}
this.setData({
tabs_height: new_tabs_height,
});
},
// 滚动加载
on_scroll_lower_event(e) {
if (!this.is_tabs_type) {
this.get_goods_list();
}
},
// 滚动到顶部
on_scroll_upper_event() {
setTimeout(() => {
this.head_scroll_top = 0;
});
},
// 查询商品
get_goods_list(is_mandatory) {
// 分页是否还有数据
if ((is_mandatory || 0) == 0) {
if (this.goods_bottom_line_status == true) {
uni.stopPullDownRefresh();
return false;
}
}
// 是否加载中
if (this.data_is_loading == 1) {
return false;
}
this.setData({
data_is_loading: 1,
});
// 获取数据
if (this.goods_page > 1) {
uni.showLoading({
title: this.$t('common.loading_in_text'),
});
}
let new_data = {
category_id: this.tabs_id,
page: this.goods_page,
};
// 九宫格数据
uni.request({
url: app.globalData.get_request_url('datalist', 'search'),
method: 'POST',
data: new_data,
dataType: 'json',
success: (res) => {
if (this.goods_page > 1) {
uni.hideLoading();
}
uni.stopPullDownRefresh();
if (res.data.code == 0) {
var data = res.data.data;
if (data.data.length > 0) {
if (this.goods_page <= 1) {
var temp_goods_list = data.data;
} else {
var temp_goods_list = this.goods_list || [];
var temp_data = data.data;
for (var i in temp_data) {
temp_goods_list.push(temp_data[i]);
}
}
this.setData({
goods_list: temp_goods_list,
random_value: Math.random(),
goods_total: data.total,
goods_page_total: data.page_total,
goods_list_loding_status: 3,
goods_list_loding_msg: '',
goods_page: this.goods_page + 1,
data_is_loading: 0,
});
// 是否还有数据
this.setData({
goods_bottom_line_status: this.goods_page > 1 && this.goods_page > this.goods_page_total,
});
} else {
this.setData({
goods_list_loding_status: 0,
goods_list_loding_msg: res.data.msg,
goods_total: 0,
data_is_loading: 0,
});
if (this.goods_page <= 1) {
this.setData({
goods_list: [],
goods_bottom_line_status: false,
});
}
}
} else {
this.setData({
goods_list_loding_status: 0,
goods_list_loding_msg: res.data.msg,
data_is_loading: 0,
});
app.globalData.is_login_check(res.data, this, 'get_goods_list', is_mandatory);
}
},
fail: () => {
if (this.goods_page > 1) {
uni.hideLoading();
}
uni.stopPullDownRefresh();
this.setData({
goods_list_loding_status: 2,
goods_list_loding_msg: this.$t('common.internet_error_tips'),
data_is_loading: 0,
});
},
});
},
// 页面滚动事件
on_scroll_event(e) {
const scroll_num = parseInt(e.detail.scrollTop);
if (scroll_num <= 20) {
this.head_scroll_top = 0;
} else {
if (scroll_num / (this.sticky_top + 33) <= 1) {
// 更新数据的逻辑
this.head_scroll_top = scroll_num;
} else {
this.head_scroll_top = this.sticky_top + 100;
}
}
// #ifdef H5 || MP-TOUTIAO
// 判断顶部导航是否置顶
if (!this.is_header_top && !this.is_immersion_model) {
if (scroll_num >= this.sticky_top + 33 + (this.is_search_alone_row ? 0 : 33 + this.data_alone_row_space)) {
this.temp_sticky_top = 0;
this.temp_header_top = 0;
this.temp_sticky_no_h5_top = 0;
this.temp_is_header_top = true;
} else {
this.temp_header_top = this.header_top;
this.temp_sticky_top = this.sticky_top;
this.temp_sticky_no_h5_top = this.sticky_top;
this.temp_is_header_top = false;
}
}
//#endif
this.scroll_timer_compute(scroll_num);
},
scroll_timer_compute(scroll_num) {
// 使用节流技术减少事件触发的处理次数
if (!this.scroll_throttle_timeout) {
const self = this;
this.scroll_throttle_timeout = setTimeout(() => {
this.scroll_top = scroll_num;
// 清除定时器
this.scroll_throttle_timeout = null;
}, 20); // 可以根据实际情况调整延时时间
}
},
// 底部菜单高度
footer_height_value_event(value) {
this.setData({
footer_height_value: (value*2)+20,
});
// 存储diy页面底部菜单高度
if(app.globalData.current_page(false) == 'pages/diy/diy') {
app.globalData.app_diy_tabbar_height_save(value);
}
},
// 商品数量更新回调
goods_buy_event(index, goods = {}, params = {}, back_data = null) {
if ((this.$refs.goods_buy || null) != null) {
this.goods_index = index;
this.$refs.goods_buy.init(goods, params, back_data);
}
},
// 商品加购回调
goods_cart_back_event(e) {
if ((this.$refs[`diy_goods_buy${this.goods_index}`][0] || null) != null) {
this.$refs[`diy_goods_buy${this.goods_index}`][0].goods_cart_back_event(e);
}
},
// 视频播放
video_play(url, width, height) {
this.setData({
video_src: url,
popup_width: width,
popup_height: height,
});
this.$refs.popup.open();
const videoContext = uni.createVideoContext('carousel_video');
if (!isEmpty(videoContext)) {
videoContext.play();
}
},
// 视频关闭
video_close() {
const videoContext = uni.createVideoContext('carousel_video');
if (!isEmpty(videoContext)) {
videoContext.pause();
}
this.$refs.popup.close();
},
// 位置回调
choice_location_back(e) {
// 如果存在tabs_id则表示当前有选择tab数据则仅当前模块更新无需给上级回调位置
if ((this.tabs_id || null) == null) {
this.$emit('onLocationBack', e);
} else {
this.tabs_click_event(this.tabs_id, this.is_tabs_type);
}
},
},
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,164 @@
<template>
<movable-area class="float-window-movable-container">
<movable-view :x="x" :y="y" direction="all" class="float-window-spread flex-row align-c jc-c" @tap="btn_event">
<block v-if="style.float_style == 'diffuse'">
<view class="ring" :style="content_style"></view>
<view class="ring" :style="content_style"></view>
</block>
<view class="img oh" :style="content_style">
<block v-if="(form || null) != null && form.button_jump == 'customer_service'">
<component-online-service :propChatImage="img_url" :propIsSpread="false" :propIsMovable="false"></component-online-service>
</block>
<block v-else>
<imageEmpty :propImageSrc="img_url" propImgFit="aspectFill" propErrorStyle="width: 60rpx;height: 60rpx;"></imageEmpty>
</block>
</view>
</movable-view>
<component-quick-nav ref="quick_nav" :propIsBtn="false"></component-quick-nav>
</movable-area>
</template>
<script>
const app = getApp();
import { isEmpty } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import componentOnlineService from '@/components/online-service/online-service';
import componentQuickNav from '@/components/quick-nav/quick-nav';
export default {
components: {
imageEmpty,
componentOnlineService,
componentQuickNav
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
}
},
data() {
return {
form: {},
style: {},
img_url: '',
x: 0,
y: 0,
content_style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
// 获取内容
let form = this.propValue.content || {};
// 获取图片
let img_url = (form.button_img || null) != null ? (form.button_img[0] || null) : null;
if (img_url != null) {
img_url = img_url.url || null;
}
const { float_style, float_style_color, display_location, offset_number_percentage } = this.propValue.style;
// 获取当前手机的宽度和高度
const { windowWidth, windowHeight } = uni.getSystemInfoSync();
// 计算出距离左边的距离
let x = display_location == 'left' ? 10 : windowWidth - 60;
// 计算出距离顶部的距离
const y = Math.ceil(windowHeight * (1 - Number(offset_number_percentage)) - 20);
this.setData({
form: form,
style: this.propValue.style,
img_url: img_url,
content_style: float_style == 'shadow' ? `box-shadow: 0 0 40rpx ${float_style_color};border-radius: 50%;` : `background-color: ${float_style_color};border-radius: 50%;`,
x: x,
y: y
});
},
// 按钮事件
btn_event() {
const { button_jump, button_link } = this.form;
switch(button_jump) {
// 链接
case 'link' :
if (!isEmpty(button_link)) {
app.globalData.url_open(button_link.page);
}
break;
// 快捷导航
case 'quick_nav' :
if ((this.$refs.quick_nav || null) != null) {
this.$refs.quick_nav.quick_open_event();
}
break;
}
},
},
};
</script>
<style scoped lang="scss">
.img {
width: 90rpx;
height: 90rpx;
border-radius: 50%;
z-index: 2;
}
.float-window-movable-container {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: transparent;
pointer-events: none;
z-index: 103;
}
/**
* 呼吸灯
*/
.float-window-spread {
position: relative;
pointer-events: auto;
z-index: 1;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
.float-window-spread .ring {
/* 速度为1.5 * 层数 = 实际运行速度,速度修改则 animation-delay 属性也修改相同速度 */
animation: pulsing 1.5s ease-out infinite;
border-radius: 100%;
width: 100rpx;
height: 100rpx;
position: absolute;
}
/* 速度为1*层数 */
.float-window-spread .ring:nth-of-type(1) {
-webkit-animation-delay: -1.5s;
animation-delay: -1.5s;
}
/* 速度为1*层数 */
.float-window-spread .ring:nth-of-type(2) {
-webkit-animation-delay: -2s;
animation-delay: -2s;
}
@keyframes pulsing {
100% {
transform: scale(1.35);
opacity: 0;
}
}
</style>

201
components/diy/footer.vue Normal file
View File

@@ -0,0 +1,201 @@
<template>
<!-- 底部导航 -->
<view v-if="(propValue || null) !== null" class="footer-nav flex-row jc-c align-c" :class="nav_type == 1 ? 'bottom-line-exclude' : ''">
<view class="flex-1 wh-auto" :style="style_container">
<view class="footer-nav-content flex-row jc-c align-c wh-auto" :style="style_img_container">
<view class="flex-row jc-c align-c wh-auto" :class="nav_type == 0 ? 'bottom-line-exclude' : ''">
<view class="flex-row jc-sa align-c wh padding-0">
<block v-for="(item, index) in nav_content" :key="index">
<view class="flex-1 flex-col jc-c align-c gap-5 pr" :data-value="item.link.page || ''" @tap="url_event">
<view v-if="nav_style != 2" class="img-content pr">
<view class="img-item pa border-radius-xs animate-linear" :class="active_index != index ? 'active' : ''">
<template v-if="item.img.length > 0">
<image :src="item.img[0].url" class="img dis-block" model="widthFix"></image>
</template>
</view>
<view class="img-item pa border-radius-xs animate-linear" :class="active_index == index ? 'active' : ''">
<template v-if="item.img_checked.length > 0">
<image :src="item.img_checked[0].url" class="img dis-block" model="widthFix"></image>
</template>
</view>
</view>
<text v-if="nav_style != 1" class="animate-linear text-size-xs pr z-i" :style="active_index == index ? text_color_checked : default_text_color">{{ item.name }}</text>
<view v-if="(item.badge || null) != null" class="pa badge-icon">
<component-badge :propNumber="item.badge"></component-badge>
</view>
</view>
</block>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
let app = getApp();
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
import componentBadge from '@/components/badge/badge';
export default {
props: {
propKey: {
type: [String,Number],
default: '',
},
propValue: {
type: Object,
default: null,
},
// 底部选中索引
propFooterActiveIndex: {
type: Number,
default: 0,
}
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
nav_content: [],
nav_type: 0,
nav_style: 0,
default_text_color: '',
text_color_checked: '',
active_index: 0,
};
},
components: {
componentBadge,
},
// 属性值改变监听
watch: {
// 唯一key
propKey(value, old_value) {
this.init();
},
// 选中索引
propFooterActiveIndex(value, old_value) {
this.init();
},
},
// 页面被展示
created: function () {
this.init();
},
methods: {
// 初始化
init() {
if ((this.propValue || null) !== null) {
let new_content = this.propValue.content || {};
let new_style = this.propValue.style || {};
let nav_content = new_content.nav_content || [];
let page = app.globalData.current_page() || null;
let active_index = this.propFooterActiveIndex;
if (page != null) {
// 角标链接定义
let badge_arr = {
'/pages/cart/cart': 'cart',
'/pages/cart-page/cart-page': 'cart',
};
for (var i in nav_content) {
if ((nav_content[i]['link'] || null) != null && (nav_content[i]['link']['page'] || null) != null) {
// 选中索引
if (nav_content[i]['link']['page'] == '/' + page) {
active_index = i;
}
// 获取角标数据
var badge_key = badge_arr[nav_content[i]['link']['page']];
if (badge_key !== undefined) {
nav_content[i]['badge'] = app.globalData.get_tab_bar_badge(badge_key);
}
}
}
}
this.setData({
nav_content: nav_content,
nav_type: new_content.nav_type || 0,
nav_style: new_content.nav_style || 0,
active_index: active_index,
default_text_color: 'color:' + new_style.default_text_color || 'rgba(0, 0, 0, 1)',
text_color_checked: 'color:' + new_style.text_color_checked || 'rgba(204, 204, 204, 1)',
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style),
});
// 高度计算
let nav_min_height = 70;
var nav_height = parseInt(new_style.common_style.padding_top) + parseInt(new_style.common_style.padding_bottom) + 44;
if (nav_height < nav_min_height) {
nav_height = nav_min_height;
}
let footer_height = nav_height + parseInt(new_style.common_style.margin_top) + parseInt(new_style.common_style.margin_bottom);
// #ifndef H5
// 底部菜单距离底部的安全距离减去20、默认的安全距离太高了
var safe_areaInsets = uni.getSystemInfoSync().safeAreaInsets || {};
var bottom = parseInt(safe_areaInsets.bottom || 0);
if (bottom > 0) {
bottom -= 24;
}
footer_height += bottom;
// #endif
// 回调高度
this.$emit('onFooterHeight', footer_height);
}
},
// 跳转链接
url_event(e) {
let index = e.currentTarget.dataset.index;
let item = this.nav_content[index];
app.globalData.url_event(e);
this.$emit('onFooterTap', index, item);
},
},
};
</script>
<style lang="scss" scoped>
.footer-nav {
z-index: 102;
position: fixed;
bottom: 0;
left: 0;
right: 0;
width: 100%;
margin: 0 auto;
background-color: transparent;
.footer-nav-content {
min-height: 140rpx;
box-sizing: border-box;
.img-content {
width: 44rpx;
height: 44rpx;
.img-item {
width: 44rpx;
height: 44rpx;
opacity: 0;
&.active {
opacity: 1;
}
.img {
width: 44rpx;
height: 44rpx;
}
}
}
.badge-icon {
margin-top: -70rpx;
margin-right: -70rpx;
}
}
}
/* #ifdef H5 */
@media only screen and (min-width: 1600rpx) {
.footer-nav {
max-width: 1600rpx;
}
}
/* #endif */
</style>

View File

@@ -0,0 +1,607 @@
<template>
<view v-if="!isEmpty(list)" class="oh" :style="style_container" @tap.stop="onTap">
<view class="oh" :style="style_img_container">
<view :class="outer_class" :style="onter_style">
<block v-if="!['5'].includes(theme)">
<view v-for="(item, index) in list" :key="index" class="pr oh" :style="layout_style" :data-index="index" :data-value="item.goods_url" @tap.stop="url_event">
<view :class="layout_type" :style="layout_img_style">
<block v-if="theme == '6'">
<view :class="['flex-row align-c jc-sb ptb-15 mlr-10 gap-20', { 'br-b-e': index != list.length - 1 }]">
<view v-if="is_show('title')" :class="text_line" :style="title_style">{{ item.title }}</view>
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num nowrap" :style="'color:' + new_style.shop_price_color">
<text class="identifying">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" class="identifying">{{ item.show_price_unit }}</text>
</view>
</view>
</block>
<block v-else>
<block v-if="!isEmpty(item)">
<view class="oh pr" :class="img_size">
<view v-if="!isEmpty(item.new_cover)" :style="img_size">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="content_img_radius" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<view v-else :style="img_size">
<imageEmpty :propImageSrc="item.images" :propStyle="content_img_radius" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</block>
<view v-if="is_show('title') || is_show('simple_desc') || is_show('price') || is_show('original_price') || is_show('sales_count') || is_show('plugins_view_icon') || form.is_shop_show == '1'" class="flex-col flex-1 jc-sb content gap-10" :style="content_style">
<view class="flex-col gap-10 top-title">
<view v-if="is_show('title') || (['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc'))" class="flex-col" :style="{ gap: new_style.title_simple_desc_spacing * 2 + 'rpx' }">
<view v-if="is_show('title')" :class="text_line" :style="title_style">{{ item.title }}</view>
<view v-if="['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc')" :class="form.simple_desc_row == '2' ? 'text-line-2' : 'text-line-1'" :style="simple_desc">{{ item.simple_desc }}</view>
</view>
<view v-if="show_content && is_show('plugins_view_icon') && !isEmpty(item.plugins_view_icon_data)" class="flex-row gap-5 align-c">
<view v-for="(icon_data, icon_index) in item.plugins_view_icon_data" :key="icon_index" class="radius text-size-xsss padding-horizontal-xs" :style="{ background: icon_data.bg_color, color: icon_data.color, border: '1rpx solid' + (!isEmpty(icon_data.br_color) ? icon_data.br_color : icon_data.bg_color) }">{{ icon_data.name }}</view>
</view>
</view>
<view v-if="!['3', '4', '5'].includes(form.theme)" class="flex-col gap-5">
<view :class="[form.is_price_solo == '1' ? 'flex-row align-c nowrap' : 'flex-col gap-5']">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="'color:' + new_style.shop_price_color">
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="show_content && is_show('original_price') && !isEmpty(item.min_original_price)" class="text-size-xss flex-row">
<!-- <image v-if="form.static_img.length > 0" class="original-price-left" :src="form.static_img[0].url" model="widthFix"></image> -->
<text :class="['original-price text-line-1', { 'flex-1': form.is_price_solo == '1' }]" :style="original_price">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<block v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</block>
</text>
</view>
</view>
<view class="flex-row jc-sb align-e">
<view>
<view v-if="show_content" class="flex-row align-c text-size-xss">
<view v-if="is_show('sales_count') && !isEmpty(item.sales_count)" class="pr-5" :style="sold_number_style">已售{{ item.sales_count || 0 }}</view>
<!-- <view v-if="is_show('sales_count')" :class="['pr-5', {'br-r-e': is_show('sales_count') && is_show('4')}]" :style="sold_number_style>已售{{ item.sales_count }}件</view> -->
<!-- <view v-if="is_show('4')" class="pl-5" :style="score_style">评分0</view> -->
</view>
</view>
<view v-if="(form.is_shop_show == '1' && form.shop_button_effect == '1' && item.is_error == 0) || (form.is_shop_show == '1' && form.shop_button_effect == '0')" class="pr" :data-index="index" @tap.stop="goods_button_event">
<block v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + ('color:' + new_style.shop_button_text_color)">{{ form.shop_button_text }}</view>
</block>
<view v-else class="round padding-horizontal-sm ptb-5" :style="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
<view v-if="form.shop_button_effect == '1'" class="cart-badge-icon pa badge-style">
<component-badge :propNumber="item.user_cart_count || 0"></component-badge>
</view>
</view>
</view>
</view>
<view v-else class="flex-row align-c jc-sb">
<view class="flex-row align-c nowrap">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="'color:' + new_style.shop_price_color">
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="show_content && is_show('original_price') && !isEmpty(item.min_original_price)" class="text-size-xss flex-row">
<!-- <image v-if="form.static_img.length > 0" class="original-price-left" :src="form.static_img[0].url" model="widthFix"></image> -->
<text :class="['original-price text-line-1', { 'flex-1': form.is_price_solo == '1' }]" :style="original_price">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<block v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</block>
</text>
</view>
</view>
<view v-if="(form.is_shop_show == '1' && form.shop_button_effect == '1' && item.is_error == 0) || (form.is_shop_show == '1' && form.shop_button_effect == '0')" class="pr" :data-index="index" @tap.stop="goods_button_event">
<block v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + ('color:' + new_style.shop_button_text_color)">{{ form.shop_button_text }}</view>
</block>
<view v-else class="round padding-horizontal-sm ptb-5" :style="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
<view v-if="form.shop_button_effect == '1'" class="cart-badge-icon pa badge-style">
<component-badge :propNumber="item.user_cart_count || 0"></component-badge>
</view>
</view>
</view>
</view>
</block>
</view>
</view>
</block>
<block v-else>
<swiper circular="true" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :next-margin="new_style.rolling_fashion == 'translation' ? '-' + content_outer_spacing_magin : '0rpx'" :display-multiple-items="slides_per_group" :style="{ width: '100%', height: new_style.content_outer_height * new_scale + 'px' }">
<swiper-item v-for="(item1, index1) in shop_content_list" :key="index1">
<view class="flex-row wh-auto ht-auto" :style="onter_style">
<view v-for="(item, index) in item1.split_list" :key="index" class="pr oh" :style="layout_style" :data-index="index1" :data-split-index="index" :data-value="item.goods_url" @tap.stop="url_event">
<view :class="layout_type" :style="layout_img_style">
<block v-if="!isEmpty(item)">
<view class="oh pr" :class="'flex-img' + theme">
<view v-if="!isEmpty(item.new_cover)" :class="'flex-img' + theme">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="content_img_radius" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<view v-else :class="'flex-img' + theme">
<imageEmpty :propImageSrc="item.images" :propStyle="content_img_radius" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</block>
<view v-if="is_show('title') || is_show('simple_desc') || is_show('price') || is_show('plugins_view_icon') || is_show('original_price') || form.is_shop_show == '1'" class="flex-col flex-1 jc-sb content gap-10" :style="content_style">
<view class="flex-col gap-10 top-title">
<view v-if="is_show('title') || (['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc'))" class="flex-col" :style="{ gap: new_style.title_simple_desc_spacing * 2 + 'rpx' }">
<view v-if="is_show('title')" :class="text_line" :style="title_style">{{ item.title }}</view>
<view v-if="['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc')" :class="form.simple_desc_row == '2' ? 'text-line-2' : 'text-line-1'" :style="simple_desc">{{ item.simple_desc }}</view>
</view>
<view v-if="show_content && is_show('plugins_view_icon') && !isEmpty(item.plugins_view_icon_data)" class="flex-row gap-5 align-c">
<view v-for="(icon_data, icon_index) in item.plugins_view_icon_data" :key="icon_index" class="radius text-size-xsss padding-horizontal-xs" :style="{ background: icon_data.bg_color, color: icon_data.color, border: '1rpx solid' + (!isEmpty(icon_data.br_color) ? icon_data.br_color : icon_data.bg_color) }">{{ icon_data.name }}</view>
</view>
</view>
<view class="flex-row align-c jc-sb">
<view class="flex-row align-c nowrap">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="'color:' + new_style.shop_price_color">
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="show_content && is_show('original_price') && !isEmpty(item.min_original_price)" class="text-size-xss flex">
<!-- <image v-if="form.static_img.length > 0" class="original-price-left" :src="form.static_img[0].url" model="widthFix"></image> -->
<text :class="['original-price text-line-1', { 'flex-1': form.is_price_solo == '1' }]" :style="original_price">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<block v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</block>
</text>
</view>
</view>
<view v-if="(form.is_shop_show == '1' && form.shop_button_effect == '1' && item.is_error == 0) || (form.is_shop_show == '1' && form.shop_button_effect == '0')" class="pr" :data-index="index1" :data-split-index="index" @tap.stop="goods_button_event">
<block v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + ('color:' + new_style.shop_button_text_color)">{{ form.shop_button_text }}</view>
</block>
<view v-else class="round padding-horizontal-sm ptb-5" :style="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
<view v-if="form.shop_button_effect == '1'" class="cart-badge-icon pa badge-style">
<component-badge :propNumber="item.user_cart_count || 0"></component-badge>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</block>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { isEmpty, common_styles_computer, common_img_computer, gradient_handle, padding_computer, radius_computer, background_computer, border_computer, box_shadow_computer, old_margin, margin_computer } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import subscriptIndex from '@/components/diy/modules/subscript/index.vue';
import componentBadge from '@/components/badge/badge';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
imageEmpty,
componentBadge,
subscriptIndex
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propIsCommonStyle: {
type: Boolean,
default: true,
},
propKey: {
type: [String, Number],
default: '',
},
propDiyIndex: {
type: Number,
default: 0,
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 0,
},
},
data() {
return {
form: {},
new_style: {},
propIsCartParaCurve: false,
list: [],
content_radius: '', // 圆角设置
content_img_radius: '', // 图片圆角设置
content_padding: '', // 内边距设置
theme: '', // 选择的风格
content_outer_spacing: '', // 商品间距
content_outer_spacing_magin: '', // 商品间距
// 最外层不同风格下的显示
outer_class: '',
onter_style: '',
// 不同风格下的样式
layout_type: '',
layout_style: '',
layout_img_style: '',
content_style: '', // 内容区域的样式
show_content: false, // 显示除标题外的其他区域
text_line: '', // 超过多少行隐藏
style_container: '', // 公共样式
style_img_container: '',
shop_content_list: [],
slides_per_group: 1,
// 内容样式
title_style: '',
price_style: '',
sold_number_style: '',
score_style: '',
button_style: '',
simple_desc: '',
price_symbol: '',
price_unit: '',
original_price: '',
// 按钮背景色
button_gradient: '',
// 图片大小
img_size: '',
new_scale: 1,
};
},
watch: {
propKey(val) {
this.init();
},
propValue(new_value, old_value) {
this.init();
},
},
created() {
this.init();
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content || null;
const new_style = this.propValue.style || null;
if (new_form != null && new_style != null) {
let new_list = [];
// 指定商品并且指定商品数组不为空
if (!isEmpty(new_form.data_list) && new_form.data_type == '0') {
new_list = new_form.data_list.map((item) => ({
...item.data,
title: !isEmpty(item.new_title) ? item.new_title : item.data.title,
new_cover: item.new_cover,
}));
} else if (!isEmpty(new_form.data_auto_list) && new_form.data_type == '1') {
// 筛选商品并且筛选商品数组不为空
new_list = new_form.data_auto_list;
}
// 最外层不同风格下的显示
const flex = ['0', '2', '6'].includes(new_form.theme) ? 'flex-col ' : 'flex-row ';
const wrap = new_form.theme == '5' ? '' : 'flex-wrap ';
const background = ['6'].includes(new_form.theme) ? 'bg-white ' : '';
const button_gradient = gradient_handle(new_style.shop_button_color, '180deg');
// 默认数据
const product_style_list = [
{ name: '单列展示', value: '0', width: 110, height: 120 },
{ name: '大图展示', value: '2', width: 166, height: 166 },
{ name: '无图模式', value: '6', width: 0, height: 0 },
{ name: '两列展示(纵向)', value: '1', width: 180, height: 180 },
{ name: '两列展示(横向)', value: '4', width: 70, height: 70 },
{ name: '三列展示', value: '3', width: 116, height: 114 },
{ name: '左右滑动展示', value: '5', width: 0, height: 0 },
];
const scale = sys_width / 390;
let img_style = ``;
if (['0', '4'].includes(new_form.theme)) {
// 图片宽度
if (typeof new_style.content_img_width == 'number') {
img_style += `width: ${ new_style.content_img_width * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_form.theme);
if (list.length > 0) {
img_style += `width: ${ list[0].width * scale }px;`;
} else {
img_style += 'width: auto;';
}
}
}
if (!['5', '6'].includes(new_form.theme)) {
// 图片宽度
if (typeof new_style.content_img_height == 'number') {
img_style += `height: ${ new_style.content_img_height * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_form.theme);
if (list.length > 0) {
img_style += `height: ${ list[0].height * scale }px;`;
} else {
img_style += 'height: auto;';
}
}
}
this.setData({
form: new_form,
new_style: new_style,
outer_class: flex + wrap + background + 'oh',
list: new_list,
new_scale: scale,
content_radius: radius_computer(new_style.shop_radius), // 圆角设置
content_img_radius: radius_computer(new_style.shop_img_radius), // 图片圆角设置
content_padding: padding_computer(new_style.shop_padding) + 'box-sizing: border-box;', // 内边距设置
theme: new_form.theme, // 选择的风格
content_outer_spacing: new_style.content_outer_spacing, // 商品间距
content_outer_spacing_magin: new_style.content_outer_spacing * 2 + 'rpx',
onter_style: new_form.theme == '6' ? radius_computer(new_style.shop_radius) : `gap: ${new_style.content_outer_spacing * 2 + 'rpx'};`,
// 不同风格下的样式
layout_type: ['0', '4'].includes(new_form.theme) ? 'flex-row wh-auto ht-auto oh' : 'flex-col wh-auto ht-auto oh',
layout_style: this.get_layout_style(new_style, new_form),
layout_img_style: this.get_layout_img_style(new_style, new_form),
content_style: this.get_content_style(new_style, new_form), // 内容区域的样式
show_content: ['0', '1', '2'].includes(new_form.theme), // 显示除标题外的其他区域
text_line: this.get_text_line(new_form), // 超过多少行隐藏
style_container: this.propIsCommonStyle ? common_styles_computer(new_style.common_style) : '', // 公共样式
style_img_container: this.propIsCommonStyle ? common_img_computer(new_style.common_style, this.propIndex) : '', // 图片样式
// 内容样式设置
button_gradient: button_gradient,
title_style: this.trends_config(new_style, 'title', 'title', new_form),
price_style: this.trends_config(new_style, 'price'),
sold_number_style: this.trends_config(new_style, 'sold_number'),
score_style: this.trends_config(new_style, 'score'),
button_style: this.trends_config(new_style, 'button', 'buy_button') + button_gradient,
simple_desc: this.trends_config(new_style, 'simple_desc', 'desc', new_form),
price_symbol: !isEmpty(new_style.shop_price_symbol_color) ? this.trends_config(new_style, 'price_symbol') : 'font-size: 18rpx;color: #EA3323;' ,
price_unit: !isEmpty(new_style.shop_price_unit_color) ? this.trends_config(new_style, 'price_unit') : 'font-size: 18rpx;color: #EA3323;',
original_price: this.trends_config(new_style, 'original_price'),
shop_content_list: this.get_shop_content_list(new_list, new_form, new_style),
slides_per_group: new_style.rolling_fashion == 'translation' ? new_form.carousel_col : 1,
img_size: img_style,
});
}
},
get_shop_content_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.carousel_col;
// 存储数据显示
let nav_list = [];
// 拆分的数量
const split_num = Math.ceil(cloneList.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({
split_list: cloneList.slice(i * num, (i + 1) * num),
});
}
return nav_list;
} else {
// 否则的话,就返回全部的信息
return [
{
split_list: cloneList,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
get_text_line(form) {
let line = '';
if (['1', '6'].includes(form.theme)) {
line = 'text-line-1';
} else if (['0', '2', '3', '4', '5'].includes(form.theme)) {
line = 'text-line-2';
}
return line;
},
// 不同风格下的样式
get_layout_style(new_style, form) {
const { shop_margin = old_margin } = new_style;
const radius = form.theme == '6' ? '' : radius_computer(new_style.shop_radius);
const style = form.theme != '6' ? gradient_handle(new_style?.shop_color_list || [], new_style?.shop_direction || '') + margin_computer(shop_margin) + border_computer(new_style) + box_shadow_computer(new_style) : '';
let size_style = ``;
const shop_left_right_width = shop_margin.margin_left + shop_margin.margin_right;
if (['1', '4'].includes(form.theme)) {
size_style = `width: calc((100% - ${new_style.content_outer_spacing * 2 + (shop_left_right_width * 4) + 'rpx'}) / 2);`;
} else if (form.theme == '3') {
size_style = `width: calc((100% - ${new_style.content_outer_spacing * 4 + (shop_left_right_width * 6) + 'rpx'}) / 3);`;
} else if (form.theme == '5') {
// 如果不是平移的时候执行
if (new_style.rolling_fashion != 'translation') {
size_style = `width: ${this.get_multicolumn_columns_width(new_style, form)};min-width: ${this.get_multicolumn_columns_width(new_style, form)};height: ${new_style.content_outer_height * (sys_width / 390) + 'px'};`;
} else {
size_style = `margin-right: ${ (new_style.content_outer_spacing * 2) + (shop_margin.margin_right * 2) }rpx;width: 100%;height: 100%;`;
}
} else if (form.theme == '0') {
size_style = `width: calc(100% - ${ shop_left_right_width }px);`;
}
return `${radius} ${ style } ${size_style}`;
},
get_layout_img_style(new_style, form) {
const padding = ['0', '4'].includes(form.theme) ? padding_computer(new_style.shop_padding) + 'box-sizing: border-box;' : '';
const data = {
background_img_style: new_style?.shop_background_img_style || '',
background_img: new_style?.shop_background_img || '',
}
const background = form.theme != '6' ? background_computer(data) : '';
return `${padding} ${background}`;
},
get_multicolumn_columns_width(new_style, form) {
const { carousel_col } = form;
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.content_outer_spacing * (carousel_col - 1)) / carousel_col;
return `calc(${100 / carousel_col}% - ${gap * 2}rpx)`;
},
get_content_style(new_style, form) {
const spacing_value = new_style.content_spacing;
let spacing = '';
if (['0', '4'].includes(form.theme)) {
spacing = `margin-left: ${spacing_value * 2}rpx;`;
} else {
spacing = padding_computer(new_style.shop_padding) + 'box-sizing: border-box;';
}
return `${spacing}`;
},
// 判断是否显示对应的内容
is_show(index) {
return this.form.is_show.includes(index);
},
// 根据传递的参数,从对象中取值
trends_config(new_style, key, type, new_form) {
return this.style_config(new_style[`shop_${key}_typeface`], new_style[`shop_${key}_size`], new_style[`shop_${key}_color`], type, new_form);
},
// 根据传递的值,显示不同的内容
style_config(typeface, size, color, type, new_form) {
let style = `font-weight:${typeface}; font-size: ${size * 2}rpx;`;
if (type == 'title') {
if (['1', '6'].includes(new_form.theme)) {
style += `line-height: ${size * 2}rpx;height: ${size * 2}rpx;color: ${color};`;
} else if (['0', '2', '3', '4', '5'].includes(new_form.theme)) {
style += `line-height: ${size > 0 ? (size + 3) * 2 : 0}rpx;height: ${size > 0 ? (size + 3) * 4 : 0}rpx;color: ${color};`;
}
} else if (type == 'desc') {
if (new_form.simple_desc_row == '2') {
style += `line-height: ${size > 0 ? (size + 3) * 2 : 0}rpx;height: ${size > 0 ? (size + 3) * 4 : 0}rpx;color: ${color};`;
} else {
style += `line-height: ${size * 2}rpx;height: ${size * 2}rpx;color: ${color};`;
}
} else {
if (type != 'buy_button') {
style += `color: ${color};`;
}
}
return style;
},
// icon标志显示样式
icon_style(item) {
let style = `background: ${item.bg_color};color: ${item.color};`;
if (!isEmpty(item.br_color)) {
style += `border: 2rpx solid ${item.br_color};`;
} else {
style += `border: 2rpx solid ${item.bg_color};`;
}
return style;
},
url_event(e) {
let index = e.currentTarget.dataset.index || 0;
let goods = this.list[index];
let split_index = 0;
if (this.theme == '5') {
split_index = e.currentTarget.dataset.splitIndex || 0;
goods = this.shop_content_list[index].split_list[split_index];
}
app.globalData.goods_data_cache_handle(goods.id, goods);
app.globalData.url_event(e);
},
goods_button_event(e) {
this.goods_cart_event(e);
},
// 加入购物车
goods_cart_event(e) {
let index = e.currentTarget.dataset.index || 0;
let split_index = 0;
let goods = this.list[index];
if (this.theme == '5') {
split_index = e.currentTarget.dataset.splitIndex || 0;
goods = this.shop_content_list[index].split_list[split_index];
}
if (this.form.shop_button_effect == '0') {
app.globalData.goods_data_cache_handle(goods.id, goods);
app.globalData.url_open(goods.goods_url);
} else {
// 开启购物车抛物线效果则展示提示操作
let is_success_tips = this.propIsCartParaCurve ? 0 : 1;
this.$emit(
'goods_buy_event',
this.propDiyIndex,
goods,
{
buy_event_type: 'cart',
is_direct_cart: 1,
is_success_tips: is_success_tips,
},
{
index: index,
split_index: split_index,
pos: e,
}
);
}
},
// 加入购物车成功回调
goods_cart_back_event(e) {
app.globalData.showToast('加入成功', 'success');
// 增加数量
var { index, split_index } = e.back_data;
let new_data = this.list;
let goods = new_data[index];
if (this.theme == '5') {
new_data = this.shop_content_list;
goods = new_data[index][split_index];
}
goods['user_cart_count'] = parseInt(goods['user_cart_count'] || 0) + parseInt(e.stock);
if (goods['user_cart_count'] > 99) {
goods['user_cart_count'] = '99+';
}
if (this.theme != '5') {
new_data[index] = goods;
this.setData({
list: new_data,
});
} else {
new_data[index][split_index] = goods;
this.setData({
shop_content_list: new_data,
});
}
},
},
};
</script>
<style scoped lang="scss">
.identifying {
font-size: 18rpx;
}
.original-price {
padding: 0 20rpx;
}
.flex-img5 {
width: 100%;
height: 100%;
}
// .original-price-left {
// width: 20rpx;
// height: 28rpx;
// }
.br-b-e {
border-bottom: 2rpx solid #eee;
}
.badge-style {
top: -20rpx;
right: 10rpx;
}
</style>

View File

@@ -0,0 +1,253 @@
<template>
<view class="goods-tabs ou" :class="'goods-tabs-' + propKey" :style="style_container">
<view class="ou" :style="style_img_container">
<componentDiyModulesTabsView :propKey="propKey" :propValue="goods_tabs" :propIsTop="top_up == '1'" :propTop="sticky_top" :propStyle="tabs_style" :propsTabsContainer="tabs_container" :propsTabsImgContainer="tabs_img_container" :propCustomNavHeight="propIsTabsUseSafeDistance ? (propCustomNavHeight * 2 + 'rpx') : '0rpx'" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" :propTabsBackground="tabs_background" @onTabsTap="tabs_click_event"></componentDiyModulesTabsView>
<view :style="shop_margin_top">
<view :style="shop_container">
<view :style="shop_img_container">
<componentGoodsList ref="diy_goods_list" :propKey="diy_key" :propDiyIndex="propDiyIndex" :propValue="goods_tabs" :propIsCommonStyle="false" @goods_buy_event="goods_buy_event"></componentGoodsList>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, margin_computer, background_computer, gradient_computer, isEmpty, old_border_and_box_shadow, old_margin, old_radius, old_padding, border_computer, box_shadow_computer, radius_computer } from '@/common/js/common/common.js';
import componentDiyModulesTabsView from '@/components/diy/modules/tabs-view';
import componentGoodsList from '@/components/diy/goods-list';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
components: {
componentDiyModulesTabsView,
componentGoodsList,
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propTop: {
type: Number,
default: 0,
},
propCustomNavHeight: {
type: Number,
default: 33,
},
propScrollTop: {
type: Number,
default: 0,
},
// 顶部导航是否开启沉浸模式
propIsImmersionModel: {
type: Boolean,
default: false,
},
propKey: {
type: [String, Number],
default: '',
},
propDiyIndex: {
type: Number,
default: 0,
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 0,
},
propIsTabsUseSafeDistance: {
type: Boolean,
default: true
}
},
data() {
return {
style_container: '',
style_img_container: '',
goods_tabs: {},
// 是否滑动置顶
top_up: '0',
tabs_top: 0,
tabs_background: 'background:transparent',
custom_nav_height: 33,
diy_key: '',
// 选项卡背景设置
tabs_container: '',
tabs_img_container: '',
tabs_sliding_fixed_bg: '',
// 商品区域背景设置
shop_margin_top: '',
shop_container: '',
shop_img_container: '',
// #ifdef MP
nav_safe_space: bar_height + 5,
// #endif
// #ifdef H5 || MP-TOUTIAO
nav_safe_space: bar_height + 7,
// #endif
// #ifdef APP
nav_safe_space: bar_height + 0,
// #endif
// 选项卡默认数据
tabs_index: 0,
sticky_top: 0,
};
},
watch: {
propScrollTop(newVal) {
if (newVal + this.sticky_top + this.custom_nav_height > this.tabs_top + this.nav_safe_space && this.top_up == '1') {
let new_style = this.propValue.style || {};
let tabs_bg = new_style.common_style.color_list;
let new_tabs_background = '';
if (tabs_bg.length > 0 && (tabs_bg[0].color || null) != null) {
new_tabs_background = gradient_computer(new_style.common_style);
}
let new_tabs_background_img = background_computer(new_style.common_style);
if (new_tabs_background_img.length > 0) {
new_tabs_background_img += 'background-position: top left;';
}
this.tabs_background = (new_tabs_background.length > 0 ? new_tabs_background : 'background:#fff;') + new_tabs_background_img;
} else {
this.tabs_background = 'background:transparent';
}
},
propTop(val) {
this.init();
},
propKey(val) {
// 初始化
this.setData({
diy_key: val,
});
this.init();
},
},
created() {
this.init();
},
mounted() {
this.$nextTick(() => {
const self = this;
setTimeout(() => {
self.getTop();
});
// #ifdef H5 || MP-TOUTIAO
// 获取自定义导航栏高度
this.setData({
custom_nav_height: this.propCustomNavHeight,
});
// #endif
});
},
methods: {
init() {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_content = new_data.content || {};
const new_style = new_data.style || {};
const new_tabs_data = new_data.content.tabs_list[this.tabs_index] || {};
// 产品的值
new_data.content.data_type = new_tabs_data.data_type;
new_data.content.category = new_tabs_data.category;
new_data.content.brand = new_tabs_data.brand;
new_data.content.number = new_tabs_data.number;
new_data.content.sort = new_tabs_data.sort;
new_data.content.sort_rules = new_tabs_data.sort_rules;
new_data.content.data_list = new_tabs_data.data_list;
new_data.content.data_auto_list = new_tabs_data.data_auto_list;
// 公共样式
const common_style = new_style.common_style;
let tabs_style_obj = {
padding_top: common_style.padding_top - this.propCustomNavHeight < 0 ? 0 : common_style.padding_top - this.propCustomNavHeight,
padding_left: common_style.padding_left,
padding_right: common_style.padding_right,
};
let new_tabs_style = padding_computer(tabs_style_obj) + `position:relative;left: -${tabs_style_obj.padding_left * 2}rpx;right: -${tabs_style_obj.padding_right * 2}rpx;width:100%;`;
// 如果是历史数据的话,就执行默认添加下边距
if (isEmpty(new_style.tabs_padding)) {
new_tabs_style += 'padding-bottom: 20rpx;';
}
const { tabs_bg_color_list = [], tabs_bg_direction = '', tabs_bg_background_img_style = '', tabs_bg_background_img = [], tabs_radius = old_radius, tabs_padding = old_padding, shop_content_color_list = [], shop_content_direction = '', shop_content_background_img_style = '', shop_content_background_img = [], shop_content_margin = old_margin, shop_content_padding = old_padding, shop_content_radius = old_radius } = new_style;
// 选项卡背景设置
const tabs_data = {
color_list: tabs_bg_color_list,
direction: tabs_bg_direction,
background_img_style: tabs_bg_background_img_style,
background_img: tabs_bg_background_img,
};
// 商品区域背景设置
const shop_content_data = {
color_list: shop_content_color_list,
direction: shop_content_direction,
background_img_style: shop_content_background_img_style,
background_img: shop_content_background_img,
};
const shop_content = new_style?.shop_content || old_border_and_box_shadow;
const tabs_content = new_style?.tabs_content || old_border_and_box_shadow;
this.setData({
top_up: new_content.tabs_top_up,
sticky_top: this.propTop + (new_style?.tabs_margin?.margin_top || 0),
goods_tabs: new_data,
style_container: common_styles_computer(common_style),
style_img_container: common_img_computer(common_style, this.propIndex),
tabs_style: new_tabs_style,
tabs_sliding_fixed_bg: gradient_computer(tabs_data),
tabs_container: gradient_computer(tabs_data) + radius_computer(tabs_radius) + margin_computer(new_style?.tabs_margin || old_margin) + border_computer(tabs_content) + box_shadow_computer(tabs_content) + 'overflow: hidden;',
tabs_img_container: background_computer(tabs_data) + padding_computer(tabs_padding) + 'box-sizing: border-box;overflow: hidden;',
shop_margin_top: 'margin-top:' + (new_style?.shop_content_spacing || 0) * 2 + 'rpx;',
shop_container: gradient_computer(shop_content_data) + margin_computer(shop_content_margin) + radius_computer(shop_content_radius) + box_shadow_computer(shop_content) + border_computer(shop_content) + 'overflow: hidden;',
shop_img_container: background_computer(shop_content_data) + padding_computer(shop_content_padding) + 'box-sizing: border-box;overflow: hidden;',
});
},
tabs_click_event(index) {
let new_data = JSON.parse(JSON.stringify(this.propValue));
new_data.content.data_type = new_data.content.tabs_list[index].data_type;
new_data.content.category = new_data.content.tabs_list[index].category;
new_data.content.brand = new_data.content.tabs_list[index].brand;
new_data.content.number = new_data.content.tabs_list[index].number;
new_data.content.sort = new_data.content.tabs_list[index].sort;
new_data.content.sort_rules = new_data.content.tabs_list[index].sort_rules;
new_data.content.data_list = new_data.content.tabs_list[index].data_list;
new_data.content.data_auto_list = new_data.content.tabs_list[index].data_auto_list;
this.setData({
tabs_index: index,
goods_tabs: new_data,
diy_key: Math.random(),
});
},
// 获取商品距离顶部的距离
getTop() {
const query = uni.createSelectorQuery().in(this);
query
.select('.goods-tabs-' + this.propKey)
.boundingClientRect((res) => {
if ((res || null) != null) {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_style = new_data.style || {};
this.setData({
tabs_top: res.top - (new_style.common_style?.margin_top || 0),
});
}
})
.exec();
},
goods_buy_event(index, goods = {}, params = {}, back_data = null) {
this.$emit('goods_buy_event', index, goods, params, back_data);
},
goods_cart_back_event(e) {
if ((this.$refs.diy_goods_list || null) != null) {
this.$refs.diy_goods_list.goods_cart_back_event(e);
}
},
},
};
</script>
<style scoped lang="scss"></style>

420
components/diy/header.vue Normal file
View File

@@ -0,0 +1,420 @@
<template>
<view v-if="(propValue || null) !== null" class="header-container">
<view class="header-around wh-auto" :style="roll_style + position">
<view class="wh-auto" :style="roll_img_style">
<view class="wh-auto ht-auto pa up_slide_bg" :style="up_slide_style">
<view class="wh-auto ht-auto" :style="up_slide_img_style"></view>
</view>
<view :style="top_content_style">
<view class="header-content flex-row align-s">
<view class="model-top flex-1 mt-1">
<view class="roll pr z-i">
<view class="model-head pr padding-left-main padding-right-main" style="box-sizing: border-box">
<view class="flex-col" :style="'gap:' + data_alone_row_space">
<view class="model-head-content flex-row align-c jc-sb gap-16 wh-auto pr" :style="header_style">
<!-- 支付宝小程序自带返回按钮这里就不给返回按钮了这里给留出一点空间就行 -->
<!-- #ifndef MP-ALIPAY -->
<view v-if="!is_tabbar_pages" class="z-i dis-inline-block margin-top-xs" @tap="top_nav_left_back_event">
<iconfont name="icon-arrow-left" size="40rpx" propContainerDisplay="flex" :color="form.style.left_back_btn_color || '#333'"></iconfont>
</view>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<view class="dis-inline-block padding-left-sm"></view>
<!-- #endif -->
<view v-if="['1', '2', '3'].includes(form.content.theme)" class="flex-1">
<view class="flex-row align-c jc-c ht-auto gap-16" :class="position_class" :style="text_style + 'justify-content:' + form.content.indicator_location || 'center'">
<template v-if="['2', '3'].includes(form.content.theme)">
<view v-if="form.content.logo.length > 0" class="logo-outer-style re flex-row align-c">
<template v-if="form.style.up_slide_logo && form.style.up_slide_logo.length > 0">
<!-- 有上滑logo的处理逻辑 -->
<view v-if="(propScrollTop - 5) / (header_top + 33) < 1" class="logo-style" :style="up_slide_old_logo_style">
<image class="logo-style" :src="form.content.logo[0].url" mode="heightFix" />
</view>
<view :class="['logo-style left-0', { pa: (propScrollTop - 5) / (header_top + 33) < 1 }]" :style="'opacity:0;' + up_slide_opacity">
<image class="logo-style" :src="form.style.up_slide_logo[0].url" mode="heightFix" />
</view>
</template>
<template v-else>
<!-- 没有上滑logo的处理逻辑 -->
<image class="logo-style" :src="form.content.logo[0].url" mode="heightFix" />
</template>
</view>
</template>
<view v-if="['1', '2', '3'].includes(form.content.theme) && !isEmpty(form.content.title)">{{ form.content.title }}</view>
<template v-if="['3'].includes(form.content.theme) && !is_search_alone_row">
<view class="flex-1 fw-n">
<componentDiySearch :propValue="form" :propIsPageSettings="true"></componentDiySearch>
</view>
</template>
</view>
</view>
<view v-else-if="['4', '5'].includes(form.content.theme)" class="flex-1 flex-row align-c">
<view v-if="form.content.positioning_name_float !== '1'" class="flex-row align-c gap-2">
<view :style="location_margin">
<component-choice-location :propLocationContainerStyle="style_location_container" :propLocationImgContainerStyle="style_location_img_container" :propBaseColor="location_color" :propTextDefaultName="form.content.positioning_name" :propIsLeftIconArrow="form.content.is_location_left_icon_show == '1'" :propLeftImgValue="form.content.location_left_img" :propLeftIconValue="'icon-' + form.content.location_left_icon" :propIconLocationSize="location_left_size" :propIconArrowSize="location_right_size" :propIsRightIconArrow="form.content.is_location_right_icon_show == '1'" :propRightImgValue="form.content.location_right_img" :propRightIconValue="'icon-' + form.content.location_right_icon" :propTextMaxWidth="location_name_style" propContainerDisplay="flex" @onBack="choice_location_back"></component-choice-location>
</view>
</view>
<template v-if="['5'].includes(form.content.theme) && !is_search_alone_row">
<view class="flex-1">
<componentDiySearch :propValue="form" :propIsPageSettings="true" :propLocationMargin="location_margin" propSearchType="header" :propLocationContainerStyle="style_location_container" :propLocationImgContainerStyle="style_location_img_container" :propBaseColor="location_color" :propIconLocationSize="location_left_size" :propIconArrowSize="location_right_size" @onBack="choice_location_back"></componentDiySearch>
</view>
</template>
</view>
<view v-if="!isEmpty(form.content.icon_setting) && !is_icon_alone_row" class="flex-row align-c z-i" :class="['1'].includes(form.content.theme) ? 'right-0' : ''" :style="{ gap: form.style.img_space * 2 + 'rpx' }">
<view v-for="(item, index) in form.content.icon_setting" :key="index" class="pr" :style="{ width: form.style.img_size * 2 + 'rpx', height: form.style.img_size * 2 + 'rpx' }" :data-value="item.link.page" @tap="url_event">
<imageEmpty v-if="item.img.length > 0" :propImageSrc="item.img[0].url" :propErrorStyle="'width: ' + Number(form.style.img_size) * 2 + 'rpx;height:' + Number(form.style.img_size) * 2 + 'rpx;'"></imageEmpty>
<iconfont v-else :name="'icon-' + item.icon" :size="form.style.img_size * 2 + 'rpx'" :color="form.style.img_color" propContainerDisplay="flex"></iconfont>
<view v-if="!isEmpty(item.badge) && item.badge !== 0" class="pa badge-style">
<component-badge :propNumber="item.badge || 0"></component-badge>
</view>
</view>
</view>
</view>
<view v-if="is_search_alone_row || is_icon_alone_row" class="model-head-content flex-row align-c gap-16">
<template v-if="['3', '5'].includes(form.content.theme) && is_search_alone_row">
<view class="flex-1">
<componentDiySearch :propValue="form" :propIsPageSettings="true" :propLocationMargin="location_margin" propSearchType="header" :propLocationContainerStyle="style_location_container" :propLocationImgContainerStyle="style_location_img_container" :propBaseColor="location_color" :propIconLocationSize="location_left_size" :propIconArrowSize="location_right_size" @onBack="choice_location_back"></componentDiySearch>
</view>
</template>
<view v-if="!isEmpty(form.content.icon_setting) && is_icon_alone_row" class="flex-row align-c z-i" :class="['1'].includes(form.content.theme) ? 'right-0' : ''" :style="{ gap: form.style.img_space * 2 + 'rpx' }">
<view v-for="(item, index) in form.content.icon_setting" :key="index" class="pr" :style="{ width: form.style.img_size * 2 + 'rpx', height: form.style.img_size * 2 + 'rpx' }" :data-value="item.link.page" @tap="url_event">
<imageEmpty v-if="item.img.length > 0" :propImageSrc="item.img[0].url" :propErrorStyle="'width: ' + Number(form.style.img_size) * 2 + 'rpx;height:' + Number(form.style.img_size) * 2 + 'rpx;'"></imageEmpty>
<iconfont v-else :name="'icon-' + item.icon" :size="form.style.img_size * 2 + 'rpx'" :color="form.style.img_color" propContainerDisplay="flex"></iconfont>
<view v-if="!isEmpty(item.badge) && item.badge !== 0" class="pa badge-style">
<component-badge :propNumber="item.badge || 0"></component-badge>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<block v-if="!is_immersion_model">
<view v-if="!is_positon_realative" class="nav-seat" :style="top_content_style">
<view :style="'height:' + (is_search_alone_row || is_icon_alone_row ? 'calc(132rpx + ' + data_alone_row_space + ');' : '66rpx;')"></view>
</view>
</block>
<!-- #ifndef H5 || MP-TOUTIAO -->
<!-- <view v-if="is_positon_realative" class="wh-auto pf top-0 left-0 right-0" :style="roll_style">
<view :style="top_content_style">
<view :style="'height:' + (is_search_alone_row || is_icon_alone_row ? 'calc(132rpx + ' + data_alone_row_space + ');' : '66rpx;')"></view>
</view>
</view> -->
<!-- #endif -->
</view>
</template>
<script>
const app = getApp();
import componentDiySearch from '@/components/diy/search';
import imageEmpty from '@/components/diy/modules/image-empty';
import componentChoiceLocation from '@/components/choice-location/choice-location';
import componentBadge from '@/components/badge/badge';
import { isEmpty, background_computer, gradient_computer, margin_computer, padding_computer, radius_computer } from '@/common/js/common/common.js';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
props: {
propValue: {
type: [String, Number, Object],
default: '',
},
// 滚动距离
propScrollTop: {
type: Number,
default: 0,
},
propKey: {
type: [String, Number],
default: '',
},
},
components: {
componentDiySearch,
imageEmpty,
componentChoiceLocation,
componentBadge,
},
data() {
return {
form: {},
new_style: {},
position: '',
roll_style: '',
text_style: '',
header_style: 'max-width:100%',
common_app_is_header_nav_fixed: 0,
// 5,7,0 是误差,, 10 是下边距66是高度bar_height是不同小程序下的导航栏距离顶部的高度
// #ifdef MP
top_content_style: 'padding-top:' + (bar_height + 5) + 'px;padding-bottom:10px;',
// #endif
// #ifdef H5 || MP-TOUTIAO
top_content_style: 'padding-top:' + (bar_height + 7) + 'px;padding-bottom:10px;',
// #endif
// #ifdef APP
top_content_style: 'padding-top:' + bar_height + 'px;padding-bottom:10px;',
// #endif
is_positon_realative: false,
// 顶部背景样式类别
header_background_type: 'color_image',
// #ifdef MP
header_top: bar_height + 5 + 10 + 33,
// #endif
// #ifdef H5 || MP-TOUTIAO
header_top: bar_height + 7 + 10 + 33,
// #endif
// #ifdef APP
header_top: bar_height + 0 + 10 + 33,
// #endif
// 判断是否是沉浸模式
is_immersion_model: false,
up_slide_opacity: '',
up_slide_old_logo_style: '',
up_slide_style: '',
up_slide_img_style: '',
// 当前页面是否在底部菜单中
is_tabbar_pages: app.globalData.is_tabbar_pages(),
// 判断header的查询是否独行
is_search_alone_row: false,
is_icon_alone_row: false,
data_alone_row_space: '0rpx',
// 定位设置
style_location_container: '',
style_location_img_container: '',
location_left_size: '24rpx',
location_right_size: '24rpx',
location_margin: '', // 悬浮之后有间距所以要将margin设置成外padding
location_color: '', // 定位颜色
location_name_style: '', // 定位样式
// 默认数据
old_radius: { radius: 0, radius_top_left: 0, radius_top_right: 0, radius_bottom_left: 0, radius_bottom_right: 0 },
old_padding: { padding: 0, padding_top: 0, padding_bottom: 0, padding_left: 0, padding_right: 0 },
old_margin: { margin: 0, margin_top: 0, margin_bottom: 0, margin_left: 0, margin_right: 0 },
};
},
watch: {
// 监听滚动距离
propScrollTop(newVal) {
const { up_slide_background_color_list, up_slide_background_direction, up_slide_background_img, up_slide_background_img_style, up_slide_display } = this.propValue.style || {};
if (up_slide_display == '1') {
// 渐变
const gradient = { color_list: up_slide_background_color_list, direction: up_slide_background_direction };
// 背景图
const back = { background_img: up_slide_background_img, background_img_style: up_slide_background_img_style };
const up_slide_opacity = 'opacity:' + ((newVal - 20) / this.header_top > 1 ? 1 : ((newVal - 20) / this.header_top).toFixed(2)) + ';';
this.up_slide_opacity = up_slide_opacity;
// 来的logo要比新的隐藏的快所以要比原来的logo快一点
this.up_slide_old_logo_style = 'opacity:' + ((newVal - 5) / this.header_top > 1 ? 0 : (1 - (newVal - 5) / this.header_top).toFixed(2)) + ';';
// =0是大小误差
this.up_slide_style = gradient_computer(gradient) + up_slide_opacity;
this.up_slide_img_style = background_computer(back);
}
},
propKey(val) {
if ((this.propValue || null) !== null) {
this.init();
}
},
},
created() {
// 判断是否有值初始化
if ((this.propValue || null) !== null) {
this.init();
}
},
methods: {
// 判断是否为空
isEmpty,
// 初始化
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
let new_roll_style = '';
let new_roll_img_style = '';
const { header_background_img, header_background_img_style, header_background_color_list, header_background_direction, header_background_type, immersive_style } = new_style;
if (header_background_type === 'color_image') {
// 渐变
const gradient = { color_list: header_background_color_list, direction: header_background_direction };
// 背景图
const back = { background_img: header_background_img, background_img_style: header_background_img_style };
new_roll_style += gradient_computer(gradient);
new_roll_img_style += background_computer(back);
} else {
new_roll_style += `background: transparent;`;
}
// 小程序下,获取小程序胶囊的宽度
let menu_button_info = 'max-width:100%';
let new_text_style = `font-weight:${new_style.header_background_title_typeface}; font-size: ${new_style.header_background_title_size * 2}rpx; color: ${new_style.header_background_title_color};`;
// #ifndef MP-TOUTIAO
// #ifdef MP
// 判断是否有胶囊
const is_current_single_page = app.globalData.is_current_single_page();
// 如果有胶囊的时候,做处理
if (is_current_single_page == 0) {
const custom = uni.getMenuButtonBoundingClientRect();
menu_button_info = `max-width:calc(100% - ${custom.width + 10}px);`;
new_text_style += `right:-${custom.width + 10}px;`;
}
// #endif
// #endif
const { location_margin = this.old_margin } = new_style;
this.setData({
form: this.propValue,
position: new_style.up_slide_display == '1' ? 'position:fixed;' : new_style.immersive_style === '1' ? 'position:absolute;' : 'position:relative;',
is_positon_realative: new_style.up_slide_display == '1' ? false : true,
roll_style: new_roll_style,
roll_img_style: new_roll_img_style,
text_style: new_text_style,
position_class: new_content.indicator_location == 'center' ? `indicator-center` : '',
header_style: menu_button_info,
header_background_type: header_background_type,
is_immersion_model: header_background_type !== 'color_image' && immersive_style == '1',
data_alone_row_space: new_style.data_alone_row_space * 2 + 'rpx',
is_search_alone_row: new_content.data_alone_row_value && new_content.data_alone_row_value.includes('search'),
is_icon_alone_row: new_content.data_alone_row_value && new_content.data_alone_row_value.includes('icon'),
style_location_container: this.get_style_location_container(new_style),
style_location_img_container: this.get_style_location_img_container(new_style),
location_left_size: !isEmpty(new_style.location_left_icon_size) ? new_style.location_left_icon_size * 2 + 'rpx' : '24rpx',
location_right_size: !isEmpty(new_style.location_right_icon_size) ? new_style.location_right_icon_size * 2 + 'rpx' : '24rpx',
location_color: !isEmpty(new_style.location_color) ? new_style.location_color : new_style?.position_color || '',
location_name_style: this.get_location_name_style(new_content),
location_margin: `padding: ${location_margin.margin_top * 2}rpx ${location_margin.margin_right * 2}rpx ${location_margin.margin_bottom * 2}rpx ${location_margin.margin_left * 2}rpx;`, // 悬浮之后有间距所以要将margin设置成外padding
});
// this.$emit('onImmersionModelCallBack', this.is_immersion_model);
},
get_location_name_style(new_content) {
const is_search_alone_row = new_content.data_alone_row_value && new_content.data_alone_row_value.includes('search');
const is_icon_alone_row = new_content.data_alone_row_value && new_content.data_alone_row_value.includes('icon');
let width = 0;
if (is_search_alone_row && is_icon_alone_row) {
width = 200;
} else if (is_search_alone_row || is_icon_alone_row) {
width = 100;
}
if (new_content.theme == '4') {
return `${(150 + width) * 2}rpx;`;
} else {
return `${(100 + width) * 2}rpx;`;
}
},
// 定位设置
get_style_location_container(new_style) {
const { location_margin = this.old_margin, location_radius = this.old_radius } = new_style;
const style = {
color_list: new_style?.location_color_list || [],
direction: new_style?.location_direction || '',
};
const height = 32 - location_margin.margin_top - location_margin.margin_bottom;
return gradient_computer(style) + radius_computer(location_radius) + `height: ${height * 2}rpx;line-height: ${height * 2}rpx;`;
},
// 背景图片
get_style_location_img_container(new_style) {
const { location_background_img = [], location_background_img_style = '2', location_border_show = '0', location_padding = this.old_padding, location_margin = this.old_margin, location_border_size = this.old_padding, location_border_color = '', location_border_style = 'solid' } = new_style;
const style = {
background_img: location_background_img,
background_img_style: location_background_img_style,
};
let border = ``;
if (location_border_show == '1') {
border += `border-width: ${location_border_size.padding_top}px ${location_border_size.padding_right}px ${location_border_size.padding_bottom}px ${location_border_size.padding_left}px;border-style: ${location_border_style};border-color: ${location_border_color};`;
}
const height = 32 - (location_margin.margin_top || 0) - (location_margin.margin_bottom || 0);
return background_computer(style) + padding_computer(location_padding) + border + `height: ${(height > 0 ? height : 0) * 2}rpx;line-height: ${(height > 0 ? height : 0) * 2}rpx;box-sizing: border-box;`;
},
// 获取顶部导航高度
get_nav_height() {
const query = uni.createSelectorQuery().in(this);
query
.select('.article-tabs')
.boundingClientRect((res) => {
if ((res || null) != null) {
this.setData({
tabs_top: res.top,
});
}
})
.exec();
},
// 位置回调
choice_location_back(e) {
this.$emit('onLocationBack', e);
},
// 打开地址
url_event(e) {
app.globalData.url_event(e);
},
// 返回事件
top_nav_left_back_event() {
app.globalData.page_back_prev_event();
},
},
};
</script>
<style lang="scss" scoped>
.header-container {
width: 100%;
.header-around {
z-index: 12;
}
.model-top {
.roll {
width: 100%;
height: 100%;
margin: 0 auto;
}
.img {
width: 680rpx;
}
}
.model-head {
.model-head-content {
height: 66rpx;
// overflow: hidden;
top: -1rpx;
/* #ifdef H5 || MP-TOUTIAO */
top: 4rpx;
/* #endif */
}
}
.model-head-icon {
position: absolute;
right: 0;
bottom: 0;
height: 66rpx;
.function-icon {
height: 66rpx;
}
}
.logo-outer-style {
height: 56rpx;
.logo-style {
height: 56rpx;
width: 100%;
}
}
}
.indicator-center {
position: absolute;
left: 0;
right: 0;
text-align: center;
top: 0;
padding-left: 0;
}
.up_slide_bg {
z-index: -1;
}
.badge-style {
top: -20rpx;
right: 5rpx;
}
</style>

122
components/diy/hot-zone.vue Normal file
View File

@@ -0,0 +1,122 @@
<template>
<!-- 热区 -->
<view ref="containerRef" class="oh container" :style="style_container">
<view :style="style_img_container">
<view ref="hotRef" class="hot pr" :style="style">
<image :src="img" class="wh-auto dis-block" mode="widthFix" @load="on_load_img" />
<view v-for="(item, index) in hot_data" :key="index" class="hot_box" :style="'left: ' + item.drag_start.x * w_scale1 * w_scale2 + 'px;top:' + item.drag_start.y * h_scale1 * h_scale2 + 'px;width: ' + Math.max(item.drag_end.width * w_scale1 * w_scale2, 1) + 'px;height: ' + Math.max(item.drag_end.height * h_scale1 * h_scale2, 1) + 'px;display: flex;'" :data-value="item.link.page" @tap="url_event"> </view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
img: '',
hot_data: [],
img_width: 1,
img_height: 1,
container_ref_h: 0,
hot_ref_h: 0,
hot_ref_w: 0,
w_scale1: 1,
h_scale1: 1,
w_scale2: 1,
h_scale2: 1,
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
this.setData({
img: new_content.img[0].url,
img_width: new_content.hot.img_width || 1,
img_height: new_content.hot.img_height || 1,
hot_data: new_content.hot.data || [],
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
// 图片加载完成 获取宽高
on_load_img(e) {
const query = uni.createSelectorQuery();
// 选择我们想要的元素
query
.in(this)
.select('.container')
.boundingClientRect((res) => {
if ((res || null) != null) {
// data包含元素的宽度、高度等信息
this.setData({
container_ref_h: res.height,
w_scale1: res.height / this.img_width,
h_scale1: res.height / this.img_height,
});
}
})
.exec(); // 执行查询
query
.in(this)
.select('.hot')
.boundingClientRect((res) => {
if ((res || null) != null) {
// data包含元素的宽度、高度等信息
this.setData({
w_scale2: res.width / this.container_ref_h,
h_scale2: res.height / this.container_ref_h,
});
}
})
.exec(); // 执行查询
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style lang="scss" scoped>
.hot {
min-height: 20rpx;
.hot_box {
// background: rgba(42, 148, 255, 0.15);
// border: 2rpx dashed rgba(142, 198, 255, 0.5);
position: absolute;
opacity: 0.4;
}
}
</style>

View File

@@ -0,0 +1,228 @@
<template>
<!-- 图片魔方 -->
<view class="img-magic" :style="style_container + 'height:' + (form.style_actived == 10 ? '100%' : container_size)">
<view class="magic-container wh-auto ht-auto" :style="style_img_container">
<view class="pr" :style="outer_style">
<!-- 风格3 -->
<template v-if="form.style_actived == 2">
<view class="flex-row align-c jc-c style-size">
<view v-for="(item, index) in form.img_magic_list" :key="index" class="three flex-row" :style="img_spacing" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto ht-auto" :mode="img_fit" :style="content_img_radius"></image>
</view>
</view>
</view>
</view>
</template>
<!-- 风格9 -->
<template v-else-if="form.style_actived == 8">
<view class="flex-row align-c jc-c style-size flex-wrap">
<view v-for="(item, index) in form.img_magic_list" :key="index" :class="['flex-row', { 'style9-top': [0, 1].includes(index), 'style9-bottom': ![0, 1].includes(index) }]" :style="img_spacing" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto ht-auto" :mode="img_fit" :style="content_img_radius"></image>
</view>
</view>
</view>
</view>
</template>
<template v-else-if="form.style_actived == 10">
<template v-if="form.limit_size == '0'">
<view v-for="(item, index) in form.img_magic_list" :key="index" class="cr-main flex-row" :style="img_spacing + selected_style(item)" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto" mode="widthFix" :style="content_img_radius"></image>
</view>
</view>
</view>
</template>
<template v-else>
<view v-for="(item, index) in form.img_magic_list" :key="index" class="cr-main flex-row" :style="img_spacing + selected_style(item) + ';height:' + image_height" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto ht-auto" :mode="img_fit" :style="content_img_radius"></image>
</view>
</view>
</view>
</template>
</template>
<template v-else>
<view v-for="(item, index) in form.img_magic_list" :key="index" class="cube-selected cr-main flex-row" :style="img_spacing + selected_style(item)" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto ht-auto" :mode="img_fit" :style="content_img_radius"></image>
</view>
</view>
</view>
</template>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, radius_computer, percentage_count, isEmpty, margin_computer, padding_computer, old_padding, old_margin, border_width } from '@/common/js/common/common.js';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propOuterContainerPadding: {
type: Number,
default: 0,
}
},
data() {
return {
form: {},
style_container: '',
style_img_container: '',
// 外部样式
outer_style: '',
// 图片间距设置
img_spacing: '',
// 图片圆角
content_img_radius: '',
cube_cell: '',
container_size: '',
div_width: 0,
image_height: '',
img_fit: '',
content_img_container: '',
content_img_style_container: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
computed: {
// 根据当前页面大小计算成百分比
selected_style() {
return (item) => {
return `overflow: hidden;width: ${this.percentage(this.getSelectedWidth(item))}; height: ${this.percentage(this.getSelectedHeight(item))}; top: ${this.percentage(this.getSelectedTop(item))}; left: ${this.percentage(this.getSelectedLeft(item))};`;
};
},
},
mounted() {
this.init();
},
methods: {
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
const new_style_spacing = new_content.style_actived === 10 ? 0 : new_style.image_spacing;
// 外部样式
const outer_spacing = `calc(100% + ${new_style_spacing * 2}rpx)`;
const outer_sx = `-${new_style_spacing}rpx`;
// 图片间距设置
const spacing = `${new_style_spacing}rpx`;
// scaleToFill 对应 cover aspectFit 对应 contain center 对应 none
let fit = '';
if (new_content.img_fit == 'contain') {
fit = 'aspectFit';
} else if (new_content.img_fit == 'fill') {
fit = 'scaleToFill';
} else if (new_content.img_fit == 'cover') {
fit = 'aspectFill';
}
const container_height = !isEmpty(new_content.container_height) ? new_content.container_height : sys_width;
const density = !isEmpty(new_content.magic_cube_density) ? new_content.magic_cube_density : 4;
const { margin_left, margin_right } = new_style.common_style;
const width = sys_width - margin_left - margin_right - border_width(new_style.common_style) - this.propOuterContainerPadding;
const scale = width / 390;
this.setData({
form: this.propValue.content,
new_style: this.propValue.style,
outer_style: `width:${outer_spacing};height:${outer_spacing};margin:${outer_sx};`,
img_spacing: `padding:${spacing};`,
content_img_radius: radius_computer(new_style),
style_container: common_styles_computer(new_style.common_style) + 'box-sizing: border-box;',
style_img_container: common_img_computer(new_style.common_style, this.propIndex) + 'box-sizing: border-box;',
img_fit: fit,
div_width: sys_width,
container_size: container_height * scale + 'px',
cube_cell: sys_width / density,
image_height: this.propValue.content.image_height * scale + 'px',
content_img_container: common_styles_computer(new_style) + margin_computer(new_style?.margin || old_margin) + 'box-sizing: border-box;',
content_img_style_container: common_img_computer(new_style) + padding_computer(new_style?.padding || old_padding) + 'box-sizing: border-box;',
});
},
getSelectedWidth(item) {
return (item.end.x - item.start.x + 1) * this.cube_cell;
},
//计算选中层的高度。
getSelectedHeight(item) {
return (item.end.y - item.start.y + 1) * this.cube_cell;
},
//计算选中层的右边距离。
getSelectedTop(item) {
return (item.start.y - 1) * this.cube_cell;
},
//计算选中层的左边距离。
getSelectedLeft(item) {
return (item.start.x - 1) * this.cube_cell;
},
// 计算成百分比
percentage(num) {
return percentage_count(num, this.div_width);
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style lang="scss" scoped>
// 图片魔方是一个正方形,根据宽度计算高度
.img-magic {
overflow: hidden;
box-sizing: border-box;
}
.cube-selected {
position: absolute;
text-align: center;
box-sizing: border-box;
}
.style-size {
height: 100%;
width: 100%;
box-sizing: border-box;
.three {
width: 33%;
height: 100%;
position: relative;
box-sizing: border-box;
}
.style9-top {
width: 50%;
height: 50%;
position: relative;
box-sizing: border-box;
}
.style9-bottom {
width: calc(100% / 3);
height: 50%;
position: relative;
box-sizing: border-box;
}
}
</style>

View File

@@ -0,0 +1,279 @@
<template>
<view :class="'wh-auto pr allSignList-' + propIndex + propKey" :style="'height:' + propDataHeight * propScale + 'px;'">
<view v-for="(item, index) in new_list" :key="index" :data-id="item.id" :data-location-x="item.location.x" :data-location-y="item.location.y" :class="'sign-' + propIndex + propKey + ' main-content ' + get_animation_class(item.com_data)" :style="'left:' + get_percentage_count(item.location.x, item.com_data.data_follow, 'left') + ';top:' + get_percentage_count(item.location.y, item.com_data.data_follow, 'top') + ';width:' + get_percentage_count(item.com_data.com_width, item.com_data.data_follow, 'width', item.com_data.is_width_auto, item.com_data.max_width, item.key) + ';height:' + get_percentage_count(item.com_data.com_height, item.com_data.data_follow, 'height', item.com_data.is_height_auto, item.com_data.max_height, item.key) + ';z-index:' + (new_list.length - 1 > 0 ? (new_list.length - 1) - index : 0)">
<template v-if="item.key == 'text'">
<model-text :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" :propTitleParams="propShowData.data_name" @url_event="url_event"></model-text>
</template>
<template v-else-if="item.key == 'img'">
<model-image :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" :propImgParams="propShowData.data_logo" @url_event="url_event"></model-image>
</template>
<template v-else-if="item.key == 'auxiliary-line'">
<model-lines :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId"></model-lines>
</template>
<template v-else-if="item.key == 'icon'">
<model-icon :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" @url_event="url_event"></model-icon>
</template>
<template v-else-if="item.key == 'panel'">
<model-panel :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" @url_event="url_event"></model-panel>
</template>
</view>
</view>
</template>
<script>
import modelText from '@/components/diy/modules/custom/model-text.vue';
import modelLines from '@/components/diy/modules/custom/model-lines.vue';
import modelImage from '@/components/diy/modules/custom/model-image.vue';
import modelIcon from '@/components/diy/modules/custom/model-icon.vue';
import modelPanel from '@/components/diy/modules/custom/model-panel.vue';
import { location_compute, isEmpty } from '@/common/js/common/common.js';
export default {
components: {
modelText,
modelLines,
modelImage,
modelIcon,
modelPanel,
},
props: {
propCustomList: {
type: Array,
default: () => {
return [];
},
required: true,
},
propIndex: {
type: Number,
default: 0,
},
propSourceList: {
type: Object,
default: () => {
return {};
}
},
propDataHeight: {
type: Number,
default: 0,
},
propScale: {
type: Number,
default: 1,
},
propDataIndex: {
type: Number,
default: 1,
},
propDataSplitIndex: {
type: Number,
default: 1,
},
propIsCustom: {
type: Boolean,
default: false,
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propShowData: {
type: Object,
default: () => ({
data_key: 'id',
data_name: 'name'
}),
},
propKey: {
type: [String, Number],
default: '',
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: () => {
return [];
}
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
new_list: [],
custom_width: 0,
};
},
watch: {
propKey(val) {
// 初始化
this.init(this.propCustomList);
},
propCustomList(val) {
this.init(val);
}
},
computed: {
get_percentage_count() {
return (num, data_follow, type, is_auto = '0', max_size = 0, key = '') => {
// 检查类型是否为'left'或'top',如果是,则根据跟随数据计算样式
if (['left', 'top'].includes(type)) {
const { id = '', type: follow_type = 'left' } = data_follow || { id: '', type: 'left' };
// 如果id不为空且follow_type与type匹配则返回原始值的字符串表示
if (id !== '' && follow_type === type) {
return `${num}px`;
}
// 如果条件不满足则根据比例缩放num并返回
return `${num * this.propScale}px`;
} else {
// 如果is_auto设置为'1'则根据type和max_size计算自动样式
if (is_auto === '1') {
if (type === 'width' || type === 'height') {
if (typeof max_size === 'number' && max_size >= 0) {
const scaledMaxSize = max_size * this.propScale;
const autoStyle = 'auto;';
const maxSizeStyle = scaledMaxSize > 0 ? ` max-${type}: ${scaledMaxSize}px;` : '';
const whiteSpaceStyle = type === 'width' && scaledMaxSize <= 0 ? ' white-space:nowrap;' : '';
return `${ autoStyle }${ maxSizeStyle }${ whiteSpaceStyle }`;
} else {
return 'auto;';
}
}
} else {
// 微信小程序图片等比缩放对小数点后的内容支持的不是特别的好,需要取向上取整数
if (key == 'img' && ['width', 'height'].includes(type)) {
// 如果is_auto未设置或条件不满足则根据比例缩放num并返回
return `${ Math.round(num * this.propScale) }px`;
} else {
// 如果is_auto未设置或条件不满足则根据比例缩放num并返回
return `${num * this.propScale}px`;
}
}
}
};
},
get_animation_class() {
return (data) => {
const { type = 'none', number = 'infinite' } = data?.animation_style || {};
if (type != 'none') {
return type + (number == 'infinite' ? `-${number}` : '');
} else {
return '';
}
};
}
},
mounted() {
this.init(this.propCustomList);
},
methods: {
async init(val) {
// 如果为空就不进行渲染
if (isEmpty(val)) {
return;
}
await this.get_custom_width();
this.set_new_list(val);
},
get_custom_width() {
// 获取当前容器的宽度
const query = uni.createSelectorQuery().in(this);
query.select('.allSignList-' + this.propIndex + this.propKey)
.boundingClientRect((res) => {
if (res) {
this.setData({
custom_width: res.width,
});
}
})
.exec();
},
async set_new_list(val) {
// 第一次渲染先渲染全部数据
this.setData({
new_list: val
});
// 判断是否有跟随的数据
const follow_list = val.filter(item => item.com_data.data_follow && item.com_data?.data_follow?.id !== '');
if (follow_list.length > 0) {
// 等待页面渲染完成之后再获取内容
await this.$nextTick();
// 第二次如果有跟随数据,更新对应数据的内容, 如果有超出容器范围的数据,限制其超出容器范围
const query = uni.createSelectorQuery().in(this);
query.selectAll('.sign-' + this.propIndex + this.propKey)
.boundingClientRect((rect) => {
if (rect) {
// 将返回的内容转成map对象方便快速查找节省性能
const idMap = new Map(rect.map(item => [item.dataset.id, item]));
// 历史数据拷贝,方便后续操作避免每次都更新数据,统一重新渲染
const val = JSON.parse(JSON.stringify(this.new_list));
val.forEach((item1) => {
const { data_follow } = item1.com_data;
const targetItem = idMap.get(data_follow?.id);
if (targetItem) {
const text_item = item1.key == 'text' ? idMap.get((item1?.id || '')+ '') : undefined;
if (data_follow?.type === 'left') {
// 更新位置信息
const location_x = this.updateLocation(targetItem, data_follow, this.propScale, true);
// 获取组件的宽度,如果是宽度自适应,则需要重新计算位置
let item_width = item1.com_data.com_width;
// 如果是宽度自适应,需要重新判断一下处理逻辑
if (item1.com_data?.is_width_auto === '1' && text_item) {
item_width = text_item.width;
}
// 根据容器信息更新位置信息
item1.location.x = location_compute(item_width, location_x, this.custom_width);
} else if (data_follow?.type === 'top') {
// 更新位置信息
const location_y = this.updateLocation(targetItem, data_follow, this.propScale, false);
// 获取组件的宽度,如果是宽度自适应,则需要重新计算位置
let item_height = item1.com_data.com_height;
// 如果是高度自适应,需要重新判断一下处理逻辑
if (item1.com_data?.is_height_auto === '1' && text_item) {
item_height = text_item.height;
}
// 根据容器信息更新位置信息
item1.location.y = location_compute(item_height, location_y, this.propDataHeight * this.propScale);
}
}
});
this.setData({
new_list: val
});
}
})
.exec();
}
},
updateLocation(targetItem, data_follow, scale, isX) {
try {
const locationValueStr = targetItem.dataset[`location${isX ? 'X' : 'Y'}`];
if (locationValueStr == null) {
return;
}
const locationValue = parseFloat(locationValueStr);
if (isNaN(locationValue) || scale <= 0 || (isX ? targetItem.width < 0 : targetItem.height < 0)) return;
return ((locationValue + (data_follow?.spacing || 0)) * scale) + (isX ? targetItem.width : targetItem.height);
} catch (error) {
console.error(`Error updating location ${isX ? 'X' : 'Y'}:`, error);
}
},
url_event(e) {
this.$emit('url_event', e, this.propDataIndex, this.propDataSplitIndex);
}
},
};
</script>
<style lang="scss" scoped>
.main-content {
position: absolute;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,290 @@
<template>
<view :class="'wh-auto pr allSignList-' + propIndex + propKey" :style="'height:' + propDataHeight * propScale + 'px;'">
<view v-for="(item, index) in new_list" :key="index" :data-id="item.id" :data-location-x="item.location.x" :data-location-y="item.location.y" :class="'sign-' + propIndex + propKey + ' main-content ' + get_animation_class(item.com_data)" :style="'left:' + get_percentage_count(item.location.x, item.com_data.data_follow, 'left') + ';top:' + get_percentage_count(item.location.y, item.com_data.data_follow, 'top') + ';width:' + get_percentage_count(item.com_data.com_width, item.com_data.data_follow, 'width', item.com_data.is_width_auto, item.com_data.max_width, item.key) + ';height:' + get_percentage_count(item.com_data.com_height, item.com_data.data_follow, 'height', item.com_data.is_height_auto, item.com_data.max_height, item.key) + ';z-index:' + (new_list.length - 1 > 0 ? (new_list.length - 1) - index : 0)">
<template v-if="item.key == 'text'">
<model-text :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" :propTitleParams="propShowData.data_name" @url_event="url_event"></model-text>
</template>
<template v-else-if="item.key == 'img'">
<model-image :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" :propImgParams="propShowData.data_logo" @url_event="url_event"></model-image>
</template>
<template v-else-if="item.key == 'auxiliary-line'">
<model-lines :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId"></model-lines>
</template>
<template v-else-if="item.key == 'icon'">
<model-icon :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" @url_event="url_event"></model-icon>
</template>
<template v-else-if="item.key == 'panel'">
<model-panel :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" @url_event="url_event"></model-panel>
</template>
<template v-else-if="item.key == 'custom-group'">
<model-custom-group :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propConfigLoop="propConfigLoop" :propDataWidth="item.com_data.com_width" :propDataHeight="item.com_data.custom_height" :propSourceList="propSourceList" :propGroupSourceList="propGroupSourceList" :propIsCustom="propIsCustom" :propShowData="propShowData"></model-custom-group>
</template>
</view>
</view>
</template>
<script>
import modelText from '@/components/diy/modules/custom/model-text.vue';
import modelLines from '@/components/diy/modules/custom/model-lines.vue';
import modelImage from '@/components/diy/modules/custom/model-image.vue';
import modelIcon from '@/components/diy/modules/custom/model-icon.vue';
import modelPanel from '@/components/diy/modules/custom/model-panel.vue';
import modelCustomGroup from '@/components/diy/modules/custom/model-custom-group.vue';
import { location_compute, isEmpty } from '@/common/js/common/common.js';
export default {
components: {
modelText,
modelLines,
modelImage,
modelIcon,
modelPanel,
modelCustomGroup
},
props: {
propCustomList: {
type: Array,
default: () => {
return [];
},
required: true,
},
propIndex: {
type: Number,
default: 0,
},
propSourceList: {
type: Object,
default: () => {
return {};
}
},
propGroupSourceList: {
type: Array,
default: () => {
return [];
}
},
propDataHeight: {
type: Number,
default: 0,
},
propScale: {
type: Number,
default: 1,
},
propDataIndex: {
type: Number,
default: 1,
},
propDataSplitIndex: {
type: Number,
default: 1,
},
propIsCustom: {
type: Boolean,
default: false,
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propShowData: {
type: Object,
default: () => ({
data_key: 'id',
data_name: 'name'
}),
},
propKey: {
type: [String, Number],
default: '',
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: () => {
return [];
}
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
new_list: [],
custom_width: 0,
};
},
watch: {
propKey(val) {
// 初始化
this.init(this.propCustomList);
},
propCustomList(val) {
this.init(val);
}
},
computed: {
get_percentage_count() {
return (num, data_follow, type, is_auto = '0', max_size = 0, key = '') => {
// 检查类型是否为'left'或'top',如果是,则根据跟随数据计算样式
if (['left', 'top'].includes(type)) {
const { id = '', type: follow_type = 'left' } = data_follow || { id: '', type: 'left' };
// 如果id不为空且follow_type与type匹配则返回原始值的字符串表示
if (id !== '' && follow_type === type) {
return `${num}px`;
}
// 如果条件不满足则根据比例缩放num并返回
return `${num * this.propScale}px`;
} else {
// 如果is_auto设置为'1'则根据type和max_size计算自动样式
if (is_auto === '1') {
if (type === 'width' || type === 'height') {
if (typeof max_size === 'number' && max_size >= 0) {
const scaledMaxSize = max_size * this.propScale;
const autoStyle = 'auto;';
const maxSizeStyle = scaledMaxSize > 0 ? ` max-${type}: ${scaledMaxSize}px;` : '';
const whiteSpaceStyle = type === 'width' && scaledMaxSize <= 0 ? ' white-space:nowrap;' : '';
return `${ autoStyle }${ maxSizeStyle }${ whiteSpaceStyle }`;
} else {
return 'auto;';
}
}
} else {
// 微信小程序图片等比缩放对小数点后的内容支持的不是特别的好,需要取向上取整数
if (key == 'img' && ['width', 'height'].includes(type)) {
// 如果is_auto未设置或条件不满足则根据比例缩放num并返回
return `${ Math.round(num * this.propScale) }px`;
} else {
// 如果is_auto未设置或条件不满足则根据比例缩放num并返回
return `${num * this.propScale}px`;
}
}
}
};
},
get_animation_class() {
return (data) => {
const { type = 'none', number = 'infinite' } = data?.animation_style || {};
if (type != 'none') {
return type + (number == 'infinite' ? `-${number}` : '');
} else {
return '';
}
};
}
},
mounted() {
this.init(this.propCustomList);
},
methods: {
async init(val) {
// 如果为空就不进行渲染
if (isEmpty(val)) {
return;
}
await this.get_custom_width();
this.set_new_list(val);
},
get_custom_width() {
// 获取当前容器的宽度
const query = uni.createSelectorQuery().in(this);
query.select('.allSignList-' + this.propIndex + this.propKey)
.boundingClientRect((res) => {
if (res) {
this.setData({
custom_width: res.width,
});
}
})
.exec();
},
async set_new_list(val) {
// 第一次渲染先渲染全部数据
this.setData({
new_list: val
});
// 判断是否有跟随的数据
const follow_list = val.filter(item => item.com_data.data_follow && item.com_data?.data_follow?.id !== '');
if (follow_list.length > 0) {
await this.$nextTick();
// 第二次如果有跟随数据,更新对应数据的内容, 如果有超出容器范围的数据,限制其超出容器范围
const query = uni.createSelectorQuery().in(this);
query.selectAll('.sign-' + this.propIndex + this.propKey)
.boundingClientRect((rect) => {
if (rect) {
// 将返回的内容转成map对象方便快速查找节省性能
const idMap = new Map(rect.map(item => [item.dataset.id, item]));
// 历史数据拷贝,方便后续操作避免每次都更新数据,统一重新渲染
const val = JSON.parse(JSON.stringify(this.new_list));
val.forEach((item1) => {
const { data_follow } = item1.com_data;
const targetItem = idMap.get(data_follow?.id);
if (targetItem) {
const text_item = item1.key == 'text' ? idMap.get((item1?.id || '')+ '') : undefined;
if (data_follow?.type === 'left') {
// 更新位置信息
const location_x = this.updateLocation(targetItem, data_follow, this.propScale, true);
// 获取组件的宽度,如果是宽度自适应,则需要重新计算位置
let item_width = item1.com_data.com_width;
// 如果是宽度自适应,需要重新判断一下处理逻辑
if (item1.com_data.is_width_auto === '1' && text_item) {
item_width = text_item.width;
}
// 根据容器信息更新位置信息
item1.location.x = location_compute(item_width, location_x, this.custom_width);
} else if (data_follow?.type === 'top') {
// 更新位置信息
const location_y = this.updateLocation(targetItem, data_follow, this.propScale, false);
// 获取组件的宽度,如果是宽度自适应,则需要重新计算位置
let item_height = item1.com_data.com_height;
// 如果是高度自适应,需要重新判断一下处理逻辑
if (item1.com_data?.is_height_auto === '1' && text_item) {
item_height = text_item.height;
}
// 根据容器信息更新位置信息
item1.location.y = location_compute(item_height, location_y, this.propDataHeight * this.propScale);
}
}
});
this.setData({
new_list: val
});
}
})
.exec();
}
},
updateLocation(targetItem, data_follow, scale, isX) {
try {
const locationValueStr = targetItem.dataset[`location${isX ? 'X' : 'Y'}`];
if (locationValueStr == null) {
return;
}
const locationValue = parseFloat(locationValueStr);
if (isNaN(locationValue) || scale <= 0 || (isX ? targetItem.width < 0 : targetItem.height < 0)) return;
const computedValue = ((locationValue + (data_follow?.spacing || 0)) * scale) + (isX ? targetItem.width : targetItem.height);
return computedValue;
} catch (error) {
console.error(`Error updating location ${isX ? 'X' : 'Y'}:`, error);
}
},
url_event(e) {
this.$emit('url_event', e, this.propDataIndex, this.propDataSplitIndex);
}
},
};
</script>
<style lang="scss" scoped>
.main-content {
position: absolute;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,378 @@
<template>
<view v-if="is_show" class="ht-auto" :style="style_container">
<view :style="style_img_container">
<view :style="style_content_container">
<view class="w h pr" :style="style_content_img_container">
<template v-if="data_source_content_list.length > 0 && form.data_source_direction == 'vertical'">
<view class="flex-row flex-wrap" :style="'row-gap:' + new_style.row_gap + 'px;column-gap:' + new_style.column_gap + 'px;'">
<view v-for="(item, index) in data_source_content_list" :key="index" class="ht-auto" :style="gap_width">
<view v-for="(item1, index1) in item.split_list" :key="index1">
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataGroupRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propFieldList="propFieldList" :propDataHeight="propDataHeight" :propScale="custom_scale" :propIsCustom="propIsCustom" :propIsCustomGroup="true" :propShowData="propShowData" :propConfigLoop="propConfigLoop !== '1' ? form.is_use_parent_data : '1'" :propDataIndex="index" :propDataSplitIndex="index1" @url_event="url_event"></dataGroupRendering>
</view>
</view>
</view>
</view>
</view>
</template>
<view v-else-if="data_source_content_list.length > 0 && ['vertical-scroll', 'horizontal'].includes(form.data_source_direction)" class="oh ht-auto">
<swiper class="w flex" circular="true" :vertical="form.data_source_direction != 'horizontal'" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_view" :style="{ width: '100%', height: swiper_height + 'px' }" @change="slideChange">
<swiper-item v-for="(item, index) in data_source_content_list" :key="index">
<view :class="form.data_source_direction != 'horizontal' ? 'wh-auto ht-auto' : 'flex-row'" :style="form.data_source_direction == 'horizontal' ? 'column-gap:' + new_style.column_gap + 'px;' : ''">
<view v-for="(item1, index1) in item.split_list" :key="index1" class="wh-auto ht-auto" :style="style_chunk_container + swiper_width + (form.data_source_direction == 'horizontal' ? gap_width : 'margin-bottom:' + content_outer_spacing_magin)">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataGroupRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propFieldList="propFieldList" :propDataHeight="propDataHeight" :propScale="custom_scale" :propIsCustom="propIsCustom" :propIsCustomGroup="true" :propShowData="propShowData" :propConfigLoop="propConfigLoop !== '1' ? form.is_use_parent_data : '1'" :propDataIndex="index" :propDataSplitIndex="index1" @url_event="url_event"></dataGroupRendering>
</view>
</view>
</view>
</swiper-item>
</swiper>
<view v-if="new_style.is_show == '1' && data_source_content_list.length > 1" :class="['left', 'right'].includes(new_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="indicator_location_style">
<block v-if="new_style.indicator_style == 'num'">
<view :style="indicator_style" class="dot-item">
<text :style="{ color: new_style.actived_color }">{{ actived_index + 1 }}</text>
<text>/{{ data_source_content_list.length }}</text>
</view>
</block>
<block v-else>
<view v-for="(item, index) in data_source_content_list" :key="index" :style="indicator_style + (actived_index == index ? 'background:' + new_style.actived_color : '')" class="dot-item" />
</block>
</view>
</view>
<template v-else>
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataGroupRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propFieldList="propFieldList" :propDataHeight="propDataHeight" :propScale="custom_scale" :propConfigLoop="propConfigLoop !== '1' ? form.is_use_parent_data : '1'" @url_event="url_event"></dataGroupRendering>
</view>
</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer, percentage_count, isEmpty, get_indicator_style, get_indicator_location_style, border_width, get_is_eligible } from '@/common/js/common/common.js';
// a组件调用b组件 b组件调用a组件为了避免循环引用在uniapp中出问题复制一个相同的data-group-rendering组件
import dataGroupRendering from '@/components/diy/modules/custom/data-group-rendering.vue';
const app = getApp();
export default {
components: {
dataGroupRendering
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1
},
propDataWidth: {
type: Number,
default: 0,
},
propDataHeight: {
type: Number,
default: 0,
},
propIsCustom: {
type: Boolean,
default: false
},
propFieldList: {
type: Array,
default: []
},
propGroupSourceList: {
type: Array,
default: () => {
return [];
}
},
propShowData: {
type: Object,
default: () => ({
data_key: 'id',
data_name: 'name',
})
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
new_style: {},
custom_scale: 1,
style_container: '',
style_img_container: '',
custom_list_length: 0,
custom_group_field_id: '',
source_list: {
// 存放手动输入的id
data_ids: [],
// 手动输入
data_list: [],
// 自动
data_auto_list: [],
},
data_source_content_list: [],
data_source: '',
// 内容样式
style_content_container: '',
style_content_img_container: '',
// 数据样式
style_chunk_container: '',
style_chunk_img_container: '',
// 指示器选中的下标
actived_index: 0,
// 轮播高度
swiper_height: 0,
swiper_width: 'width: 100%;',
// 指示器样式
indicator_location_style: '',
indicator_style: '',
slides_per_view: 1,
show_data: { data_key: 'id', data_name: 'name' },
old_data_style: {
color_list: [{ color: 'rgb(244, 252, 255)', color_percentage: undefined }],
direction: '180deg',
background_img_style: '2',
background_img: [],
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
},
content_outer_spacing_magin: '0rpx',
gap_width: '',
is_show: true
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
percentage_count,
init() {
const new_form = this.propValue;
const new_style = this.propValue.data_style;
const data_source_id = new_form?.data_source_field?.id || '';
// 自定义组的数据源内容切换, 判断是取自定义组数据源内容还是取自定义数据源内容
const is_data_source_id = this.propFieldList.filter((item) => item.field == data_source_id);
let new_list = [];
// 判断是否是循环内容
if (this.propConfigLoop == '1') {
// 如果自定义组选择了数据源,就按照自定义组的数据源的方式走,否则的话就按照自定义的数据走
if (is_data_source_id.length > 0) {
const list = this.get_data_source_content_list(this.propSourceList, new_form);
// 数据来源的内容
new_list = list.length > 0 ? this.get_list(list, new_form, new_style) : [];
} else {
if (!isEmpty(this.propSourceList)) {
const new_source_list = [ this.propSourceList ];
new_list = [{ split_list: new_source_list }];
} else {
new_list = [];
}
}
} else {
// 如果使用父级数据,就直接使用父级的全部数据,否则的话就没有任何数据
if (new_form.is_use_parent_data == '1') {
new_list = this.propGroupSourceList;
} else {
new_list = [];
}
}
// 初始化数据
const { common_style, data_content_style, data_style } = new_style;
const old_width = this.propDataWidth * this.propScale;
// 外层左右间距
const outer_spacing = (common_style?.margin_left || 0) + (common_style?.margin_right || 0) + (common_style?.padding_left || 0) + (common_style?.padding_right || 0) + border_width(common_style);
// 内容左右间距
const content_spacing = (data_content_style?.margin_left || 0) + (data_content_style?.margin_right || 0) + (data_content_style?.padding_left || 0) + (data_content_style?.padding_right || 0) + border_width(data_content_style);
// 数据左右间距
const internal_spacing = (data_style?.margin_left || 0) + (data_style?.margin_right || 0) + (data_style?.padding_left || 0) + (data_style?.padding_right || 0) + border_width(data_style);
// 一行显示的数量
const carousel_col = Number(new_form.data_source_carousel_col);
// 数据间距
const data_spacing = ['vertical', 'horizontal'].includes(new_form.data_source_direction) ? new_style.column_gap * (carousel_col - 1) : 0;
// 自定义组件宽度
const width = old_width - outer_spacing - content_spacing - internal_spacing - data_spacing;
// 自定义组的比例
const scale_number = width / this.propDataWidth;
const custom_scale = scale_number > 0 ? scale_number : 0;
const new_data_style = !isEmpty(new_style.data_style) ? new_style.data_style : this.old_data_style;
const new_data_content_style = !isEmpty(new_style.data_content_style)? new_style.data_content_style : this.old_data_style;
// 判断是平移还是整屏滚动
const { padding_top = 0, padding_bottom = 0, margin_bottom = 0, margin_top = 0 } = new_data_style;
let swiper_height = 0;
let col = Number(new_form.data_source_carousel_col);
// 间距
const space_between = new_form.data_source_direction == 'horizontal' ? new_style.column_gap : new_style.row_gap;
// 轮播图高度控制
if (new_form.data_source_direction == 'horizontal') {
swiper_height = this.propDataHeight * custom_scale + padding_top + padding_bottom + margin_bottom + margin_top;
} else {
// 商品数量大于列数的时候,高度是列数,否则是当前的数量
col = new_list.length > carousel_col ? carousel_col : new_list.length;
swiper_height = (this.propDataHeight * custom_scale + padding_top + padding_bottom + margin_bottom + margin_top) * col + ((Number(new_form.data_source_carousel_col) - 1) * space_between);
}
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.column_gap * (carousel_col - 1)) / carousel_col;
// 横向的时候,根据选择的行数和每行显示的个数来区分具体是显示多少个
const swiper_width = (new_form.data_source_direction == 'horizontal' && new_style.rolling_fashion != 'translation') ? `width: ${ 100 / carousel_col }%;`: 'width: 100%;';
this.setData({
form: new_form,
new_style: new_style,
custom_scale: custom_scale,
custom_list_length: new_form.custom_list.length - 1,
style_container: common_styles_computer(new_style.common_style) + (new_form.is_scroll_bar == '1' ? 'overflow: auto;' : '') + 'box-sizing: border-box;', // 用于样式显示
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
style_content_container: common_styles_computer(new_data_content_style) + 'box-sizing: border-box;', // 用于样式显示
style_content_img_container: common_img_computer(new_data_content_style),
style_chunk_container: common_styles_computer(new_data_style) + 'box-sizing: border-box;', // 用于样式显示
style_chunk_img_container: common_img_computer(new_data_style),
style_chunk_width: width,
data_source_content_list: new_list,
data_source: !isEmpty(new_form.data_source)? new_form.data_source : '',
indicator_style: get_indicator_style(new_style), // 指示器的样式
indicator_location_style: get_indicator_location_style(new_style),
swiper_height: swiper_height,
swiper_width: swiper_width,
content_outer_spacing_magin: space_between + 'px',
gap_width: `width: calc(${100 / carousel_col}% - ${gap}px);`,
slides_per_view: new_style.rolling_fashion == 'translation' ? (new_form.data_source_direction != 'horizontal' ? col : new_form.data_source_carousel_col ) : 1,
is_show: this.get_is_show(new_form),
custom_group_field_id: new_form.data_source_field.id,
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, false, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_data_source_content_list(sourceList, form) {
if (!isEmpty(sourceList)) {
const data_source_id = form?.data_source_field.id || '';
let list = this.get_nested_property(sourceList, data_source_id);
// 如果是自定义标题,进一步处理嵌套对象中的数据
if (sourceList.data) {
list = this.get_nested_property(sourceList.data, data_source_id);
}
return list == '' ? [] : list;
} else {
return [];
}
},
get_nested_property(obj, path) {
// 检查路径参数是否为字符串且非空,若不满足条件则返回空字符串
if (typeof path !== 'string' || !path) return [];
// 将属性路径字符串拆分为属性键数组
const keys = path.split('.');
// 使用reduce方法遍历属性键数组逐层访问对象属性
// 如果当前对象存在且拥有下一个属性键,则继续访问;否则返回空字符串
return keys.reduce((o, key) => (o != null && o[key] != null ? o[key] : []), obj) ?? [];
},
get_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation' && form.data_source_direction != 'vertical') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.data_source_carousel_col;
// 存储数据显示
let nav_list = [];
// 拆分的数量
const split_num = Math.ceil(cloneList.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({
split_list: cloneList.slice(i * num, (i + 1) * num),
});
}
return nav_list;
} else {
// 否则的话,就返回全部的信息
return [
{
split_list: cloneList,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
slideChange(e) {
this.setData({
actived_index: e.detail.current,
});
},
url_event(e, index, split_index) {
if (this.data_source == 'goods' && this.data_source_content_list.length > 0) {
const list = this.data_source_content_list[index];
if (!isEmpty(list) && !isEmpty(list.split_list[split_index])) {
const new_list = list.split_list[split_index];
if (!isEmpty(new_list)) {
// 缓存商品数据
app.globalData.goods_data_cache_handle(new_list.data.id, new_list.data);
}
}
}
app.globalData.url_open(e);
},
},
};
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,161 @@
<template>
<view v-if="is_show" class="img-outer pr oh flex-row align-c wh-auto ht-auto" :style="com_style" @tap="url_event">
<iconfont :name="'icon-' + icon_class" :color="form.icon_color" :size="form.icon_size * scale + 'px'" propContainerDisplay="flex"></iconfont>
</view>
</template>
<script>
import { radius_computer, padding_computer, gradient_handle, isEmpty, get_nested_property, get_custom_link, get_is_eligible } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1,
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
com_style: '',
scale: 1,
icon_class: '',
icon_url: '',
is_show: true,
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
let icon_class_value = '';
if (!isEmpty(new_form.icon_class)) {
icon_class_value = new_form.icon_class;
} else {
if (!isEmpty(this.propSourceList)) {
let icon = '';
// 获取数据源ID
const data_source_id = !isEmpty(new_form?.data_source_field?.id || '') && this.propConfigLoop == '1' ? new_form.data_source_field.id : '';
// 数据源内容
const option = new_form?.data_source_field?.option || {};
if (data_source_id.includes(';')) {
const ids = data_source_id.split(';');
let url = '';
ids.forEach((item, index) => {
url += this.data_handling(item) + (index != ids.length - 1 ? (option?.join || '') : '');
});
icon = url;
} else {
// 不输入商品, 文章和品牌时,从外层处理数据
icon = this.data_handling(data_source_id);
}
icon_class_value = (option?.first || '') + icon + (option?.last || '');
} else {
icon_class_value = '';
}
}
this.setData({
form: new_form,
scale: this.propScale,
com_style: this.get_com_style(new_form, this.propScale),
icon_class: icon_class_value,
icon_url: this.get_icon_link(new_form),
is_show: this.get_is_show(new_form),
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_icon_link(new_form) {
let url = '';
if (!isEmpty(new_form.icon_link)) {
url = new_form.icon_link?.page || '';
} else {
// 获取数据源ID
const data_source_link_id = !isEmpty(new_form?.data_source_link_field?.id || '') && this.propConfigLoop == '1' ? new_form?.data_source_link_field?.id : '';
// 数据源内容
const source_link_option = new_form?.data_source_link_field?.option || {};
url = get_custom_link(data_source_link_id, this.propSourceList, source_link_option)
}
return url;
},
data_handling(data_source_id) {
// 不输入商品, 文章和品牌时,从外层处理数据
let icon = get_nested_property(this.propSourceList, data_source_id);
// 如果是商品,品牌,文章的图片, 其他的切换为从data中取数据
if (this.propIsCustom && !isEmpty(this.propSourceList.data)) {
icon = get_nested_property(this.propSourceList.data, data_source_id);
}
return icon;
},
get_com_style(form, scale) {
let style = `${ gradient_handle(form.color_list, form.direction) } ${ radius_computer(form.bg_radius, scale, true) };transform: rotate(${form.icon_rotate}deg);${ padding_computer(form.icon_padding, scale, true) };`;
if (form.border_show == '1') {
style += `border: ${form.border_size * scale }px ${form.border_style} ${form.border_color};box-sizing: border-box;`;
}
if (form.icon_location == 'center') {
style += `justify-content: center;`;
} else if (form.icon_location == 'left') {
style += `justify-content: flex-start;`;
} else if (form.icon_location == 'right') {
style += `justify-content: flex-end;`;
}
return style;
},
url_event(e) {
this.$emit('url_event', this.icon_url);
},
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,178 @@
<template>
<view v-if="is_show" class="img-outer pr wh-auto ht-auto" :style="border_style" @tap="url_event">
<imageEmpty :propImageSrc="img" :propStyle="image_style" propErrorStyle="width: 60rpx;height: 60rpx;"></imageEmpty>
</view>
</template>
<script>
import { percentage_count, radius_computer, isEmpty, get_nested_property, get_custom_link, get_is_eligible } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
export default {
components: {
imageEmpty,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propImgParams: {
type: String,
default: ''
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
img: '',
img_url: '',
image_style: '',
border_style: '',
keyMap: {
goods: 'images',
article: 'cover',
brand: 'logo'
},
is_show: true,
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
this.setData({
form: new_form,
img: this.get_img_url(new_form),
image_style: this.get_image_style(new_form, this.propScale),
border_style: this.get_border_style(new_form, this.propScale),
img_url: this.get_img_link(new_form),
is_show: this.get_is_show(new_form),
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_img_link(form) {
let url = '';
if (!isEmpty(form.link)) {
url = form.link?.page || '';
} else {
// 获取数据源ID
const data_source_link_id = !isEmpty(form?.data_source_link_field?.id || '') && this.propConfigLoop == '1' ? form.data_source_link_field.id : '';
// 数据源内容
const source_link_option = form?.data_source_link_field?.option || {};
url = get_custom_link(data_source_link_id, this.propSourceList, source_link_option)
}
return url;
},
get_img_url(form) {
if (!isEmpty(form.img[0])) {
return form.img[0];
} else {
if (!isEmpty(this.propSourceList)) {
let image_url = '';
// 获取数据源ID
const data_source_id = !isEmpty(form?.data_source_field?.id || '') && this.propConfigLoop == '1' ? form.data_source_field.id : '';
// 数据源内容
const option = form?.data_source_field?.option || {};
if (data_source_id.includes(';')) {
const ids = data_source_id.split(';');
let url = '';
ids.forEach((item, index) => {
url += this.data_handling(item) + (index != ids.length - 1 ? (option?.join || '') : '');
});
image_url = url;
} else {
image_url = this.data_handling(data_source_id);
}
return (option?.first || '') + image_url + (option?.last || '');
} else {
return '';
}
}
},
data_handling(data_source_id) {
// 不输入商品, 文章和品牌时,从外层处理数据
let image_url = get_nested_property(this.propSourceList, data_source_id);
// 如果是商品,品牌,文章的图片, 其他的切换为从data中取数据
if (this.propIsCustom && !isEmpty(this.propSourceList.data)) {
// 判断是否是同一标志
if (data_source_id == this.propImgParams) {
// 如果是符合条件的标志,先判断新的图片是否存在,存在就取新的图片,否则的话取原来的图片
image_url = !isEmpty(this.propSourceList.new_cover)? this.propSourceList.new_cover[0]?.url || '' : get_nested_property(this.propSourceList.data, data_source_id);
} else {
image_url = get_nested_property(this.propSourceList.data, data_source_id);
}
}
return image_url;
},
get_image_style(form, scale) {
return `width: ${percentage_count(form.img_width, form.com_width)}; height: ${percentage_count(form.img_height, form.com_height)};transform: rotate(${form.img_rotate}deg); ${radius_computer(form.img_radius, scale, true)};`;
},
get_border_style(form, scale) {
let style = ``;
if (form.border_show == '1') {
style += `border: ${form.border_size * scale }px ${form.border_style} ${form.border_color}; ${radius_computer(form.border_radius, scale, true)};box-sizing: border-box;`;
}
return style;
},
url_event(e) {
this.$emit('url_event', this.img_url);
},
},
};
</script>
<style lang="scss" scoped>
.img-outer {
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,94 @@
<template>
<view v-if="is_show" :style="border_style"></view>
</template>
<script>
import { get_is_eligible } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1,
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
border_style: '',
is_show: true,
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
this.setData({
form: new_form,
border_style: this.get_border_style(new_form, this.propScale),
is_show: this.get_is_show(new_form),
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_border_style(form, scale) {
if (form.line_settings === 'horizontal') {
return `margin: 10rpx 0;border-bottom: ${form.line_size * scale }px ${form.line_style} ${form.line_color};`;
} else {
return `margin: 0 10rpx;height:100%;border-right: ${form.line_size * scale }px ${form.line_style} ${form.line_color};`;
}
}
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,133 @@
<template>
<view v-if="is_show" class="wh-auto ht-auto re oh" :style="com_style" @tap="url_event">
<view class="wh-auto ht-auto" :style="com_img_style"></view>
</view>
</template>
<script>
import { radius_computer, background_computer, gradient_handle, isEmpty, get_custom_link, get_is_eligible } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
text_title: '',
text_style: '',
com_style: '',
panel_url: '',
is_show: true,
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
let url = '';
if (!isEmpty(new_form.link)) {
url = new_form.link?.page || '';
} else {
// 获取数据源ID
const data_source_link_id = !isEmpty(new_form?.data_source_link_field?.id || '') && this.propConfigLoop == '1' ? new_form.data_source_link_field.id : '';
// 数据源内容
const source_link_option = new_form?.data_source_link_field?.option || {};
url = get_custom_link(data_source_link_id, this.propSourceList, source_link_option);
}
this.setData({
form: new_form,
com_style: this.get_com_style(new_form, this.propScale),
com_img_style: this.get_com_img_style(new_form),
panel_url: url,
is_show: this.get_is_show(new_form),
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_com_style(form, scale) {
let style = `${ gradient_handle(form.color_list, form.direction) } ${radius_computer(form.bg_radius, scale, true)}; transform: rotate(${form.panel_rotate}deg);`;
if (form.border_show == '1') {
style += `border: ${form.border_size * scale }px ${form.border_style} ${form.border_color};box-sizing: border-box;`;
}
return style;
},
get_com_img_style(form) {
const data = {
background_img: form?.background_img || [],
background_img_style: form?.background_img_style || '2'
}
return background_computer(data);
},
url_event(e) {
this.$emit('url_event', this.panel_url);
},
},
};
</script>
<style lang="scss" scoped>
.break {
word-wrap: break-word;
word-break: break-all;
}
.rich-text-content {
white-space: normal;
word-break: break-all;
* {
max-width: 100%;
}
}
</style>

View File

@@ -0,0 +1,236 @@
<template>
<view v-if="is_show" class="img-outer wh-auto ht-auto re oh" :style="com_style" @tap="url_event">
<view :style="text_style" :class="'break ' + text_line_class">
<template v-if="form.is_rich_text == '1'">
<mp-html :content="text_title" />
</template>
<template v-else>
{{ text_title }}
</template>
</view>
</view>
</template>
<script>
import { radius_computer, padding_computer, isEmpty, gradient_handle, get_nested_property, get_custom_link, get_is_eligible, custom_condition_data } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propTitleParams: {
type: String,
default: 'name'
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
text_title: '',
text_style: '',
com_style: '',
text_url: '',
keyMap: {
goods: 'title',
article: 'title',
brand: 'name'
},
is_show: true,
text_line_class: ''
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
let url = '';
if (!isEmpty(new_form.text_link)) {
url = new_form.text_link?.page || '';
} else {
// 获取数据源ID
const data_source_link_id = !isEmpty(new_form?.data_source_link_field?.id || '') && this.propConfigLoop == '1' ? new_form.data_source_link_field.id : '';
// 数据源内容
const source_link_option = new_form?.data_source_link_field?.option || {};
// 调用方法处理数据显示
url = get_custom_link(data_source_link_id, this.propSourceList, source_link_option);
}
this.setData({
form: new_form,
text_title: (new_form?.text_captions || '') + this.get_text_title(new_form),
text_style: this.get_text_style(new_form, this.propScale),
com_style: this.get_com_style(new_form, this.propScale),
text_url: url,
is_show: this.get_is_show(new_form),
text_line_class: new_form.width_omit_num == '0' || new_form.is_rich_text == '1' ? '' : `text-line-${ new_form?.width_omit_num || '' }`
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_text_title(form) {
let text = '';
if (!isEmpty(form.text_title)) {
// 存储待处理的文本标题
let new_title = JSON.parse(JSON.stringify((form.text_title)));
let new_field_list = this.propFieldList;
// 判断是否是自定义组
if (this.propIsCustomGroup && !isEmpty(this.propCustomGroupFieldId)) {
// 取出对应自定义组的内容
const group_option_list = new_field_list.find((item) => item.field === this.propCustomGroupFieldId);
// 取出自定义组内部数据源参数的详细数据
new_field_list = group_option_list?.data || [];
}
// 遍历字段列表,替换文本标题中的占位符
if (!isEmpty(new_field_list)) {
new_field_list.forEach((item) => {
const new_field = '${' + item.field + '}';
if (form.text_title.includes(new_field)) {
// 获取到字段的真实数据
const field_value = custom_condition_data(item.field, item, this.propSourceList, this.propIsCustom);
// 使用正则表达式替换文本标题
const regular = new RegExp(`\\$\\{\\s*${item.field}\\s*\\}`, 'g');
// 替换后的内容赋值给原内容, 确保后续可以继续替换
new_title = new_title.replace(regular, field_value);
}
});
}
// 将内容替换为处理后的标题
text = new_title;
} else {
let text_title = '';
// 获取数据源ID
const data_source_id = !isEmpty(form?.data_source_field?.id || []) && this.propConfigLoop == '1' ? form?.data_source_field?.id : [];
// 数据源内容
const option = form?.data_source_field?.option || [];
// 多选判断
if (data_source_id.length > 0) {
text_title += form?.data_split?.left || '';
// 遍历取出所有的值
data_source_id.forEach((source_id, index) => {
const sourceList = option.find((item) => item.field == source_id);
// 根据数据源ID是否包含点号来区分处理方式
if (source_id.includes(';')) {
const ids = source_id.split(';');
let source_text = '';
ids.forEach((item, index) => {
source_text += this.data_handling(item) + (index != ids.length - 1 ? (sourceList?.join || '') : '');
});
text_title += (sourceList?.first || '') + source_text + (sourceList?.last || '');
} else {
text_title += (sourceList?.first || '') + this.data_handling(source_id) + (sourceList?.last || '');
}
if (index < data_source_id.length - 1) {
text_title += form?.data_split?.middle || '';
}
});
text_title += form?.data_split?.right || '';
}
// 如果是商品的标题或者是品牌的名称,需要判断是否有新的标题,没有的话就取原来的标题
text = text_title;
}
return text;
},
data_handling(data_source_id) {
let text = get_nested_property(this.propSourceList, data_source_id);
// 如果是商品的标题或者是品牌的名称,需要判断是否有新的标题,没有的话就取原来的标题
if (this.propIsCustom && !isEmpty(this.propSourceList.data)) {
// 其他的切换为从data中取数据
if (data_source_id == this.propTitleParams) {
// 如果是符合条件的标志,先判断新的标题是否存在,存在就取新的标题,否则的话取原来的标题
text = !isEmpty(this.propSourceList.new_title) ? this.propSourceList.new_title : get_nested_property(this.propSourceList.data, data_source_id);
} else {
text = get_nested_property(this.propSourceList.data, data_source_id);
}
}
return text;
},
get_text_style(form, scale) {
let style = `font-size: ${form.text_size * scale }px;line-height: ${ (typeof form.line_text_size === "number" ? form.line_text_size : form.text_size) * scale }px;color: ${form.text_color}; text-align: ${form.text_location}; transform: rotate(${form.text_rotate}deg);text-decoration: ${form.text_option};${padding_computer(form.text_padding, scale, true)};box-sizing: border-box;`;
if (form.text_weight == 'italic') {
style += `font-style: italic`;
} else if (['bold', '500'].includes(form.text_weight)) {
style += `font-weight: bold;`;
}
return style;
},
get_com_style(form, scale) {
let style = `${ gradient_handle(form.color_list, form.direction) } ${radius_computer(form.bg_radius, scale, true)}`;
if (form.border_show == '1') {
style += `border: ${form.border_size * scale }px ${form.border_style} ${form.border_color};box-sizing: border-box;`;
}
// 是富文本并且开启了上下滚动的开关
if (form.is_rich_text == '1' && form.is_up_down == '1') {
style += `overflow-y: auto;`;
}
return style;
},
url_event(e) {
this.$emit('url_event', this.text_url)
},
},
};
</script>
<style lang="scss" scoped>
.break {
word-wrap: break-word;
word-break: break-all;
}
.rich-text-content {
white-space: normal;
word-break: break-all;
* {
max-width: 100%;
}
}
</style>

View File

@@ -0,0 +1,304 @@
<template>
<view :style="style_content_container">
<view :style="style_content_img_container">
<template v-if="!isEmpty(form.data_source) && form.data_source_is_loop !== '0'">
<view v-if="data_source_content_list.length > 0 && form.data_source_direction == 'vertical'">
<view class="flex-row flex-wrap" :style="'row-gap:' + new_style.row_gap + 'px;column-gap:' + new_style.column_gap + 'px;'">
<view v-for="(item, index) in data_source_content_list" :key="index" class="ht-auto" :style="gap_width">
<view v-for="(item1, index1) in item.split_list" :key="index1">
<view :style="style_container">
<view class="wh-auto ht-auto oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propSourceType="form.data_source" :propDataHeight="form.height" :propScale="scale" :propDataIndex="index" :propDataSplitIndex="index1" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</view>
</view>
</view>
<view v-else-if="data_source_content_list.length > 0 && ['vertical-scroll', 'horizontal'].includes(form.data_source_direction)" class="oh pr">
<swiper class="w flex" circular="true" :vertical="form.data_source_direction != 'horizontal'" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_view" :style="{ width: '100%', height: swiper_height + 'px' }" @change="slideChange">
<swiper-item v-for="(item, index) in data_source_content_list" :key="index">
<view :class="form.data_source_direction != 'horizontal' ? '' : 'flex-row'" :style="form.data_source_direction == 'horizontal' ? 'column-gap:' + new_style.column_gap + 'px;' : ''">
<view v-for="(item1, index1) in item.split_list" :key="index1" :style="style_container + swiper_width + (form.data_source_direction == 'horizontal' ? gap_width : 'margin-bottom:' + content_outer_spacing_magin)">
<view class="wh-auto ht-auto oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propSourceType="form.data_source" :propDataHeight="form.height" :propScale="scale" :propDataIndex="index" :propDataSplitIndex="index1" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
<view v-else>
<view :style="style_container">
<view class="wh-auto ht-auto oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propConfigLoop="form.data_source_is_loop || '1'" :propDataHeight="form.height" :propScale="scale" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</template>
<view v-else-if="!isEmpty(form.data_source) && form.data_source_is_loop == '0'">
<view :style="style_container">
<view class="w h oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propSourceList="{}" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propSourceType="form.data_source" :propDataHeight="form.height" :propScale="scale" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
<view v-else>
<view :style="style_container">
<view class="wh-auto ht-auto oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propConfigLoop="form.data_source_is_loop || '1'" :propDataHeight="form.height" :propScale="scale" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { padding_computer, isEmpty, margin_computer, gradient_computer, radius_computer, background_computer, common_styles_computer, common_img_computer, border_width, box_shadow_computer, border_computer, old_border_and_box_shadow, old_margin, old_padding, old_radius } from '@/common/js/common/common.js';
import dataRendering from '@/components/diy/modules/custom/data-rendering.vue';
const app = getApp();
export default {
components: {
dataRendering
},
props: {
propKey: {
type: String,
default: '',
},
propValue: {
type: Object,
default: () => {
return {};
},
},
propMagicScale: {
type: Number,
default: 1,
},
propDataSpacing: {
type: Number,
default: 0,
},
propDataIndex: {
type: Number,
default: 0,
},
},
data() {
return {
form: {},
new_style: {},
scale: 1,
style_container: '',
style_img_container: '',
div_width: 0,
div_height: 0,
custom_list_length: 0,
data_source_content_list: [],
data_source: '',
// 一屏显示的数量
slides_per_view: 1,
// 轮播高度
swiper_height: 0,
swiper_width: 'width: 100%;',
show_data: { data_key: 'id', data_name: 'name' },
gap_width: '',
content_outer_spacing_magin: '0rpx',
defalt_style: {
color_list: [{ color: '', color_percentage: undefined }],
direction: '180deg',
background_img_style: '2',
background_img: [],
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
},
style_content_container: '',
style_content_img_container: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
computed: {
get_percentage_count() {
return (num) => {
return num * this.scale + 'px';
};
},
},
mounted() {
this.init();
},
methods: {
isEmpty,
init() {
if (!isEmpty(this.propValue)) {
const new_form = this.propValue.data_content;
const new_style = this.propValue.data_style;
const { data_common_style = {}, data_color_list = [], data_direction = '180deg', data_radius = old_radius, data_background_img = [], data_background_img_style = '2', data_chunk_padding = old_padding, data_chunk_margin = old_margin, data_content_style = {}, data_pattern = old_border_and_box_shadow } = new_style;
const style_data = {
color_list: data_color_list,
direction: data_direction,
}
const style_img_data = {
background_img: data_background_img,
background_img_style: data_background_img_style,
}
// 数据来源的内容
let list = [];
if (new_form.is_custom_data == '1') {
if (Number(new_form.data_source_content.data_type) === 0) {
list = new_form.data_source_content.data_list;
} else {
list = new_form.data_source_content.data_auto_list.map(item => ({
id: Math.random(),
new_cover: [],
new_title: '',
data: item,
}));
}
} else {
list = new_form.data_source_content.data_list;
}
const new_list = list.length > 0 ? this.get_list(list, new_form, new_style) : [];
// 计算宽度
const { padding_left, padding_right, padding_top, padding_bottom } = data_chunk_padding;
const { margin_left, margin_right, margin_bottom, margin_top } = data_chunk_margin;
const old_width = new_form.width * this.propMagicScale;
// 数据宽度
const data_style = padding_left + padding_right + margin_left + margin_right + border_width(data_pattern);
// 通用样式
const chunk_padding = new_style?.chunk_padding || old_padding;
const chunk_margin = new_style?.chunk_margin || old_margin;
const common_styles = (chunk_margin?.margin_left || 0) + (chunk_margin?.margin_right || 0) + (chunk_padding?.padding_left || 0) + (chunk_padding?.padding_right || 0) + border_width(data_common_style);
// 内容左右间距
const content_spacing = (data_content_style?.margin_left || 0) + (data_content_style?.margin_right || 0) + (data_content_style?.padding_left || 0) + (data_content_style?.padding_right || 0) + border_width(data_content_style);
const carousel_col = Number(new_form.data_source_carousel_col);
// 数据间距
const data_spacing = ['vertical', 'horizontal'].includes(new_form.data_source_direction) ? new_style.column_gap * (carousel_col - 1) : 0;
// 当前容器的宽度 减去 左右两边的padding值 再减去 数据间距的一半 再除以 容器的宽度 得到比例 再乘以数据魔方的比例
const width = old_width - data_style - content_spacing - common_styles - data_spacing - (this.propDataSpacing / 2);
// 计算缩放比例
// 比例增加最小值判断
const scale_number = width / new_form.width;
const new_scale = scale_number > 0 ? scale_number : 0;
// 间距
const space_between = new_form.data_source_direction == 'horizontal' ? new_style.column_gap : new_style.row_gap;
// 判断是平移还是整屏滚动
let swiper_height = 0;
// 商品数量大于列数的时候,高度是列数,否则是当前的数量
const col = new_list.length > carousel_col ? carousel_col : new_list.length;
// 轮播图高度控制
if (new_form.data_source_direction == 'horizontal') {
swiper_height = new_form.height * new_scale + padding_top + padding_bottom + margin_bottom + margin_top;
} else {
swiper_height = (new_form.height * new_scale + padding_top + padding_bottom + margin_bottom + margin_top) * col + ((carousel_col - 1) * space_between);
}
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.column_gap * (carousel_col - 1)) / carousel_col;
// 横向的时候,根据选择的行数和每行显示的个数来区分具体是显示多少个
const swiper_width = (new_form.data_source_direction == 'horizontal' && new_style.rolling_fashion != 'translation') ? `width: ${ 100 / carousel_col }%;`: 'width: 100%;';
const content_style = !isEmpty(new_style.data_content_style)? new_style.data_content_style : this.defalt_style;
this.setData({
form: new_form,
new_style: new_style,
div_width: width,
scale: new_scale,
custom_list_length: new_form.custom_list.length - 1,
style_content_container: common_styles_computer(content_style),
style_content_img_container: common_img_computer(content_style),
style_container: gradient_computer(style_data) + radius_computer(data_radius) + margin_computer(data_chunk_margin) + box_shadow_computer(data_pattern) + border_computer(data_pattern), // 用于样式显示
style_img_container: padding_computer(data_chunk_padding) + background_computer(style_img_data) + 'box-sizing: border-box;',
data_source_content_list: new_list,
data_source: !isEmpty(new_form.data_source)? new_form.data_source : '',
slides_per_view: new_style.rolling_fashion == 'translation' ? (new_form.data_source_direction != 'horizontal' ? col : carousel_col) : 1,
swiper_height: swiper_height,
swiper_width: swiper_width,
show_data: new_form?.show_data || { data_key: 'id', data_name: 'name' },
content_outer_spacing_magin: space_between + 'px',
gap_width: `width: calc(${100 / carousel_col}% - ${gap}px);`,
});
}
},
get_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation' && form.data_source_direction != 'vertical') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.data_source_carousel_col;
// 存储数据显示
let nav_list = [];
// 拆分的数量
const split_num = Math.ceil(cloneList.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({
split_list: cloneList.slice(i * num, (i + 1) * num),
});
}
return nav_list;
} else {
// 否则的话,就返回全部的信息
return [
{
split_list: cloneList,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
slideChange(e) {
this.$emit('onCarouselChange', e.detail.current, this.propDataIndex);
},
url_event(e, index, split_index) {
if (this.form.data_source == 'goods' && this.data_source_content_list.length > 0) {
const list = this.data_source_content_list[index];
if (!isEmpty(list) && !isEmpty(list.split_list[split_index])) {
const new_list = list.split_list[split_index];
if (!isEmpty(new_list)) {
// 缓存商品数据
app.globalData.goods_data_cache_handle(new_list.data.id, new_list.data);
}
}
}
app.globalData.url_open(e);
},
},
};
</script>
<style scoped lang="scss">
.main-content {
position: absolute;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,142 @@
<template>
<view class="wh-auto ht-auto oh" :style="style_container">
<view class="pr oh wh-auto ht-auto" :style="style_img_container">
<swiper circular="true" :autoplay="propValue.data_style.is_roll == '1'" :interval="propValue.data_style.interval_time * 1000" :duration="500" :vertical="propValue.data_style.rotation_direction == 'vertical'" :next-margin="next_margin" :display-multiple-items="slides_per_view" class="swiper" style="height: 100%" @change="carousel_change">
<swiper-item v-for="(item1, index1) in propValue.data_content.list" :key="index1">
<template v-if="propType === 'img'">
<view class="wh-auto ht-auto" :data-value="item1.carousel_link.page" @tap="url_event">
<imageEmpty :propImageSrc="item1.carousel_img[0]" :propStyle="propValue.data_style.get_img_radius" :propImgFit="propValue.data_content.fit" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<template v-else>
<view class="wh-auto ht-auto" :style="shop_spacing">
<product-list-show :propKey="propKey" :propOuterflex="propValue.data_content.goods_outerflex" :propFlex="propValue.data_content.goods_flex" :propNum="show_num" :propActived="propActived" :propIsShow="propValue.data_content.is_show" :propChunkPadding="propValue.data_style.chunk_padding" :propValue="item1.split_list" :propGoodStyle="propGoodStyle" :propContentImgRadius="propValue.data_style.get_img_radius" @url_event="url_event"></product-list-show>
</view>
</template>
</swiper-item>
</swiper>
</view>
</view>
</template>
<script>
import { gradient_computer, radius_computer, padding_computer, background_computer, isEmpty, border_computer, box_shadow_computer, old_border_and_box_shadow, old_margin, old_radius, old_padding, margin_computer } from "@/common/js/common/common.js";
const app = getApp();
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import productListShow from '@/components/diy/modules/data-magic/product-list-show.vue';
export default {
components: {
imageEmpty,
productListShow,
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propContentImgRadius: {
type: String,
default: () => '',
},
propType: {
type: String,
default: () => '',
},
propActived: {
type: Number,
default: () => 0,
},
propGoodStyle: {
type: Object,
default: () => {},
},
propDataIndex: {
type: Number,
default: () => 0,
},
propKey: {
type: [String, Number],
default: '',
},
},
data() {
return {
style_container: '',
style_img_container: '',
slides_per_view: 1,
show_num: 1,
shop_spacing: '',
next_margin: '0rpx',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
init() {
if (!isEmpty(this.propValue)) {
const { data_color_list = [], data_direction = '180deg', data_chunk_margin = old_margin, data_radius = old_radius, data_pattern = old_border_and_box_shadow, data_background_img = [], data_background_img_style = '2', data_chunk_padding = old_padding } = this.propValue.data_style;
const style_data = {
color_list: data_color_list,
direction: data_direction,
}
const style_img_data = {
background_img: data_background_img,
background_img_style: data_background_img_style,
}
let slides_per_view = 1;
let show_num = 1;
// 商品时的处理逻辑
const { goods_outerflex, goods_num } = this.propValue.data_content;
const { rotation_direction, rolling_fashion, data_goods_gap } = this.propValue.data_style;
// 图片时的处理
if (this.propType === 'img') {
slides_per_view = 1; // 能够同时显示的slides数量
} else {
// 判断是平移还是整屏滚动, 平移的时候是一个为1组如果是整屏滚动就为一屏为一组
if (rolling_fashion == 'translation') {
// 如果是商品是横排的,轮播也是横排的,就不对商品进行拆分/如果商品是竖排的,轮播也是竖排的,不对商品进行拆分
if ((goods_outerflex == 'row' && rotation_direction == 'horizontal') || (goods_outerflex == 'col' && rotation_direction == 'vertical')) {
slides_per_view = goods_num; // 能够同时显示的slides数量
show_num = 1; // 一屏显示的数量 用于商品内部处理显示
} else {
slides_per_view = 1; // 能够同时显示的slides数量
show_num = goods_num; // 一屏显示的数量 用于商品内部处理显示
}
} else {
// 切屏的时候为多个为一组
show_num = goods_num; // 一屏显示的数量 用于商品内部处理显示
slides_per_view = 1; // 能够同时显示的slides数量
}
}
this.setData({
style_container: gradient_computer(style_data) + radius_computer(data_radius) + margin_computer(data_chunk_margin) + box_shadow_computer(data_pattern) + border_computer(data_pattern), // 用于样式显示
style_img_container: padding_computer(data_chunk_padding) + background_computer(style_img_data) + 'box-sizing: border-box;',
next_margin: rolling_fashion == 'translation' && rotation_direction == 'horizontal' ? `-${ data_goods_gap * 2 }rpx` : '0rpx',
shop_spacing: this.propType === 'img' ? 'margin-right: 0px;' : `margin-right: ${ data_goods_gap * 2 }rpx;`,
slides_per_view: slides_per_view,
show_num: show_num,
});
} else {
return '';
}
},
carousel_change(e) {
this.$emit('onCarouselChange', e.detail.current, this.propDataIndex);
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style></style>

View File

@@ -0,0 +1,233 @@
<template>
<view :class="['align-c flex-1 w h', (propOuterflex == 'row' ? 'flex-row' : 'flex-col')]" :style="'gap:' + propGoodStyle.data_goods_gap + 'px;'">
<template v-if="propFlex === 'row'">
<view v-for="(item, index) in propValue" :key="index" :style="block_size" class="w h">
<view class="w h oh" :style="style_container">
<view class="w h flex-row gap-10" :style="style_img_container" :data-index="index" :data-value="item.goods_url" @tap="url_event">
<template v-if="!isEmpty(item.new_cover)">
<view class="w h">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<template v-else>
<view class="w h">
<imageEmpty :propImageSrc="item.images" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<view v-if="!isEmpty(propIsShow)" class="flex-col w h tl jc-sb">
<view v-if="propIsShow.includes('title')" class="text-line-2" :style="propGoodStyle.goods_title_style + 'height:'+ ((propGoodStyle.goods_title_size + 3) * 4) + 'rpx;'">{{ item.title || '' }}</view>
<view v-if="propIsShow.includes('price')" :style="propGoodStyle.goods_price_style">
<text :style="propGoodStyle.goods_price_symbol_style">{{ item.show_price_symbol || '' }}</text>
{{ item.min_price || '' }}
<template v-if="propIsShow.includes('price_unit')">
<text :style="propGoodStyle.goods_price_unit_style">{{ item.show_price_unit || '' }}</text>
</template>
</view>
</view>
</view>
</view>
</view>
</template>
<template v-else-if="propFlex === 'col_price_float'">
<view v-for="(item, index) in propValue" :key="index" :style="block_size" class="w h">
<view class="w h oh" :style="style_container">
<view class="w h flex-col gap-10" :style="style_img_container" :data-index="index" :data-value="item.goods_url" @tap="url_event">
<view class="w h flex-1 pr oh">
<template v-if="!isEmpty(item.new_cover)">
<view class="w h">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<template v-else>
<view class="w h">
<imageEmpty :propImageSrc="item.images" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<view v-if="propIsShow.includes('price')" class="pa" :style="propGoodStyle.goods_price_style + float_pirce_style">
<text :style="propGoodStyle.goods_price_symbol_style">{{ item.show_price_symbol || ''}}</text>{{ item.min_price || ''}}
<template v-if="propIsShow.includes('price_unit')">
<text :style="propGoodStyle.goods_price_unit_style">{{ item.show_price_unit || ''}}</text>
</template>
</view>
</view>
<view v-if="propIsShow.includes('title')" class="text-line-1 tl w" :style="propGoodStyle.goods_title_style + 'height:'+ ((propGoodStyle.goods_title_size + 3) * 2) + 'rpx;'">{{ item.title || '' }}</view>
</view>
</view>
</view>
</template>
<template v-else>
<view v-for="(item, index) in propValue" :key="index" :style="block_size" class="w h">
<view class="w h oh" :style="style_container">
<view class="w h flex-col" :style="style_img_container" :data-index="index" :data-value="item.goods_url" @tap="url_event">
<template v-if="!isEmpty(item.new_cover)">
<view class="w h">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<template v-else>
<view class="w h">
<imageEmpty :propImageSrc="item.images" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<view v-if="!isEmpty(propIsShow)" class="flex-col w h tl jc-sb">
<view v-if="propIsShow.includes('title')" class="text-line-2" :style="propGoodStyle.goods_title_style + 'height:'+ ((propGoodStyle.goods_title_size + 3) * 4) + 'rpx;'">{{ item.title || '' }}</view>
<view v-if="propIsShow.includes('price')" :style="propGoodStyle.goods_price_style">
<text :style="propGoodStyle.goods_price_symbol_style">{{ item.show_price_symbol || ''}}</text>{{ item.min_price || '' }}
<template v-if="propIsShow.includes('price_unit')">
<text :style="propGoodStyle.goods_price_unit_style">{{ item.show_price_unit || ''}}</text>
</template>
</view>
</view>
</view>
</view>
</view>
</template>
</view>
</template>
<script>
const app = getApp();
import { gradient_computer, radius_computer, padding_computer, background_computer, isEmpty, margin_computer, box_shadow_computer, border_computer, old_margin, old_border_and_box_shadow, old_padding } from "@/common/js/common/common.js";
import imageEmpty from '@/components/diy/modules/image-empty.vue';
export default {
components: {
imageEmpty,
},
props: {
propValue: {
type: Array,
default: () => [],
},
propOuterflex: {
type: String,
default: () => '',
},
propFlex: {
type: String,
default: () => '',
},
propContentImgRadius: {
type: String,
default: () => '',
},
propNum: {
type: Number,
default: () => 0,
},
propActived: {
type: Number,
default: () => 0,
},
propIsShow: {
type: Array,
default: () => [],
},
propChunkPadding: {
type: Object,
default: () => {},
},
propGoodStyle: {
type: Object,
default: () => {},
},
propKey: {
type: [String, Number],
default: '',
},
},
data() {
return {
style_container: '',
style_img_container: '',
block_size: '',
float_pirce_style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
computed: {
img_padding_computer() {
if (!isEmpty(this.propChunkPadding)) {
return padding_computer(this.propChunkPadding) + ';box-sizing: border-box;';
} else {
return '';
}
},
},
methods: {
isEmpty,
init() {
if (!isEmpty(this.propGoodStyle)) {
const { goods_color_list = [], goods_direction = '180deg', goods_radius = old_radius, goods_background_img = [], goods_background_img_style = '2', goods_chunk_padding = old_padding, goods_price_color_list = [], goods_price_direction = '180deg', goods_price_radius = old_radius, goods_price_padding = old_padding, goods_price_margin = old_margin, goods_price_location = 'center', goods_chunk_margin = old_margin} = this.propGoodStyle;
const style_data = {
color_list: goods_color_list,
direction: goods_direction,
}
const style_img_data = {
background_img: goods_background_img,
background_img_style: goods_background_img_style,
}
const data = {
color_list: goods_price_color_list,
direction: goods_price_direction,
}
let location = 'left:50%;transform:translateX(-50%);bottom:0;'
if (goods_price_location == 'left') {
location = 'left:0;bottom:0;';
} else if (goods_price_location == 'right') {
location = 'right:0;bottom:0;';
}
// 左右间距
const shop_left_right_width_margin = goods_chunk_margin?.margin_left || 0 + goods_chunk_margin?.margin_right || 0;
// 上下间距
const shop_top_bottom_width_margin = goods_chunk_margin?.margin_top || 0 + goods_chunk_margin?.margin_bottom || 0;
// 内容间距
const total_gap = this.propGoodStyle.data_goods_gap * (this.propValue.length - 1);
// 总的宽度
const all_width = total_gap + (shop_left_right_width_margin.value * this.propNum);
// 总的高度
const all_height = total_gap + (shop_top_bottom_width_margin.value * this.propNum);
this.setData({
float_pirce_style: gradient_computer(data) + radius_computer(goods_price_radius) + padding_computer(goods_price_padding) + margin_computer(goods_price_margin) + location,
style_container: gradient_computer(style_data) + radius_computer(goods_radius) + margin_computer(goods_chunk_margin) + border_computer(this.propGoodStyle) + box_shadow_computer(this.propGoodStyle), // 用于样式显示
style_img_container: this.propFlex == 'col' ? background_computer(style_img_data) : padding_computer(goods_chunk_padding) + background_computer(style_img_data) + 'box-sizing: border-box;',
block_size: this.propOuterflex == 'row' ? 'height:calc(100% - ' + shop_top_bottom_width_margin.value + 'px);width:calc((100% - ' + all_width + 'px) / ' + this.propNum + ');' : 'width:calc(100% - ' + shop_left_right_width_margin.value + 'px);height:calc((100% - ' + all_height + 'px) / ' + this.propNum + ');',
});
} else {
return '';
}
},
url_event(e) {
// 存储数据显示缓存
let index = e.currentTarget.dataset.index || 0;
let goods = this.propValue[index];
app.globalData.goods_data_cache_handle(goods.id, goods);
this.$emit('url_event', e);
},
},
};
</script>
<style scoped lang="scss">
.w {
width: 100%;
}
.h {
height: 100%;
}
.half-width {
width: 50%;
}
.gap-20 {
gap: 40rpx;
}
</style>

View File

@@ -0,0 +1,57 @@
<template>
<!-- 视频 -->
<view class="video pr wh-auto ht-auto">
<video :src="video" class="wh-auto ht-auto" :poster="video_img" objectFit="cover" :style="'object-fit: cover;' + video_style"></video>
</view>
</template>
<script>
import { padding_computer, radius_computer } from '@/common/js/common/common.js';
export default {
props: {
propKey: {
type: [String, Number],
default: '',
},
propValue: {
type: Object,
default: () => ({}),
},
propDataStyle: {
type: Object,
default: () => ({}),
}
},
data() {
return {
style_container: '',
video_img: '',
video: '',
video_style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue || {};
// 视频比例
this.setData({
video_img: new_content.video_img.length > 0 ? new_content.video_img[0].url : '',
video: new_content.video.length > 0 ? new_content.video[0].url : '',
video_style: radius_computer(this.propDataStyle.img_radius),
});
}
},
};
</script>
<style></style>

View File

@@ -0,0 +1,79 @@
<template>
<view :class="['oh img_wh', propClass]" :style="empty_outer_style + propStyle">
<image :src="img_url" :mode="propImgFit" :style="empty_style + 'display: block;'" />
</view>
</template>
<script>
import { is_obj, isEmpty } from '@/common/js/common/common.js';
export default {
props: {
propImageSrc: {
type: [Object, String],
default: () => {},
},
propErrorStyle: {
type: String,
default: () => '',
},
propImgFit: {
type: String,
default: () => 'aspectFill',
},
propStyle: {
type: String,
default: () => '',
},
propClass: {
type: String,
default: () => '',
},
},
data() {
return {
empty_outer_style: '',
empty_style: 'width: 100%; height: 100%;', // 有图片的时候显示为100%
img_url: '',
default_image: '/static/images/common/image-empty.png',
};
},
watch: {
propImageSrc(val) {
this.init();
}
},
mounted() {
this.init();
},
methods: {
init() {
let img_url = this.propImageSrc;
if (is_obj(this.propImageSrc)) {
img_url = !isEmpty(this.propImageSrc) ? this.propImageSrc.url : '';
}
// 没有图片的时候根据默认值来算
if (img_url == undefined || img_url == null || img_url == '') {
this.setData({
empty_outer_style: 'background: #f4fcff;display:flex;align-items: center;justify-content: center;',
empty_style: `${this.propErrorStyle}`,
});
img_url = this.default_image;
} else {
this.setData({
empty_outer_style: '',
empty_style: 'width: 100%; height: 100%;', // 有图片的时候显示为100%
});
}
this.setData({
img_url: img_url,
});
},
},
};
</script>
<style lang="scss" scoped>
.img_wh {
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<view v-if="form.seckill_subscript_show == '1'" class="corner-marker" :style="corner_marker">
<view class="flex-row nowrap" :style="corner_img_marker">
<template v-if="form.subscript_type == 'img-icon'">
<template v-if="!isEmpty(form.subscript_img_src)">
<image :src="form.subscript_img_src[0].url" mode="aspectFill" :style="img_style" />
</template>
<template v-else>
<iconfont :name="'icon-' + form.subscript_icon_class" propContainerDisplay="flex" :size="new_style.subscript_style.text_or_icon_size * 2 + 'rpx'" :color="new_style.subscript_style.text_or_icon_color"></iconfont>
</template>
</template>
<template v-else>
<span class="text-line-1" :style="text_size">{{ form.subscript_text }}</span>
</template>
</view>
</view>
</template>
<script>
import { common_img_computer, common_styles_computer, isEmpty } from '@/common/js/common/common.js';
import iconfont from '@/components/iconfont/iconfont';
export default {
components: {
iconfont,
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propType: {
type: String,
default: 'outer',
},
},
data() {
return {
form: {},
new_style: {},
corner_img_marker: '',
img_style: '',
text_size: '',
};
},
created() {
this.init();
},
methods: {
isEmpty,
// 初始化数据
init() {
if (!isEmpty(this.propValue)) {
const new_content = this.propValue.content || {};
const new_style = this.propType == 'outer' ? this.propValue.style || {} : { subscript_style: this.propValue.style || {} };
if (!isEmpty(new_style.subscript_style)) {
// 视频比例
this.setData({
form: new_content,
new_style: new_style,
corner_marker: this.get_corner_marker(new_style),
text_size: `font-size: ${ new_style.subscript_style.text_or_icon_size * 2 }rpx;color: ${ new_style.subscript_style.text_or_icon_color };`,
corner_img_marker: common_img_computer(new_style.subscript_style),
img_style: `height: ${new_style.subscript_style.img_height * 2}rpx; width: ${new_style.subscript_style.img_width * 2}rpx`,
});
}
}
},
get_corner_marker(new_style) {
const { subscript_style } = new_style;
let location = common_styles_computer(subscript_style);
// 获取内部的显示数据
const { seckill_subscript_location, top_or_bottom_spacing, left_or_right_spacing } = subscript_style;
// 圆角根据图片的圆角来计算 对角线是同样的圆角
if (seckill_subscript_location == 'top-left') {
location += `top: ${ top_or_bottom_spacing * 2 }rpx;left: ${ left_or_right_spacing * 2 }rpx;`;
} else if (seckill_subscript_location == 'top-center') {
location += 'top: 0;left: 50%;transform: translateX(-50%);';
} else if (seckill_subscript_location == 'top-right') {
location += `top: ${ top_or_bottom_spacing * 2 }rpx;right:${ left_or_right_spacing * 2 }rpx;`;
} else if (seckill_subscript_location == 'bottom-left') {
location += `bottom: ${ top_or_bottom_spacing * 2 }rpx;left: ${ left_or_right_spacing * 2 }rpx;`;
} else if (seckill_subscript_location == 'bottom-center') {
location += 'bottom: 0;left: 50%;transform: translateX(-50%);';
} else if (seckill_subscript_location == 'bottom-right') {
location += `bottom: ${ top_or_bottom_spacing * 2 }rpx;right: ${ left_or_right_spacing * 2 }rpx;`;
}
return location;
},
},
};
</script>
<style>
.corner-marker {
position: absolute;
max-width: 100%;
}
</style>

View File

@@ -0,0 +1,592 @@
<template>
<!-- 66rpx是自定义顶部导航栏的高度-->
<view class="tabs-view" :style="tabs_sticky">
<view class="tabs-view" :style="propStyle + propTabsBackground">
<view class="pr" :style="propsTabsContainer">
<view v-if="propIsRotatingBackground" class="pa top-0 wh-auto ht-auto" :style="propBgImgStyle"></view>
<view class="flex-row gap-10 jc-sb align-c" :style="propsTabsImgContainer">
<view class="tabs flex-1 flex-width">
<scroll-view :scroll-x="true" :show-scrollbar="false" :scroll-with-animation="tabs_list_is_sliding_fixed" :scroll-left="scroll_left" :class="'wh-auto interior-area-' + propKey">
<view :class="'flex-row ' + flex_class" :style="'height:' + tabs_height + ';width:' + tabs_scroll_width + 'px;'">
<view v-for="(item, index) in tabs_list" :key="index" :class="'item nowrap flex-col jc-c align-c gap-4 scroll-item-' + propKey + ' ' + tabs_theme + (index == active_index ? ' active' : '') + ((tabs_theme_index == '0' && tabs_theme_1_style) || tabs_theme_index == '1' || tabs_theme_index == '2' ? ' pb-0' : '')" :style="'flex:0 0 auto;padding-left:' + (index == 0 ? '0' : tabs_spacing) + 'rpx;padding-right:' + (index + 1 == tabs_list.length ? '0' : tabs_spacing) + 'rpx;' + get_item_style(item.is_sliding_fixed)" :data-index="index" @tap="handle_event">
<view class="nowrap ma-auto">
<view v-if="tabs_theme_index == '4'" :class="'img oh pr z-i-deep ' + (!isEmpty(item.img) ? 'img-no-empty' : '')" :style="tabs_theme_style.tabs_top_img">
<imageEmpty :propImageSrc="item.img[0]" propImgFit="aspectFit" propErrorStyle="width: 20rpx;height: 20rpx;"></imageEmpty>
<!-- <image :src="item.img[0].url" class="img" mode="aspectFit" /> -->
</view>
<template v-if="item.tabs_type == '1'">
<template v-if="!isEmpty(item.tabs_icon)">
<view :class="tabs_theme_index == '4' ? 'title pr z-i' : 'title pr z-i-deep'" :style="(tabs_theme_index == '4' ? tabs_sign_spacing : '') + (index == active_index ? (['2', '4'].includes(tabs_theme_index) ? tabs_check : '') : '' + tabs_padding_bottom)">
<iconfont :name="'icon-' + item.tabs_icon" :color="index == active_index ? tabs_icon_checked_color : tabs_icon_color" propContainerDisplay="flex" :size="index == active_index ? tabs_icon_checked_size : tabs_icon_size"></iconfont>
</view>
</template>
<template v-else>
<view :class="tabs_theme_index == '4' ? 'title pr z-i' : 'title pr z-i-deep'" :style="(tabs_theme_index == '4' ? tabs_sign_spacing : '') + index == active_index ? new_style.is_tabs_img_background == '1' && ['2', '4'].includes(tabs_theme_index) ? tabs_check : '' : tabs_padding_bottom">
<imageEmpty :propImageSrc="item.tabs_img[0]" :propStyle="index == active_index ? tabs_theme_style.tabs_img_checked : tabs_theme_style.tabs_img" propImgFit="heightFix" propErrorStyle="width: 40rpx;height: 40rpx;"></imageEmpty>
</view>
</template>
</template>
<template v-else>
<view :class="tabs_theme_index == '4' ? 'title pr z-i' : 'title pr z-i-deep'" :style="(tabs_theme_index == '4' ? tabs_sign_spacing : '') + (index == active_index ? ['2', '4'].includes(tabs_theme_index) ? tabs_theme_style.tabs_title_checked + tabs_check : tabs_theme_style.tabs_title_checked : tabs_theme_style.tabs_title + tabs_padding_bottom)">{{ item.title }}</view>
</template>
<view class="desc pr z-i wh-auto" :style="tabs_sign_spacing + (tabs_theme_index == '1' && index == active_index ? tabs_check : '')">{{ item.desc }}</view>
<template v-if="tabs_theme_index == '3' && index == active_index">
<template v-if="!isEmpty(form.tabs_adorn_icon)">
<view class="icon pr z-i wh-auto flex-row jc-c align-c" :style="tabs_sign_spacing">
<iconfont :name="'icon-' + form.tabs_adorn_icon" :style="icon_tabs_check" propContainerDisplay="flex" :size="tabs_adorn_icon_size"></iconfont>
</view>
</template>
<template v-else>
<view class="pr z-i wh-auto flex-row jc-c align-c ma-auto" :style="tabs_sign_spacing">
<imageEmpty :propImageSrc="form.tabs_adorn_img[0]" :propStyle="tabs_adorn_img_style" propImgFit="aspectFit" propErrorStyle="width: 20rpx;height: 20rpx;"></imageEmpty>
</view>
</template>
</template>
<view class="bottom_line z-i" :class="tabs_bottom_line_theme" :style="tabs_check + tabs_sign_spacing"></view>
</view>
</view>
</view>
</scroll-view>
</view>
<view v-if="propIsTabsIcon && form.show_more !== '0'" :style="tabs_padding_bottom">
<iconfont :name="'icon-' + icon.more_icon_class || 'category-more'" :size="icon.more_icon_size + '' || '14'" :color="icon.more_icon_color || '#000'" propContainerDisplay="flex" @click="category_check_event"></iconfont>
</view>
</view>
</view>
</view>
<!-- 选项卡更多弹窗 -->
<componentPopup :propShow="popup_status" :propIsBar="propIsBar" propPosition="top" :propMask="true" :propTop="newPropTop" :propStyle="newPropStyle" @onclose="quick_close_event">
<view :class="'padding-bottom-lg ' + (['toutiao', 'app', 'h5'].includes(platform) ? 'padding-top-lg' : 'padding-top')">
<view class="padding-left-main padding-bottom-main">全部选项卡</view>
<view class="divider-b">
<view class="nav-list-more">
<view class="flex-row flex-wrap align-c">
<view v-for="(item, index) in tabs_list" :key="index" class="item tc cr-base cp text-size-xs" :data-index="index" @tap="handle_event">
<view class="dis-inline-block padding-vertical-xs padding-horizontal round" :class="active_index == index ? 'bg-main border-color-main cr-white' : ''">
{{ item.title }}
</view>
</view>
</view>
</view>
</view>
<view class="tc padding-top-lg flex-row jc-c align-c" @tap="quick_close_event">
<text class="padding-right-sm">{{ $t('nav-more.nav-more.h9g4b1') }}</text>
<iconfont name="icon-arrow-top" color="#ccc" propContainerDisplay="flex"></iconfont>
</view>
</view>
</componentPopup>
</view>
</template>
<script>
const app = getApp();
import { gradient_computer, isEmpty, radius_computer } from '@/common/js/common/common.js';
import componentPopup from '@/components/popup/popup';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
components: {
componentPopup,
imageEmpty,
},
props: {
propValue: {
type: Object,
default: () => {},
},
// 是否需要icon
propIsTabsIcon: {
type: Boolean,
default: false,
},
// 是否需要粘性定位置顶
propIsTop: {
type: Boolean,
default: false,
},
// 指定样式
propStyle: {
type: String,
default: '',
},
// 层级
propZIndex: {
type: Number,
default: 11,
},
// 自定义导航栏高度
propCustomNavHeight: {
type: String,
default: '66rpx',
},
// tabs背景色
propTabsBackground: {
type: String,
default: 'transparent',
},
// 置顶距离顶部高度
propTop: {
type: [String, Number],
default: '0',
},
propsTabsContainer: {
type: String,
default: ''
},
propsTabsImgContainer: {
type: String,
default: '',
},
propIsRotatingBackground: {
type: Boolean,
default: false,
},
propBgImgStyle: {
type: String,
default: '',
},
propKey: {
type: [String, Number],
default: '',
},
propTabsSlidingFixedBg: {
type: String,
default: ''
}
},
data() {
return {
form: {},
new_style: {},
tabs_theme_index: '',
tabs_theme: '',
tabs_check: '',
icon_tabs_check: '',
tabs_spacing: '',
tabs_sign_spacing: '',
tabs_padding_bottom: '',
tabs_list: [],
active_index: 0,
tabs_theme_style: {
tabs_title_checked: '',
tabs_title: '',
tabs_img_checked: '',
tabs_img: '',
tabs_top_img: ''
},
tabs_icon_checked_size: '',
tabs_icon_size: '',
tabs_icon_checked_color: '',
tabs_icon_color: '',
icon: {
more_icon_class: '',
more_icon_size: '',
more_icon_color: '',
},
// 过滤弹窗
popup_status: false,
propIsBar: false,
tabs_bottom_line_theme: '',
tabs_sticky: '',
tabs_height: '100%',
tabs_adorn_img_style: '',
tabs_width: 0,
tabs_scroll_width: 0,
// #ifdef MP
sticky_top: bar_height,
// #endif
// #ifdef H5 || MP-TOUTIAO
sticky_top: bar_height,
// #endif
// #ifdef APP
sticky_top: bar_height,
// #endif
newPropTop: '',
newPropStyle: '',
platform: app.globalData.application_client_type(),
is_out_of_range: false,
tabs_list_is_sliding_fixed: true,
scroll_left: 0,
tabs_adorn_icon_size: '0rpx',
// 默认数据
old_radius: { radius: 0, radius_top_left: 0, radius_top_right: 0, radius_bottom_left: 0, radius_bottom_right: 0 },
old_padding: { padding: 0, padding_top: 0, padding_bottom: 0, padding_left: 0, padding_right: 0 },
old_margin: { margin: 0, margin_top: 10, margin_bottom: 0, margin_left: 0, margin_right: 0 },
};
},
computed: {
flex_class() {
if (this.is_out_of_range) {
return this.form.justification == 'center' ? 'jc-c' : this.form.justification == 'right' ? 'jc-e': '';
} else {
return '';
}
},
get_item_style() {
return (val) => {
return val == '1' ? `${ this.propTabsSlidingFixedBg };position: sticky;left: 0;z-index: 11;` : ''
}
}
},
watch: {
propValue() {
this.init();
},
propIsTop: {
deep: true,
immediate: true,
handler(new_val) {
if (new_val) {
this.tabs_sticky = 'position: sticky;top: calc(' + parseInt(this.propTop) + 'px + ' + this.propCustomNavHeight + ');z-index: ' + this.propZIndex + ';';
} else {
this.tabs_sticky = '';
}
},
},
propTop(new_val, old_val) {
if (this.propIsTop) {
this.tabs_sticky = 'position: sticky;top: calc(' + parseInt(this.propTop) + 'px + ' + this.propCustomNavHeight + ');z-index: ' + this.propZIndex + ';';
} else {
this.tabs_sticky = '';
}
},
},
mounted() {
this.init();
},
methods: {
// 判断是否为空
isEmpty,
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
const new_tabs_check = this.tabs_check_computer(new_style);
const new_icon = {
more_icon_class: new_style.more_icon_class,
more_icon_size: new_style.more_icon_size,
more_icon_color: new_style.more_icon_color,
};
const tabs_top_img_height = new_style?.tabs_top_img_height || 39;
const tabs_top_img_radius = new_style?.tabs_top_img_radius || { radius: 100, radius_top_left: 100, radius_top_right: 100, radius_bottom_left: 100, radius_bottom_right: 100}
// 标题样式
const new_tabs_theme_style = {
tabs_title_checked: `font-weight: ${new_style.tabs_weight_checked};font-size: ${new_style.tabs_size_checked * 2}rpx;line-height: ${new_style.tabs_size_checked * 2}rpx;color:${new_style.tabs_color_checked};`,
tabs_title: `font-weight: ${new_style.tabs_weight};font-size: ${new_style.tabs_size * 2}rpx;line-height: ${new_style.tabs_size * 2}rpx;color:${new_style.tabs_color};`,
tabs_img_checked: `height: ${(new_style?.tabs_img_height || 0) * 2}rpx;` + radius_computer(new_style?.tabs_img_radius || this.old_radius),
tabs_img: `height: ${(new_style?.tabs_img_height || 0) * 2}rpx;` + radius_computer(new_style?.tabs_img_radius || this.old_radius),
tabs_top_img: `height: ${tabs_top_img_height * 2 }rpx;width: ${tabs_top_img_height * 2 }rpx;box-sizing: border-box;` + radius_computer(tabs_top_img_radius),
};
const { tabs_size_checked, tabs_size, tabs_icon_size_checked = 0, tabs_icon_size = 0, tabs_img_height = 0, tabs_sign_spacing = 0 } = new_style || {};
let default_height = 0;
if (new_content.tabs_theme == '2') {
default_height = 12; // 选中的时候,风格二的内间距
} else if (new_content.tabs_theme == '4') {
// const top_index = new_content?.tabs_list?.findIndex((item) => !isEmpty(item.img)) ?? -1;
// default_height = 4 + (top_index > -1 ? tabs_top_img_height + tabs_sign_spacing : 0); // 选中的时候,风格二的内间距 加上上边图片的大小和上边图片之间的间距
default_height = 4 + tabs_top_img_height + tabs_sign_spacing;
}
// 筛选出所有的icon
const is_icon = new_content?.tabs_list?.findIndex((item) => item.tabs_type === '1' && !isEmpty(item.tabs_icon)) ?? -1;
// 如果有icon则取选中的icon大小和未选中的icon大小取最大值作为图标的高度
let icon_height = 0;
if (is_icon > -1) {
icon_height = Math.max(tabs_icon_size_checked + default_height, tabs_icon_size);
}
// 筛选出所有的图片, 没有选择图标的时候默认是图片
const is_img = new_content?.tabs_list?.findIndex((item) => item.tabs_type === '1' && isEmpty(item.tabs_icon)) ?? -1;
// 选项卡高度 五个值,作为判断依据,因为图片没有未选中的大小设置,所以高度判断的时候只取选中的高度, 其余的icon和标题都分别取选中和未选中的大小对比取出最大的值作为选项卡的高度避免选项卡切换时会出现抖动问题
const height = Math.max(tabs_size_checked + default_height, tabs_size, icon_height, is_img > -1 ? (tabs_img_height + default_height) : '');
const findIndex = new_content.tabs_list.findIndex(item => item.is_sliding_fixed == '1');
// 参数设置
this.setData({
form: new_content,
tabs_list_is_sliding_fixed: findIndex == -1,
newPropTop: `calc(${ this.sticky_top * 2}rpx);`,
newPropStyle: `padding-top: ${ this.sticky_top * 2 }rpx;margin-top: -${ this.sticky_top * 2 }rpx;`,
new_style: new_style,
tabs_spacing: Number(new_style.tabs_spacing),
tabs_sign_spacing: !isEmpty(new_style.tabs_sign_spacing) ? `margin-top: ${new_style.tabs_sign_spacing * 2}rpx;` : 'margin-top: 8rpx;',
tabs_list: new_content.tabs_list,
tabs_padding_bottom: this.get_padding_bottom(new_content, new_style) + 'z-index: 11;',
// 选项卡主题
tabs_theme: this.get_tabs_theme(new_content),
tabs_theme_index: new_content.tabs_theme,
// 选项卡样式
tabs_check: new_tabs_check,
icon_tabs_check: `${new_tabs_check};line-height: 1;background-clip: text;-webkit-background-clip: text;-webkit-text-fill-color: transparent;`,
icon: new_icon,
tabs_icon_checked_size: (new_style?.tabs_icon_size_checked || 0) * 2 + 'rpx',
tabs_icon_size: (new_style?.tabs_icon_size || 0) * 2 + 'rpx',
tabs_icon_checked_color: new_style?.tabs_icon_color_checked || '',
tabs_icon_color: new_style?.tabs_icon_color || '',
tabs_theme_style: new_tabs_theme_style,
tabs_bottom_line_theme: new_style.tabs_one_theme == '1' ? 'tabs-bottom-line-theme' : '',
tabs_theme_1_style: new_style.tabs_one_theme == '1',
tabs_height: ['2', '4'].includes(new_content.tabs_theme) ? height * 2 + 'rpx' : '100%;',
tabs_adorn_img_style: this.get_tabs_adorn_img_style(new_style),
tabs_adorn_icon_size: (new_style?.tabs_adorn_icon_size || 0) * 2 + 'rpx',
});
// 只有居中居右的才重新获取dom判断
// if (['center', 'right'].includes(this.form.justification)) {
setTimeout(() => {
const query = uni.createSelectorQuery().in(this);
query.select('.interior-area-' + this.propKey)
.fields({ size: true, scrollOffset: true},(res) => {
if ((res || null) != null) {
const { scrollWidth, width } = res;
this.setData({
is_out_of_range: scrollWidth <= width,
tabs_scroll_width: scrollWidth,
tabs_width: width
});
}
})
.exec();
}, 0)
// }
},
get_tabs_adorn_img_style(new_style) {
return radius_computer(new_style?.tabs_adorn_img_radius || this.old_radius) + `height: ${(new_style?.tabs_adorn_img_height || 10) * 2}rpx;${ new_style.is_tabs_adorn_img_background == '1' ? tabs_check.value : ''}`;
},
// 获取选项卡主题
get_tabs_theme(data) {
let arr = {
1: 'tabs-style-2',
2: 'tabs-style-3',
3: 'tabs-style-4',
4: 'tabs-style-5',
};
let value = arr[data.tabs_theme];
return value === undefined ? 'tabs-style-1' : value;
},
get_padding_bottom(form, new_style) {
let bottom = 0;
if (form.tabs_theme == '0') {
if (new_style.tabs_one_theme == '1') {
bottom = 6;
} else {
bottom = 3;
}
} else if (form.tabs_theme == '3') {
bottom = !isEmpty(form.tabs_adorn_icon) ? new_style.tabs_adorn_icon_size : new_style.tabs_adorn_img_height;
}
const tabs_sign_spacing = !isEmpty(new_style.tabs_sign_spacing) ? new_style.tabs_sign_spacing : 4;
return ['1', '2', '4'].includes(form.tabs_theme) ? '' : `padding-bottom: ${(tabs_sign_spacing + bottom) * 2 }rpx;`;
},
// 选中的背景渐变色样式
tabs_check_computer(data) {
const new_gradient_params = {
color_list: data.tabs_checked,
direction: data.tabs_direction,
};
return gradient_computer(new_gradient_params);
},
// 事件
// tabs切换事件
handle_event(e) {
const index = e.currentTarget.dataset.index;
const tabs_list_item = this.tabs_list[index];
this.setData({
active_index: index,
popup_status: false,
});
this.set_scoll_left(index);
this.$emit('onTabsTap', index, tabs_list_item);
setTimeout(() => {
this.$emit('tabsZindex', 11)
}, 200)
},
// 将选中的内容定位到中间
set_scoll_left(index) {
const query = uni.createSelectorQuery().in(this);
query.selectAll(`.scroll-item-` + this.propKey)
.boundingClientRect((rect) => {
const tabs_index = this.form.tabs_list.findIndex(item => item.is_sliding_fixed == '1');
// 如果第一个悬浮了就取第二个的left加上 第一个的宽度和left
let new_width = tabs_index == 0 && index != 0 ? rect[1].left - rect[0].width - rect[0].left : rect[0].left;
// 如果悬浮的不是第一个并且选中的是悬浮的内容
if (index > 0 && tabs_index == index) {
new_width = rect[0].left - rect[index + 1].left + rect[index].width;
}
const scrollLeft =
rect[index].left +
rect[index].width / 2 -
this.tabs_width / 2 -
new_width
this.setData({
scroll_left: scrollLeft,
});
})
.exec();
},
// 分类选择事件
category_check_event() {
this.setData({
popup_status: true,
});
setTimeout(() => {
this.$emit('tabsZindex', 13)
}, 0)
},
// 关闭分类选择事件
quick_close_event(e) {
this.setData({
popup_status: false,
});
setTimeout(() => {
this.$emit('tabsZindex', 11)
}, 200)
},
},
};
</script>
<style lang="scss" scoped>
.tabs {
.item {
// padding: 0 0 10rpx 0;
position: relative;
&:first-of-type {
margin-left: 0;
}
&:last-of-type {
margin-right: 0;
}
.title {
font-size: 28rpx;
text-align: center;
}
.desc {
font-size: 22rpx;
color: #999;
text-align: center;
display: none;
}
.bottom_line {
width: 100%;
height: 6rpx;
border-radius: 20rpx;
background-color: red;
// position: absolute;
left: 0;
right: 0;
bottom: 0;
display: none;
}
.icon {
// position: absolute;
bottom: 0;
text-align: center;
font-size: 40rpx;
line-height: 20rpx !important;
display: none;
}
.img {
// width: 78rpx;
// height: 78rpx;
border-radius: 100%;
border: 2rpx solid transparent;
display: none;
margin: 0 auto;
}
&.tabs-style-1 {
&.active {
.bottom_line {
display: block;
}
}
.tabs-bottom-line-theme {
opacity: 0.6;
bottom: 0;
z-index: 0;
height: 14rpx;
border-radius: 0;
// left: 12%;
}
}
&.tabs-style-2 {
&.active {
.desc {
background: #ff5e5e;
color: #fff;
}
}
.desc {
border-radius: 40rpx;
padding: 4rpx 12rpx;
box-sizing: border-box;
display: block;
}
}
&.tabs-style-3 {
&.active {
.title {
// background: #ff2222;
border-radius: 40rpx;
padding: 6px 12px;
box-sizing: border-box;
color: #fff;
}
}
}
&.tabs-style-4 {
// padding-bottom: 28rpx;
&.active {
.title {
color: #ff2222;
}
.icon {
color: #ff2222;
display: flex;
}
}
}
&.tabs-style-5 {
align-items: center;
&.active {
.title {
// font-size: 22rpx;
// background: #ff5e5e;
border-radius: 40rpx;
padding: 2px 7px;
box-sizing: border-box;
// color: #fff;
}
.img {
border-color: #ff5e5e;
}
}
.img-no-empty {
width: 100% !important;
}
.img {
display: block;
}
}
}
}
.nav-list-more {
width: 100%;
max-height: 550rpx;
padding-bottom: 20rpx;
overflow-y: auto;
}
.nav-list-more .item {
width: 20%;
padding: 20rpx 0;
}
.pb-14 {
padding-bottom: 28rpx;
}
.pb-0 {
padding-bottom: 0 !important;
}
.ma-auto {
margin: auto;
}
</style>

View File

@@ -0,0 +1,248 @@
<template>
<view :style="style_container">
<view class="pr" :style="style_img_container">
<swiper class="swiper" circular="true" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_group" :style="{ height: new_height }" @change="slideChange">
<swiper-item v-for="(item, index) in nav_content_list" :key="index" class="swiper-item">
<view class="banner-img flex-row flex-wrap wh-auto" :class="'banner-img-' + propKey" :style="space">
<view v-for="(item1, index1) in item.split_list" :key="index1" class="flex-col gap-10 align-c" :style="group_width + nav_title_space" :data-value="item1.link.page" @tap="url_open_event">
<view v-if="['image_with_text', 'image'].includes(nav_style)" class="flex-row align-c jc-c pr">
<view class="top-img" :style="img_size">
<imageEmpty :propImageSrc="item1.img[0]" :propStyle="img_style" propErrorStyle="width: 60rpx;height: 60rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="item1.subscript" propType="nav-group"></subscriptIndex>
</view>
<view v-if="['image_with_text', 'text'].includes(nav_style)" class="wh-auto size-12 ma-0 nowrap oh tc" :style="text_style">{{ item1.title }}</view>
</view>
</view>
</swiper-item>
</swiper>
<view v-if="form.display_style == 'slide' && new_style.is_show == '1'" :class="['left', 'right'].includes(new_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="indicator_location_style">
<block v-if="new_style.indicator_style == 'num'">
<view :style="indicator_style" class="dot-item">
<text :style="{ color: new_style.actived_color }">{{ actived_index + 1 }}</text>
<text>/{{ nav_content_list.length }}</text>
</view>
</block>
<block v-else>
<view v-for="(item, index) in nav_content_list" :key="index" :style="indicator_style + (actived_index == index ? 'background:' + new_style.actived_color : '')" class="dot-item" />
</block>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { isEmpty, common_styles_computer, common_img_computer, radius_computer, padding_computer, get_indicator_style, get_indicator_location_style } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import subscriptIndex from '@/components/diy/modules/subscript/index.vue';
export default {
components: {
imageEmpty,
subscriptIndex,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
form: {},
new_style: {},
style_container: '',
style_img_container: '',
img_style: '',
text_style: '',
indicator_style: '',
new_height: '300rpx',
actived_index: 0,
group_width: '',
nav_content_list: [],
nav_title_space: 'row-gap:20rpx', // 导航标题间距
space: 'row-gap:20rpx', // 导航间距
img_size: '72rpx',
slides_per_group: 1,
indicator_location_style: '',
subscriptStyle: {
seckill_subscript_location: 'top-left',
text_or_icon_color: '#fff',
text_or_icon_size: 12,
img_width: 20,
img_height: 20,
direction: '90deg',
top_or_bottom_spacing: 0,
left_or_right_spacing: 0,
color_list: [{ color: '#FF7607', color_percentage: undefined }],
background_img_style: '2',
background_img: [],
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
radius: 4,
radius_top_left: 4,
radius_top_right: 4,
radius_bottom_left: 4,
radius_bottom_right: 4,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
box_shadow_color: '',
box_shadow_x: 0,
box_shadow_y: 0,
box_shadow_blur: 0,
box_shadow_spread: 0,
}
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
init() {
const new_content = this.propValue.content;
const new_style = this.propValue.style;
let group = 1;
let group_width = `width: ${100 / (new_content.single_line || 4)}%;`;
// 判断是否是轮播图
if (new_content?.display_style == 'slide') {
if (new_content.row == 1 && new_style.rolling_fashion == 'translation') {
group = new_content.single_line || 4;
group_width = 'width: 100%;';
} else {
group = 1;
group_width = `width: ${100 / (new_content.single_line || 4)}%;`;
}
}
this.setData({
form: new_content,
new_style: new_style,
style_container: common_styles_computer(new_style.common_style), // 用于样式显示
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
img_style: radius_computer(new_style), // 图片的设置
text_style: `font-size: ${new_style.title_size * 2 || 24}rpx; color: ${new_style.title_color || '#000'};`, // 标题的样式
indicator_style: get_indicator_style(new_style), // 指示器的样式
indicator_location_style: get_indicator_location_style(new_style), // 指示器位置处理
actived_color: new_style.actived_color || '#2A94FF', // 轮播图显示样式
slides_per_group: group, // 每个轮播图显示的个数
group_width: group_width, // 每个导航所占位置
nav_title_space: 'row-gap:' + (new_style.title_space || 0) * 2 + 'rpx', // 导航标题间距
space: 'row-gap:' + (new_style.space || 0) * 2 + 'rpx;' + padding_computer(new_style.data_padding), // 导航间距
img_size: 'width:' + (new_style.img_size || 0) * 2 + 'rpx;height:' + (new_style.img_size || 0) * 2 + 'rpx;', // 图片大小
nav_style: new_content.nav_style || 'image_with_text', // 是否显示文字和图片
nav_content_list: this.get_nav_content_list(new_content, new_style),
});
setTimeout(() => {
const query = uni.createSelectorQuery().in(this);
// 选择我们想要的元素
query
.select('.banner-img-' + this.propKey)
.boundingClientRect((res) => {
if ((res || null) != null) {
// data包含元素的宽度、高度等信息
this.setData({
new_height: res.height + 'px',
});
}
})
.exec(); // 执行查询
}, 50);
},
get_nav_content_list(data, new_style) {
// 深拷贝一下,确保不会出现问题
const list = JSON.parse(JSON.stringify(data.nav_content_list || Array(4))).map(item => ({
...item,
// 角标配置
subscript: isEmpty(item.subscript) ?
{
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: this.subscriptStyle,
} : item.subscript,
}));
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (list.length > 0 && data.display_style == 'slide') {
// 存储数据显示
let nav_list = [];
if (data.row == 1 && new_style.rolling_fashion == 'translation') {
// 拆分的数量
list.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
} else {
// 每页显示的数量
const num = (data.single_line || 4) * (data.row || 1);
// 拆分的数量
const split_num = Math.ceil(list.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({
split_list: list.slice(i * num, (i + 1) * num),
});
}
return nav_list;
}
} else {
// 否则的话,就返回全部的信息
return [
{
split_list: list,
},
];
}
},
slideChange(e) {
this.setData({
actived_index: e.detail.current,
});
},
url_open_event(link) {
app.globalData.url_event(link);
},
},
};
</script>
<style scoped lang="scss">
.top-img {
height: 72rpx;
width: 72rpx;
border-radius: 8rpx;
}
.gap-x-10 {
row-gap: 20rpx;
}
</style>

211
components/diy/notice.vue Normal file
View File

@@ -0,0 +1,211 @@
<template>
<!-- 公告 -->
<view :style="style_container">
<view :style="style_img_container">
<template v-if="form_content.notice_style == 'inherit'">
<view class="news-box" :style="container_background_style + container_height">
<view class="flex-row align-c gap-8" :style="container_background_img_style">
<template v-if="form_content.title_type == 'img-icon'">
<view v-if="form_content.icon_class">
<iconfont :name="'icon-' + form_content.icon_class" :size="form_style.icon_size * 2 + 'rpx'" :color="form_style.icon_color" propContainerDisplay="flex"></iconfont>
</view>
<view v-else>
<image v-if="form_content.img_src.length > 0" :src="form_content.img_src[0].url" class="border-radius-sm dis-block" mode="aspectFill" :style="img_style"></image>
</view>
</template>
<template v-else>
<view :style="title_style" class="padding-horizontal-sm border-radius-sm">{{ form_content.title || '公告' }}</view>
</template>
<swiper class="swiper flex-1" circular :indicator-dots="false" :autoplay="true" :interval="interval_time" :vertical="direction_type == 'vertical'" :style="container_height">
<swiper-item v-for="(item, index) in notice_list" :key="index">
<view class="swiper-item flex-row align-c ht-auto" :style="content_title_style + 'color:' + form_style.news_color" :data-value="item.notice_link.page" @tap="url_event">
<view class="text-line-1">{{ item.notice_title }}</view>
</view>
</swiper-item>
</swiper>
<view v-if="form_content.is_right_button == '1'" class="flex-row align-c" :style="'color: ' + form_style.right_button_color + ';font-size:' + form_style.right_button_size * 2 + 'rpx;'" :data-value="form_content.more_link.page" @tap="url_event">
{{ form_content.right_title }}
<view class="pr">
<iconfont name="icon-arrow-right" :color="form_style.right_button_color || '#999'" :size="form_style.right_button_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
</view>
</template>
<template v-else>
<view class="news-card" :style="container_background_style">
<view class="flex-col gap-10" :style="container_background_img_style">
<view class="flex-row wh-auto jc-sb align-c">
<template v-if="form_content.title_type == 'img-icon'">
<template v-if="form_content.icon_class">
<iconfont :name="'icon-' + form_content.icon_class" :size="form_style.icon_size * 2 + 'rpx'" :color="form_style.icon_color" propContainerDisplay="flex"></iconfont>
</template>
<template v-else>
<image v-if="form_content.img_src.length > 0" :src="form_content.img_src[0].url" class="border-radius-sm dis-block" mode="aspectFill" :style="img_style"></image>
</template>
</template>
<template v-else>
<view :style="title_style" class="padding-horizontal-sm border-radius-sm">{{ form_content.title }}</view>
</template>
<view v-if="form_content.is_right_button == '1'" class="flex-row align-c" :style="'color: ' + form_style.right_button_color + ';font-size:' + form_style.right_button_size * 2 + 'rpx;'" :data-value="form_content.more_link.page" @tap="url_event">
{{ form_content.right_title }}
<view class="pr">
<iconfont name="icon-arrow-right" :color="form_style.right_button_color || '#999'" :size="form_style.right_button_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
<view v-for="(item, index) in notice_list" :key="index" class="flex-row" :style="content_title_style" :data-value="item.notice_link.page" @tap="url_event">
<view class="num" :class="'one' + (index + 1)">{{ index + 1 }}</view>
<view class="break" :style="'color:' + form_style.news_color">{{ item.notice_title }}</view>
</view>
</view>
</view>
</template>
</view>
</view>
</template>
<script>
const app = getApp();
import { background_computer, common_styles_computer, common_img_computer, gradient_computer, gradient_handle, radius_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
form_content: {},
form_style: {},
style_container: '',
style_img_container: '',
// 容器高度
container_height: '',
// 容器背景
container_background_style: '',
// 图片设置
img_style: '',
// 标题的设置
title_style: '',
// 内容标题设置
content_title_style: '',
// 指示器的样式
// 轮播图定时显示
interval_time: 2000,
// 轮播图滚动方向 // vertical' | 'horizontal
direction_type: 'vertical',
// 记录当前显示的轮播图的数据
interval_list: {
time: 2000,
direction: 'vertical',
notice_length: 1,
},
// 公告数据
notice_list: [],
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
// 容器背景
const { container_color_list, container_direction, container_background_img_style, container_background_img } = new_style;
const temp_obj = {
color_list: container_color_list,
direction: container_direction,
background_img: container_background_img,
background_img_style: container_background_img_style,
};
const temp_container_background_style = gradient_computer(temp_obj) + radius_computer(new_style.container_radius) + `overflow:hidden;`;
const temp_container_background_img_style = background_computer(temp_obj);
// 标题渐变色处理
const gradient = gradient_handle(new_style.title_color_list, '90deg');
const time = (new_style.interval_time || 2) * 1000;
const direction = new_content.direction;
const new_notice_list = new_content.notice_list.filter((item) => item.is_show == '1');
// 判断长度是否相等
const notice_length = new_notice_list.length;
const new_interval_list = {
time: time,
direction: direction,
notice_length: notice_length,
};
this.setData({
form_content: new_content,
form_style: new_style,
container_height: 'height:' + new_style.container_height * 2 + 'rpx',
container_background_style: temp_container_background_style,
container_background_img_style: temp_container_background_img_style,
img_style: `height: ${new_style.title_height * 2}rpx; width: ${new_style.title_width * 2}rpx`,
title_style: `color:${new_style.title_color}; font-size: ${new_style.title_size * 2}rpx; font-weight: ${new_style.title_typeface}; ${gradient}`,
content_title_style: `font-size: ${new_style.news_size * 2}rpx; font-weight: ${new_style.news_typeface};`,
notice_list: new_notice_list,
interval_time: time,
direction_type: direction,
interval_list: new_interval_list,
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style lang="scss" scoped>
.news-box {
overflow: hidden;
padding: 0 20rpx;
background: #fff;
}
.news-card {
padding: 30rpx;
background: #fff;
}
.num {
padding-right: 14rpx;
color: #999;
}
.one1 {
color: #ea3323;
}
.one2 {
color: #ff7303;
}
.one3 {
color: #ffc300;
}
.two-style {
width: 48rpx;
height: 48rpx;
}
.break {
word-break: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
}
</style>

View File

@@ -0,0 +1,73 @@
<template>
<!-- 富文本 -->
<view class="diy-rich-text" :style="style_container">
<view :style="style_img_container">
<mp-html :content="content" />
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
content: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
this.setData({
content: new_content.html,
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
},
};
</script>
<style>
.diy-rich-text {
* {
max-width: 100%;
}
/* #ifdef H5 */
[id^=v] {
width: 100%;
}
/* #endif */
}
.diy-rich-text video {
width: 100%;
}
</style>

327
components/diy/search.vue Normal file
View File

@@ -0,0 +1,327 @@
<template>
<view :style="style_container">
<view :style="style_img_container">
<view class="search wh-auto pr">
<view class="box oh flex-row align-c" :style="box_style">
<view v-if="form.positioning_name_float == '1' && propSearchType == 'header'" :style="propLocationMargin">
<component-choice-location propType="search" :propLocationContainerStyle="propLocationContainerStyle" :propLocationImgContainerStyle="propLocationImgContainerStyle" :propBaseColor="propBaseColor" :propTextDefaultName="form.positioning_name" :propIsLeftIconArrow="form.is_location_left_icon_show == '1'" :propLeftImgValue="form.location_left_img" :propLeftIconValue="'icon-' + form.location_left_icon" :propIconLocationSize="propIconLocationSize" :propIconArrowSize="propIconArrowSize" :propIsRightIconArrow="form.is_location_right_icon_show == '1'" :propRightImgValue="form.location_right_img" :propRightIconValue="'icon-' + form.location_right_icon" :propTextMaxWidth="['4'].includes(form.theme) ? '300rpx' : '200rpx'" propContainerDisplay="flex" @onBack.stop="choice_location_back"></component-choice-location>
</view>
<view :class="'oh flex-1 ht-auto flex-row align-c gap-10' + (form.is_center == '1' ? ' tips-float' : '')" @tap.stop="search_tap">
<view v-if="form.is_icon_show == '1'" class="pr">
<view class="search-icon-before" :style="(form.positioning_name_float == '1' && propSearchType == 'header') || form.is_center == '1' ? '' : 'left: -' + (new_style.search_padding_left ? new_style.search_padding_left * 2 : 30) + 'rpx;'" @tap.stop="search_icon_tap"></view>
<view class="wh-auto ht-auto">
<template v-if="form.icon_img.length > 0">
<view class="img-box">
<image :src="form.icon_img[0].url" class="img" mode="heightFix"></image>
</view>
</template>
<template v-else>
<view>
<iconfont :name="!isEmpty(form.icon_class) ? 'icon-' + form.icon_class : 'icon-search-max'" size="28rpx" :color="new_style.icon_color" propContainerDisplay="flex"></iconfont>
</view>
</template>
</view>
</view>
<view v-if="!isEmpty(form.hot_word_list) && form.is_hot_word_show == '1'" :style="form.is_center == '1' ? `min-width:100px;` : 'width:100%;'">
<swiper circular="true" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :vertical="true" :duration="500" class="swiper_style" @change="slideChange">
<swiper-item v-for="(item, index) in form.hot_word_list" :key="index">
<view class="flex-row align-c wh-auto ht-auto" :style="{ color: !isEmpty(item.color) ? item.color : !isEmpty(new_style.hot_words_color) ? new_style.hot_words_color : '#999' }" :data-value="item.value" @tap.stop="serch_event">
{{ item.value }}
</view>
</swiper-item>
</swiper>
</view>
<template v-else>
<text v-if="form.is_tips_show == '1'" :class="[propIsPageSettings ? 'text-size-xs text-line-1' : 'text-size-md text-line-1']" :style="'color:' + new_style.tips_color">{{ form.tips }}</text>
</template>
</view>
</view>
<view class="flex-row align-c pa search-outer-botton oh" :style="right_icon_style">
<view v-if="form.is_right_icon_show == '1' && (form.right_icon_img.length > 0 || !isEmpty(form.right_icon_class))" class="pr margin-right-main">
<view class="search-icon-before" @tap.stop="search_right_icon_tap"></view>
<view class="wh-auto ht-auto">
<template v-if="form.right_icon_img.length > 0">
<view class="img-box right_icon_height flex-row align-c">
<image :src="form.right_icon_img[0].url" class="img" mode="heightFix"></image>
</view>
</template>
<template v-else>
<view class="right_icon_height flex-row align-c">
<iconfont :name="!isEmpty(form.right_icon_class) ? 'icon-' + form.right_icon_class : 'icon-search-max'" size="28rpx" :color="new_style.right_icon_color" propContainerDisplay="flex"></iconfont>
</view>
</template>
</view>
</view>
<view v-if="form.is_search_show == '1'" class="flex-row align-c jc-c">
<view class="search-botton flex-row align-c jc-c z-i" :style="search_button_style" @tap.stop="serch_button_event">
<view class="oh" :style="search_button_img_style">
<template v-if="form.search_type === 'text'">
<view class="text-size-xs">{{ form.search_tips }}</view>
</template>
<template v-else-if="!isEmpty(form.search_botton_img) && form.search_botton_img.length > 0">
<image :src="form.search_botton_img[0].url" class="img" :style="search_button_height" mode="heightFix"></image>
</template>
<template v-else>
<view class="text-size-xs">
<iconfont :name="!isEmpty(form.search_botton_icon) ? 'icon-' + form.search_botton_icon : ''" size="28rpx" propContainerDisplay="flex"></iconfont>
</view>
</template>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import componentChoiceLocation from '@/components/choice-location/choice-location';
const app = getApp();
import { background_computer, common_styles_computer, common_img_computer, gradient_computer, radius_computer, isEmpty, padding_computer, old_padding, old_radius, margin_computer } from '@/common/js/common/common.js';
export default {
components: {
componentChoiceLocation,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propIsPageSettings: {
type: Boolean,
default: false,
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propSearchType: {
type: String,
default: 'search',
},
propBaseColor: {
type: String,
default: '',
},
propLocationMargin: {
type: String,
default: '',
},
propLocationContainerStyle: {
type: String,
default: '',
},
propLocationImgContainerStyle: {
type: String,
default: '',
},
propIconLocationSize: {
type: String,
default: '',
},
propIconArrowSize: {
type: String,
default: '',
},
},
data() {
return {
search_content: '',
form: {},
new_style: {},
style: '',
style_container: '',
style_img_container: '',
search_button_radius: '',
box_style: '',
keywords: '',
right_icon_style: '',
search_button_style: '',
search_button_img_style: '',
search_button_height: '',
button_padding: { padding: 0, padding_bottom: 3, padding_left: 12, padding_right: 12, padding_top: 3 },
button_margin: { margin: 0, margin_bottom: 2, margin_left: 0, margin_right: 2, margin_top: 2 },
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
const { search_button_radius, common_style } = new_style;
this.setData({
form: new_form,
new_style: new_style,
search_button_img_style: this.get_search_button_img_style(new_style),
search_button_height: this.get_search_button_height(new_style),
// style: this.get_style(new_style), // 内部样式
style_container: this.propIsPageSettings ? '' : common_styles_computer(common_style), // 全局样式
style_img_container: this.propIsPageSettings ? '' : common_img_computer(common_style, this.propIndex),
search_button_radius: radius_computer(search_button_radius), // 按钮圆角
box_style: this.get_box_style(new_style, new_form), // 搜索框设置
search_box_style: `border: 2rpx solid ${new_style.search_border == '#fff' ? '#eee' : new_style.search_border};`,
search_button_style: this.get_search_button_style(new_form, new_style), // 搜索按钮显示
right_icon_style: `border-radius: 0px ${ new_style.search_border_radius?.radius_top_right * 2 || 0 }rpx ${ new_style.search_border_radius?.radius_bottom_right * 2 || 0 }rpx 0px;`,
});
},
get_search_button_style(form, new_style) {
const { search_botton_color_list = [], search_botton_direction, search_botton_background_img_style, search_button_radius = old_radius, search_botton_background_img, search_botton_margin = this.button_margin, search_botton_border_show = '0', search_botton_border_size = old_padding, search_botton_border_style = 'solid', search_botton_border_color = '' } = new_style;
let style = radius_computer(search_button_radius);
if (form.search_type != 'img') {
const data = {
color_list: search_botton_color_list,
direction: search_botton_direction,
background_img: search_botton_background_img,
background_img_style: search_botton_background_img_style,
}
style += gradient_computer(data) + margin_computer(search_botton_margin) + `color: ${ new_style.button_inner_color };`;
}
let border = ``;
if (search_botton_border_show == '1') {
border += `border-width: ${search_botton_border_size.padding_top * 2}rpx ${search_botton_border_size.padding_right * 2}rpx ${search_botton_border_size.padding_bottom * 2}rpx ${search_botton_border_size.padding_left * 2}rpx;border-style: ${ search_botton_border_style };border-color: ${search_botton_border_color};`
}
const height = 32 - search_botton_margin.margin_top - search_botton_margin.margin_bottom - search_botton_border_size.padding_top - search_botton_border_size.padding_bottom;
return style + border + `height: ${height > 0 ? height * 2 : 0}rpx;line-height: ${height > 0 ? height * 2 : 0}rpx;`;
},
get_search_button_height(new_style) {
const { search_botton_border_size = old_padding, search_botton_padding = this.button_padding } = new_style;
const height = 32 - search_botton_border_size.padding_top - search_botton_border_size.padding_bottom - search_botton_padding.padding_top - search_botton_padding.padding_bottom;
return `height: ${height > 0 ? height * 2 : 0}rpx !important;line-height: ${height > 0 ? height * 2 : 0}rpx;`;
},
// 搜索按钮圆角
get_search_button_img_style(new_style) {
const { search_botton_background_img_style, search_botton_background_img } = new_style;
const data = {
background_img: search_botton_background_img,
background_img_style: search_botton_background_img_style,
}
return background_computer(data) + padding_computer(new_style?.search_botton_padding || this.button_padding) + 'box-sizing: border-box;';
},
// get_style(new_style) {
// let common_styles = '';
// if (new_style.text_style == 'italic') {
// common_styles += `font-style: italic`;
// } else if (new_style.text_style == '500') {
// common_styles += `font-weight: 500`;
// }
// return common_styles;
// },
get_box_style(new_style, form) {
let style = `background: ${ new_style?.search_bg_color || '' };border: 2rpx solid ${new_style.search_border}; ${radius_computer(new_style.search_border_radius)};box-sizing: border-box;`;
if (form.positioning_name_float == '1' && this.propSearchType == 'header') {
style += `padding-left: ${ (new_style.search_padding_left? new_style.search_padding_left : 15) * 2 }rpx;`;
} else if (form.is_center != '1') {
style += `padding-left: ${ (new_style.search_padding_left ? new_style.search_padding_left : 15) * 2 }rpx;`;
}
return style;
},
search_tap() {
app.globalData.url_open('/pages/goods-search-start/goods-search-start?keywords=' + this.keywords);
},
serch_event() {
app.globalData.url_open('/pages/goods-search-start/goods-search-start?keywords=' + this.keywords);
},
serch_button_event() {
if (!isEmpty(this.keywords)) {
app.globalData.url_open('pages/goods-search/goods-search?keywords=' + this.keywords);
}
app.globalData.url_open('/pages/goods-search-start/goods-search-start?keywords=' + this.keywords);
},
search_icon_tap() {
if (isEmpty(this.form.icon_link)) {
this.search_tap();
return;
}
app.globalData.url_open(this.form.icon_link.page);
},
search_right_icon_tap() {
if (isEmpty(this.form.right_icon_link)) {
this.search_tap();
return;
}
app.globalData.url_open(this.form.right_icon_link.page);
},
slideChange(e) {
let actived_index = e.detail.current;
this.setData({
keywords: this.form.hot_word_list[actived_index].value,
})
},
// 位置回调
onBack(e) {
this.$emit('onBack', e);
},
},
};
</script>
<style lang="scss" scoped>
.search {
.box {
height: 64rpx;
// padding: 12rpx 30rpx;
}
.swiper_style {
height: 64rpx;
width: 100%;
}
.img-box {
height: 100%;
.img {
height: 36rpx;
display: block;
}
}
.search-outer-botton {
height: 64rpx;
top: 0;
right: 0;
.search-botton {
height: 64rpx;
.img {
height: 64rpx;
min-width: 6rpx;
max-width: 20rpx;
}
}
}
.right_icon_height {
position: relative;
height: 56rpx !important;
}
.search-icon {
position: relative;
}
.search-icon-before {
position: absolute;
z-index: 10; // 确保悬浮在内容上层
top: -20rpx;
right: -20rpx;
bottom: -20rpx;
left: -20rpx
}
.tips-float {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
}
</style>

643
components/diy/seckill.vue Normal file
View File

@@ -0,0 +1,643 @@
<template>
<view v-if="!isEmpty(list) || !isEmpty(sckill_list)" :style="style_container">
<view :style="style_img_container">
<view class="flex-col gap-10">
<view v-if="form.head_state == '1'" class="oh" :style="seckill_head_style">
<view class="seckill-head flex-row align-c jc-sb" :style="seckill_head_img_style">
<view :class="['flex-row align-c', { 'gap-10': form.theme != '1', 'jc-sb wh-auto': form.theme == '2' }]">
<view class="seckill-title">
<imageEmpty v-if="form.title_type == 'image'" :propImageSrc="form.title_src[0]" propImgFit="heightFix" propErrorStyle="width:42rpx; height: 20rpx;"></imageEmpty>
<text v-else :style="{ color: new_style.title_color, 'font-size': new_style.title_size * 2 + 'rpx', 'line-height': '42rpx', 'font-weight': 600 }">{{ form.title_text }}</text>
</view>
<view v-if="form.theme == '1'" class="padding-horizontal-sm cr-white">|</view>
<view v-if="intervalId != undefined" class="flex-row align-c gap-4">
<text class="text-size-xss" :style="{ color: new_style.end_text_color }">{{ seckill_time.time_first_text }}</text>
<view class="flex-row gap-3 jc-c align-c" :style="form.theme == '4' ? time_bg + 'padding: 6rpx 8rpx;border-radius: 22rpx;' : ''">
<image v-if="form.theme == '4' && form.theme_4_static_img.length > 0" class="seckill-head-icon radius-xs" :src="form.theme_4_static_img[0].url" />
<view v-for="(item, index) in time_config" :key="item.key" class="flex-row gap-3 jc-c align-c">
<template v-if="form.theme == '4'">
<view class="text-size-xs flex-row jc-c align-c" :style="'min-width:50rpx;color:' + new_style.countdown_color">{{ item.value }}</view>
<text v-if="[0, 1].includes(index)" class="colon" :style="{ color: new_style.countdown_color }">:</text>
</template>
<template v-else>
<view class="time-config text-size-xs flex-row jc-c align-c" :style="time_bg + 'min-width:50rpx;color:' + new_style.countdown_color">{{ item.value }}</view>
<text v-if="[0, 1].includes(index)" class="colon" :style="icon_time_check">:</text>
</template>
</view>
</view>
</view>
<view v-else class="flex-row align-c gap-4">
<text class="text-size-xss" :style="{ color: new_style.end_text_color }">已结束</text>
</view>
</view>
<view v-if="form.button_status == '1'" class="flex-row align-c" :style="{ color: new_style.head_button_color }" :data-value="'/pages/plugins/seckill/index/index'" @tap="url_event">
<text :style="{ 'font-size': new_style.head_button_size * 2 + 'rpx' }">{{ form.button_text }}</text>
<iconfont name="icon-arrow-right" :color="new_style.head_button_color" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
<view class="oh" :style="shop_container">
<view class="oh" :style="shop_img_container">
<template v-if="form.shop_style_type != '3'">
<view class="flex-row flex-wrap wh-auto ht-auto" :style="{ gap: content_outer_spacing }">
<view v-for="(item, index) in sckill_list" :key="index" :style="layout_style + layout_type_style + content_radius" :data-value="item.goods_url" @tap="url_event">
<view :class="layout_type" :style="layout_img_style">
<template v-if="!isEmpty(item)">
<view class="oh pr">
<view v-if="!isEmpty(item.new_cover)" :style="img_size">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="content_img_radius" propErrorStyle="width:100rpx; height: 100rpx;"></imageEmpty>
</view>
<view v-else :style="img_size">
<imageEmpty :propImageSrc="item.images" :propStyle="content_img_radius" propErrorStyle="width:100rpx; height: 100rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</template>
<view v-if="is_show('title') || is_show('simple_desc') || is_show('price') || is_show('original_price') || form.is_shop_show == '1'" class="flex-col gap-10 wh-auto flex-1 jc-sb oh" :style="content_style">
<view class="flex-col gap-10 wh-auto">
<!-- 标题 -->
<view v-if="is_show('title') || is_show('simple_desc')" class="flex-col" :style="{'gap': new_style.title_simple_desc_spacing * 2 + 'rpx' }">
<view v-if="is_show('title')" :style="title_style" class="text-line-2">{{ item.title }}</view>
<view v-if="is_show('simple_desc')" class="text-line-1" :style="simple_desc">{{ item.simple_desc }}</view>
</view>
<!-- 进度条 -->
<!-- <view v-if="form.shop_style_type == '1'" class="flex-row align-c gap-6">
<view class="re flex-1">
<view class="slide-bottom" :style="`background: ${new_style.progress_bg_color}`"></view>
<view class="slide-top" :style="` width: 51%; ${slide_active_color}`">
<view class="slide-top-icon round" :style="`background: ${new_style.progress_button_color}`"><icon name="a-miaosha" :color="new_style.progress_button_icon_color" size="9"></icon></view>
</view>
</view>
<text class="text-size-xss" :style="`color: ${new_style.progress_text_color}`">已抢51%</text>
</view> -->
</view>
<view class="flex-row align-e gap-10 jc-sb">
<view class="flex-col gap-5">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="{ color: new_style.shop_price_color }">
<text v-if="form.shop_style_type == '1'" class="text-size-xss pr-4">{{ form.seckill_pirce_title ? form.seckill_pirce_title : ''}}</text>
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="is_show('original_price') && !isEmpty(item.min_original_price)" class="size-11 flex" :style="original_price">
<text class="original-price text-line-1 flex-1">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<template v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</template>
</text>
</view>
</view>
<view v-if="form.is_shop_show == '1'">
<template v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + 'color:' + new_style.shop_button_text_color">{{ form.shop_button_text }}</view>
</template>
<view v-else class="round padding-horizontal-sm ptb-5" :styles="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<template v-else>
<swiper circular="false" :autoplay="new_style.is_roll == '1'" :next-margin="new_style.rolling_fashion == 'translation' ? '-' + content_outer_spacing_magin : '0rpx'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_group" :style="{ height: new_style.content_outer_height * 2 + 'rpx' }">
<swiper-item v-for="(item1, index1) in list" :key="index1">
<view class="wh-auto ht-auto" :class="{ 'flex-row': new_style.rolling_fashion != 'translation' }" :style="new_style.rolling_fashion != 'translation' ? 'gap:' + content_outer_spacing_magin : ''">
<view v-for="(item, index) in item1.split_list" :key="index" :style="layout_style + content_radius + (new_style.rolling_fashion != 'translation' ? layout_type_style : 'margin-right:' + content_outer_spacing_magin + ';height: 100%;whith: 100%')" :data-value="item.goods_url" @tap="url_event">
<view :class="layout_type" :style="layout_img_style">
<template v-if="!isEmpty(item)">
<view class="oh pr wh-auto ht-auto">
<view v-if="!isEmpty(item.new_cover)" class="wh-auto ht-auto">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="content_img_radius" propErrorStyle="width:100rpx; height: 100rpx;"></imageEmpty>
</view>
<view v-else class="wh-auto ht-auto">
<imageEmpty :propImageSrc="item.images" :propStyle="content_img_radius" propErrorStyle="width:100rpx; height: 100rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</template>
<view v-if="is_show('title') || is_show('simple_desc') || is_show('price') || is_show('original_price') || form.is_shop_show == '1'" class="flex-col gap-10 wh-auto flex-1 jc-sb" :style="content_style">
<view class="flex-col gap-10 wh-auto">
<!-- 标题 -->
<view v-if="is_show('title') || is_show('simple_desc')" class="flex-col" :style="{'gap': new_style.title_simple_desc_spacing * 2 + 'rpx' }">
<view v-if="is_show('title')" :style="title_style" class="text-line-2">{{ item.title }}</view>
<view v-if="is_show('simple_desc')" class="text-line-1" :style="simple_desc">{{ item.simple_desc }}</view>
</view>
<!-- 进度条 -->
<!-- <view v-if="form.shop_style_type == '1'" class="flex-row align-c gap-6">
<view class="re flex-1">
<view class="slide-bottom" :style="{ 'background': new_style.progress_bg_color }"></view>
<view class="slide-top" :style="'width: 51%;' + slide_active_color ">
<view class="slide-top-icon round" :style="{ 'background': new_style.progress_button_color}"><icon name="a-miaosha" :color="new_style.progress_button_icon_color" size="9"></icon></view>
</view>
</view>
<text class="text-size-xss" :style="{ 'color': new_style.progress_text_color }">已抢51%</text>
</view> -->
</view>
<view class="flex-row align-e gap-10 jc-sb">
<view class="flex-col gap-5">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="{ color: new_style.shop_price_color }">
<text v-if="form.shop_style_type == '1'" class="text-size-xss pr-4">{{ form.seckill_pirce_title ? form.seckill_pirce_title : ''}}</text>
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="is_show('original_price') && !isEmpty(item.min_original_price)" class="size-11 flex" :style="original_price">
<text class="original-price text-line-1 flex-1">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<template v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</template>
</text>
</view>
</view>
<view v-if="form.is_shop_show == '1'">
<template v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + 'color:' + new_style.shop_button_text_color">{{ form.shop_button_text }}</view>
</template>
<view v-else class="round padding-horizontal-sm ptb-5" :styles="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</template>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { background_computer, common_styles_computer, common_img_computer, gradient_computer, gradient_handle, padding_computer, radius_computer, isEmpty, margin_computer, box_shadow_computer, border_computer, old_margin, old_border_and_box_shadow } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import subscriptIndex from '@/components/diy/modules/subscript/index.vue';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
imageEmpty,
subscriptIndex
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
form: {},
new_style: {},
time_bg: '',
slide_active_color: '',
seckill_head_style: '',
style_container: '',
style_img_container: '',
time_config: [
{ key: 'hour', value: '00' },
{ key: 'minute', value: '00' },
{ key: 'second', value: '00' },
],
seckill_time: {},
// 商品间距
content_outer_spacing: '',
content_outer_spacing_magin: '',
// 圆角设置
content_radius: '',
// 内边距设置
content_padding: '',
// 内容区域的样式
content_style: '',
// 不同风格下的样式
img_size: '',
layout_type: '',
layout_type_style: '',
layout_img_style: '',
layout_style: '',
//图片圆角设置
content_img_radius: '',
//角标设置
corner_marker: '',
// 定时器
intervalId: null,
// 数据存储
list: [],
// 一屏显示的数量
slides_per_group: '',
// 内容样式
title_style: '',
price_style: '',
button_style: '',
simple_desc: '',
price_symbol: '',
price_unit: '',
original_price: '',
shop_container: '',
shop_img_container: '',
// 商品列表
sckill_list: [],
};
},
computed: {
icon_time_check() {
return `${this.time_bg};line-height: 1;background-clip: text;-webkit-background-clip: text;-webkit-text-fill-color: transparent;`;
},
// 按钮渐变色处理
button_gradient() {
return gradient_handle(this.new_style.shop_button_color, '180deg');
},
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
beforeDestroy() {
// 如果有定时任务执行,在离开的时候清空掉定时任务
if (!isEmpty(this.intervalId)) {
clearInterval(this.intervalId);
}
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
const data = new_form.data;
let new_list = [];
if (data && !isEmpty(data.current)) {
if (!isEmpty(data.current.goods)) {
new_list = data.current.goods;
}
const { status, time_first_text } = data.current.time;
this.setData({
seckill_time: {
time_end_number: Number(data.current.time_end_number + '000'),
time_start_number: Number(data.current.time_start_number + '000'),
status: status,
time_first_text: time_first_text,
},
});
// 先执行一次倒计时,后续的等待倒计时执行
setTimeout(() => {
this.updateCountdown();
}, 0);
this.setData({
intervalId: setInterval(this.updateCountdown, 1000),
});
}
// 默认数据
const product_style_list = [
{ name: '单列', value: '1', width: 110, height: 120 },
{ name: '双列', value: '2', width: 180, height: 180 },
{ name: '横向滑动', value: '3', width: 0, height: 0 },
];
const scale = sys_width / 390;
let img_style = ``;
if (['1'].includes(new_form.shop_style_type)) {
// 图片宽度
if (typeof new_style.content_img_width == 'number') {
img_style += `width: ${ new_style.content_img_width * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_form.shop_style_type);
if (list.length > 0) {
img_style += `width: ${ list[0].width * scale }px;`;
} else {
img_style += 'width: auto;';
}
}
}
if (!['3'].includes(new_form.shop_style_type)) {
// 图片宽度
if (typeof new_style.content_img_height == 'number') {
img_style += `height: ${ new_style.content_img_height * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_form.shop_style_type);
if (list.length > 0) {
img_style += `height: ${ list[0].height * scale }px;`;
} else {
img_style += 'height: auto;';
}
}
}
const shop_style = new_style?.shop_style || old_border_and_box_shadow;
this.setData({
form: this.propValue.content,
new_style: this.propValue.style,
time_bg: this.get_time_bg(new_style),
slide_active_color: this.get_slide_active_color(new_style),
seckill_head_style: this.get_seckill_head_style(new_style, '1'),
seckill_head_img_style: this.get_seckill_head_style(new_style, '2'),
style_container: common_styles_computer(new_style.common_style) + 'box-sizing: border-box;',
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
content_outer_spacing: new_style.content_outer_spacing + 'px',
content_outer_spacing_magin: new_style.content_outer_spacing * 2 + 'rpx',
content_radius: radius_computer(new_style.shop_radius),
content_padding: padding_computer(new_style.shop_padding) + 'box-sizing: border-box;',
content_style: this.get_content_style(new_style, new_form),
layout_type: new_form.shop_style_type == '1' ? 'flex-row wh-auto ht-auto oh' : 'flex-col wh-auto ht-auto oh',
layout_img_style: this.get_layout_img_style(new_style, new_form),
layout_type_style: this.get_layout_type_style(new_style, new_form),
layout_style: gradient_handle(new_style.shop_color_list, new_style.shop_direction) + margin_computer(new_style?.shop_margin || old_margin) + border_computer(shop_style) + box_shadow_computer(shop_style),
content_img_radius: radius_computer(new_style.shop_img_radius),
corner_marker: this.get_corner_marker(new_style),
slides_per_group: new_style.rolling_fashion == 'translation' ? new_form.carousel_col : 1,
// 内容样式设置
title_style: this.trends_config(new_style, 'title', 'title'),
price_style: this.trends_config(new_style, 'price'),
button_style: this.trends_config(new_style, 'button', 'gradient'),
simple_desc: this.trends_config(new_style, 'simple_desc', 'desc'),
price_symbol: !isEmpty(new_style.shop_price_symbol_color) ? this.trends_config(new_style, 'price_symbol') : 'font-size: 18rpx;color: #EA3323;' ,
price_unit: !isEmpty(new_style.shop_price_unit_color) ? this.trends_config(new_style, 'price_unit') : 'font-size: 18rpx;color: #EA3323;',
original_price: this.trends_config(new_style, 'original_price'),
list: this.get_shop_content_list(new_list, new_form, new_style),
shop_container: this.get_shop_container(new_style),
shop_img_container: this.get_shop_img_container(new_style),
sckill_list: new_list,
img_size: img_style,
});
},
// 商品内容区域显示
get_shop_container(new_style){
const { shop_content_color_list = [], shop_content_direction = '', shop_content_radius = old_radius, shop_content_margin = old_margin, shop_content = old_border_and_box_shadow } = new_style;
// 渐变
const gradient = { color_list: shop_content_color_list, direction: shop_content_direction };
return gradient_computer(gradient) + radius_computer(shop_content_radius) + margin_computer(shop_content_margin) + border_computer(shop_content) + box_shadow_computer(shop_content);
},
get_shop_img_container (new_style) {
const { shop_content_background_img_style = '2', shop_content_background_img = [], shop_content_padding = old_padding } = new_style;
// 背景图
const back = { background_img: shop_content_background_img, background_img_style: shop_content_background_img_style };
return padding_computer(shop_content_padding) + background_computer(back);
},
get_shop_content_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.carousel_col;
// 存储数据显示
let nav_list = [];
// 拆分的数量
const split_num = Math.ceil(cloneList.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({
split_list: cloneList.slice(i * num, (i + 1) * num),
});
}
return nav_list;
} else {
// 否则的话,就返回全部的信息
return [
{
split_list: cloneList,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
get_time_bg(new_style) {
const { countdown_bg_color_list, countdown_direction } = new_style;
// 渐变
const gradient = { color_list: countdown_bg_color_list, direction: countdown_direction };
return gradient_computer(gradient);
},
get_slide_active_color(new_style) {
const { progress_actived_color_list, progress_actived_direction } = new_style;
// 渐变
const gradient = { color_list: progress_actived_color_list, direction: progress_actived_direction };
return gradient_computer(gradient);
},
get_seckill_head_style(new_style, num) {
const { header_background_img, header_background_img_style, header_background_color_list, header_background_direction, seckill_head_padding, seckill_head_radius, seckill_head_margin = old_margin, seckill_head_style = old_border_and_box_shadow } = new_style;
// 渐变
const gradient = { color_list: header_background_color_list, direction: header_background_direction };
// 背景图
const back = { background_img: header_background_img, background_img_style: header_background_img_style };
if (num == '1') {
return gradient_computer(gradient) + radius_computer(seckill_head_radius) + margin_computer(seckill_head_margin) + border_computer(seckill_head_style) + box_shadow_computer(seckill_head_style);
} else {
// 秒杀头部内间距设置, 没有的时候默认15px
const padding = !isEmpty(seckill_head_padding) ? seckill_head_padding : { padding: 0, padding_top: 15, padding_bottom: 15, padding_left: 13, padding_right: 13};
return background_computer(back) + padding_computer(padding) + 'box-sizing: border-box;';
}
},
updateCountdown() {
let time_end_number = this.seckill_time.time_end_number;
if (this.seckill_time.status === 0) {
time_end_number = this.seckill_time.time_start_number;
}
// 先获取秒杀结束时间和当前时间的差值
const distance = time_end_number - new Date().getTime();
// 如果倒计时结束,显示结束信息
if (distance <= 1000) {
clearInterval(this.intervalId);
// 如果是待开始状态,则显示开始倒计时,并且在结束的时候根据结束时候再执行一个定时器
if (this.seckill_time.status === 0) {
this.setData({
seckill_time: {
time_end_number: this.seckill_time.time_end_number,
time_start_number: this.seckill_time.time_start_number,
status: 1,
time_first_text: '距结束',
},
});
// 先执行一次倒计时,后续的等待倒计时执行
setTimeout(() => {
this.updateCountdown();
}, 0);
this.setData({
intervalId: setInterval(this.updateCountdown, 1000),
});
}
return;
}
// 计算时间
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
this.time_config.forEach((item) => {
if (item.key == 'hour') {
item.value = hours < 10 ? '0' + hours : hours.toString();
} else if (item.key == 'minute') {
item.value = minutes < 10 ? '0' + minutes : minutes.toString();
} else if (item.key == 'second') {
item.value = seconds < 10 ? '0' + seconds : seconds.toString();
}
});
},
get_content_style(new_style, form) {
const spacing_value = new_style.content_spacing;
let spacing = '';
if (form.shop_style_type == '1') {
spacing = `margin-left: ${spacing_value * 2}rpx;`;
} else {
spacing = padding_computer(new_style.shop_padding) + 'box-sizing: border-box;';
}
return `${spacing}`;
},
get_layout_type_style(new_style, form) {
return ['1', '2'].includes(form.shop_style_type) ? `height: 100%;width: ${this.get_multicolumn_columns_width(new_style, form)};min-width: ${this.get_multicolumn_columns_width(new_style, form)};` : `height: ${new_style.content_outer_height * 2}rpx;width: ${this.get_multicolumn_columns_width(new_style, form)};min-width: ${this.get_multicolumn_columns_width(new_style, form)};`;
},
get_layout_img_style(new_style, form) {
const padding = ['1'].includes(form.shop_style_type) ? padding_computer(new_style.shop_padding) + 'box-sizing: border-box;' : '';
const data = {
background_img_style: new_style.shop_background_img_style,
background_img: new_style.shop_background_img,
}
return `${padding} ${ background_computer(data) }`;
},
// 根据传递的参数,从对象中取值
trends_config(new_style, key, type) {
return this.style_config(new_style[`shop_${key}_typeface`], new_style[`shop_${key}_size`], new_style[`shop_${key}_color`], type, new_style);
},
// 根据传递的值,显示不同的内容
style_config(typeface, size, color, type, new_style) {
let style = `font-weight:${typeface}; font-size: ${size * 2}rpx;`;
if (type == 'gradient') {
style += gradient_handle(new_style.shop_button_color, '180deg');
} else if (type == 'title') {
style += `line-height: ${size > 0 ? (size + 3) * 2 : 0}rpx;height: ${size > 0 ? (size + 3) * 4 : 0}rpx;color: ${color};`;
} else if (type == 'desc') {
style += `line-height: ${size * 2}rpx;height: ${size * 2}rpx;color: ${color};`;
} else {
style += `color: ${color};`;
}
return style;
},
get_multicolumn_columns_width(new_style, form) {
let model_number = form.carousel_col;
if (form.shop_style_type == '1') {
model_number = 1;
} else if (form.shop_style_type == '2') {
model_number = 2;
}
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.content_outer_spacing * (model_number - 1)) / model_number;
return `calc(${100 / model_number}% - ${gap}px)`;
},
is_show(index) {
return this.form.is_show.includes(index);
},
get_corner_marker(new_style) {
const { seckill_subscript_location, shop_img_radius, seckill_subscript_bg_color, seckill_subscript_text_color } = new_style;
let location = `background: ${seckill_subscript_bg_color};color: ${seckill_subscript_text_color};`;
// 圆角根据图片的圆角来计算 对角线是同样的圆角
if (seckill_subscript_location == 'top-left') {
location += `top: 0;left: 0;border-radius: ${shop_img_radius.radius_top_left * 2}rpx 0 ${shop_img_radius.radius_top_left * 2}rpx 0;`;
} else if (seckill_subscript_location == 'top-right') {
location += `top: 0;right: 0;border-radius: 0 ${shop_img_radius.radius_top_right * 2}rpx 0 ${shop_img_radius.radius_top_right * 2}rpx;`;
} else if (seckill_subscript_location == 'bottom-left') {
location += `bottom: 0;left: 0;border-radius: 0 ${shop_img_radius.radius_bottom_left * 2}rpx 0 ${shop_img_radius.radius_bottom_left * 2}rpx;`;
} else if (seckill_subscript_location == 'bottom-right') {
location += `bottom: 0;right: 0;border-radius: ${shop_img_radius.radius_bottom_right * 2}rpx 0 ${shop_img_radius.radius_bottom_right * 2}rpx 0;`;
}
return location;
},
// 跳转链接
url_event(link) {
app.globalData.url_event(link);
},
},
};
</script>
<style scoped lang="scss">
.seckill-head {
padding: 30rpx 26rpx;
width: 100%;
height: 102rpx;
border-radius: 16rpx 16rpx 0 0;
box-sizing: border-box;
.seckill-title {
width: auto;
height: 42rpx;
}
.time-config {
padding: 2rpx 10rpx;
box-sizing: border-box;
line-height: 34rpx;
border-radius: 8rpx;
}
}
.seckill-head-icon {
width: 32rpx;
height: 32rpx;
}
.colon {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 33rpx;
top: -2rpx;
}
.slide-bottom {
height: 20rpx;
border-radius: 10rpx;
background: red;
}
.slide-top {
position: absolute;
height: 20rpx;
top: 0;
left: 0;
border-radius: 10rpx;
.slide-top-icon {
position: absolute;
top: -6rpx;
right: 0;
width: 32rpx;
height: 32rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
.original-price {
text-decoration-line: line-through;
}
.size-11 {
font-size: 22rpx;
}
</style>

View File

@@ -0,0 +1,231 @@
<template>
<view class="ou pr" :style="style_container + swiper_bg_style">
<view class="pa top-0 wh-auto ht-auto" :style="swiper_bg_img_style"></view>
<view class="ou wh-auto" :style="style_img_container + (!isEmpty(swiper_bg_img_style) ? swiper_bg_img_style_null : '')">
<componentDiyTabs :propKey="propKey" :propContentPadding="propContentPadding" :propValue="propValue" :propTop="propTop" :propIsRotatingBackground="is_rotating_background" :propBgStyle="swiper_bg_style" :propBgImgStyle="swiper_bg_img_style" :propStickyTop="propStickyTop" :propIsImmersionModel="propIsImmersionModel" :propNewIsTabsSafeDistance="new_is_tabs_safe_distance" :propNavIsTop="propNavIsTop" :propTabsIsTop="propTabsIsTop" :propIsCommon="false" :propsTabsContainer="tabs_container" :propsTabsImgContainer="tabs_img_container" :propSpacingCommonStyle="spacing_common_style" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" @onComputerHeight="tabs_height_event" @onTabsTap="tabs_click_event"></componentDiyTabs>
<view :style="carousel_margin_top">
<view :style="carousel_container">
<view :style="carousel_img_container">
<componentDiycarousel :propValue="propValue" :propIsCommon="false" @onVideoPlay="video_play" @slideChange="slideChange"></componentDiycarousel>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, isEmpty, gradient_computer, margin_computer, background_computer, radius_computer, old_margin, old_padding, old_radius, old_border_and_box_shadow, border_computer, box_shadow_computer, } from '@/common/js/common/common.js';
import componentDiyTabs from '@/components/diy/tabs';
import componentDiycarousel from '@/components/diy/carousel';
export default {
components: {
componentDiyTabs,
componentDiycarousel,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propTop: {
type: [String, Number],
default: 0,
},
propStickyTop: {
type: Number,
default: 0,
},
// 是否导航栏置顶
propNavIsTop: {
type: Boolean,
default: true,
},
// 是否选项卡置顶
propTabsIsTop: {
type: Boolean,
default: false,
},
propKey: {
type: [String, Number],
default: '',
},
propContentPadding: {
type: String,
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
// 选项卡是否使用安全距离
propIsImmersionModel: {
type: Boolean,
default: false
}
},
data() {
return {
style_container: '',
style_img_container: '',
style_margin_container: '',
spacing_common_style: {
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
},
tabs_padding_style: '',
// 选项卡内容
tabs_container: '',
tabs_img_container: '',
tabs_sliding_fixed_bg: '',
// 轮播图内容
carousel_margin_top: '',
carousel_container: '',
carousel_img_container: '',
// top_up: '0',
actived_index: 0,
// 轮播图背景
swiper_bg_style: '',
swiper_bg_img_style: '',
swiper_bg_img_style_null: `background-image: url('')`,
is_rotating_background: false,
new_is_tabs_safe_distance: false,
};
},
created() {
this.init();
},
// 属性值改变监听
watch: {
// 数据
propTabsIsTop(value, old_value) {
this.init();
},
propKey(val) {
// 初始化
this.init();
},
},
methods: {
isEmpty,
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
const { tabs_bg_color_list = [], tabs_bg_direction = '', tabs_bg_background_img_style = '', tabs_bg_background_img = [], tabs_radius = old_radius, tabs_padding = old_padding, tabs_margin = old_margin, tabs_content = old_border_and_box_shadow, carousel_content_color_list = [], carousel_content_direction = '', carousel_content_background_img_style = '', carousel_content_background_img = [], carousel_content_margin = old_margin, carousel_content_padding = old_padding, carousel_content_radius = old_radius, carousel_content = old_border_and_box_shadow } = new_style;
// 选项卡背景设置
const tabs_data = {
color_list: tabs_bg_color_list,
direction: tabs_bg_direction,
background_img_style: tabs_bg_background_img_style,
background_img: tabs_bg_background_img,
}
// 商品区域背景设置
const carousel_content_data = {
color_list: carousel_content_color_list,
direction: carousel_content_direction,
background_img_style: carousel_content_background_img_style,
background_img: carousel_content_background_img,
}
// 头部的高度
const newPropTop = app.globalData.rpx_to_px(this.propTop);
// 选项卡的外边距
const new_tabs_top = app.globalData.rpx_to_px(tabs_margin?.margin_top || 0);
// 选项卡的实际外边距
const tabs_margin_top = new_content.is_tabs_safe_distance == '1' ? newPropTop + this.propStickyTop : 0;
// 选项卡的内边距处理
const new_padding_top = new_style.common_style.padding_top - newPropTop;
this.setData({
// style_container: `${common_styles_computer(common_style)};gap:${new_style.data_spacing * 2}rpx`,
style_container: `${common_styles_computer(new_style.common_style)};`,
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
carousel_margin_top: 'margin-top:' + new_style.data_spacing * 2 + 'rpx',
new_is_tabs_safe_distance: new_content.is_tabs_safe_distance == '1',
// 是否开启轮播图背景设置
is_rotating_background: new_content.rotating_background == '1',
tabs_sliding_fixed_bg: gradient_computer(tabs_data),
tabs_container: gradient_computer(tabs_data) + radius_computer(tabs_radius) + margin_computer(tabs_margin) + border_computer(tabs_content) + box_shadow_computer(tabs_content) + `overflow: hidden;margin-top: ${ new_tabs_top - tabs_margin_top }px;`,
tabs_img_container: background_computer(tabs_data) + padding_computer(tabs_padding) + `box-sizing: border-box;overflow: hidden;padding-top: ${ (tabs_padding?.padding_top || 0) + tabs_margin_top }px;`,
carousel_container: gradient_computer(carousel_content_data) + margin_computer(carousel_content_margin) + radius_computer(carousel_content_radius) + border_computer(carousel_content) + box_shadow_computer(carousel_content) + 'overflow: hidden;',
carousel_img_container: background_computer(carousel_content_data) + padding_computer(carousel_content_padding) + 'box-sizing: border-box;overflow: hidden;',
spacing_common_style: {
padding: 0,
padding_top: (this.propIsImmersionModel && new_content.is_tabs_safe_distance == '1' ? new_style.common_style.padding_top + this.propStickyTop : new_padding_top > 0 ? new_padding_top : 0),
padding_bottom: 0,
padding_left: new_style.common_style.padding_left,
padding_right: new_style.common_style.padding_right,
margin: 0,
margin_top: new_style.common_style.margin_top,
margin_left: new_style.common_style.margin_left,
margin_right: new_style.common_style.margin_right,
},
swiper_bg_style: this.get_swiper_bg_style(new_content, 0),
swiper_bg_img_style: this.get_swiper_bg_img_style(new_content, 0),
});
},
// tab点击
tabs_click_event(tabs_id, is_micro_page) {
this.$emit('onTabsTap', tabs_id, is_micro_page);
},
// tab高度
tabs_height_event(height) {
this.$emit('onComputerHeight', height);
},
// 视频播放
video_play(url, popup_width, popup_height) {
this.$emit('onVideoPlay', url, popup_width, popup_height);
},
get_swiper_bg_style(form, actived_index) {
const style = form?.carousel_list?.[actived_index]?.style;
if (style && !isEmpty(style.color_list)) {
const color_list = style.color_list;
const list = color_list.filter((item) => !isEmpty(item.color));
if (list.length > 0) {
try {
return gradient_computer(style);
} catch (error) {
return '';
}
}
return '';
}
return '';
},
get_swiper_bg_img_style(form, actived_index) {
const { carousel_img, style = {} } = form?.carousel_list[actived_index] || {};
// 如果是自定义的图片 判断图片是否存在
if (!isEmpty(carousel_img) && style?.background_type == 'carousel') {
// 如果是使用轮播图,判断轮播图是否存在
const data = {
background_img: carousel_img,
background_img_style: style?.background_img_style || '2',
}
return background_computer(data) + (style.is_background_img_blur == '1' ? `filter: blur(14px);opacity: 0.6;` : '');
} else if (!isEmpty(style?.background_img)) {
return background_computer(style) + (style.is_background_img_blur == '1' ? `filter: blur(14px);opacity: 0.6;` : '');
}
return '';
},
slideChange(index) {
this.setData({
actived_index: index,
swiper_bg_style: this.get_swiper_bg_style(this.propValue.content || {}, index),
swiper_bg_img_style: this.get_swiper_bg_img_style(this.propValue.content || {}, index),
});
},
},
};
</script>
<style scoped lang="scss"></style>

270
components/diy/tabs.vue Normal file
View File

@@ -0,0 +1,270 @@
<template>
<!-- 选项卡 -->
<view class="tabs-container pr" :style="tabs_z_index">
<view :class="top_up == '1' ? 'tabs-top' : ''" :style="tabs_top_style + (top_up == '1' ? propContentPadding : '')">
<view :style="style_margin_container">
<view class="tabs-contents bs-bb pr" :style="style_container">
<view :class="top_up == '1' ? 'bs-bb' : 'wh-auto bs-bb'" :style="style_img_container">
<componentDiyModulesTabsView :propKey="propKey" :propValue="tabs_data" :propIsTabsIcon="true" :propTop="propTop" :propsTabsContainer="propsTabsContainer + (propIsRotatingBackground ? propBgStyle : '')" :propIsRotatingBackground="propIsRotatingBackground" :propBgImgStyle="propBgImgStyle" :propsTabsImgContainer="propsTabsImgContainer" :propStyle="propStyle" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" @onTabsTap="tabs_click_event" @tabsZindex="tabsZindex"></componentDiyModulesTabsView>
</view>
</view>
</view>
</view>
<view v-if="top_up == '1'" class="tabs-seat" :style="'height:' + (propIsCommon ? tabs_seat_height : tabs_carousel_seat_height) + 'px;'"></view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, margin_computer, gradient_computer, background_computer } from '@/common/js/common/common.js';
import componentDiyModulesTabsView from '@/components/diy/modules/tabs-view';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
// 置顶距离顶部高度
propTop: {
type: [String, Number],
default: '0',
},
propStickyTop: {
type: Number,
default: 0,
},
// 是否导航栏置顶
propNavIsTop: {
type: Boolean,
default: true,
},
// 是否选项卡置顶
propTabsIsTop: {
type: Boolean,
default: false,
},
propIsCommon: {
type: Boolean,
default: true,
},
propSpacingCommonStyle: {
type: Object,
default: () => ({}),
},
// 样式
propStyle: {
type: String,
default: '',
},
propKey: {
type: [String, Number],
default: '',
},
propsTabsContainer: {
type: String,
default: ''
},
propsTabsImgContainer: {
type: String,
default: '',
},
propContentPadding: {
type: String,
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propIsImmersionModel: {
type: Boolean,
default: false
},
propNewIsTabsSafeDistance: {
type: Boolean,
default: true
},
propTabsSlidingFixedBg: {
type: String,
default: ''
},
propIsRotatingBackground: {
type: Boolean,
default: false
},
propBgStyle: {
type: String,
default: ''
},
propBgImgStyle: {
type: String,
default: ''
}
},
components: {
componentDiyModulesTabsView,
},
data() {
return {
style_container: '',
style_img_container: '',
tabs_sliding_fixed_bg: '',
content: '',
tabs_data: {},
// 是否滑动置顶
top_up: '0',
// 置顶时,选项卡高度
tabs_seat_height: 0,
// 置顶时,轮播选项卡高度
tabs_carousel_seat_height: 0,
// 置顶时,选项卡样式
tabs_top_style: '',
style_margin_container: '',
tabs_z_index: ''
};
},
created() {
this.init();
},
mounted() {
setTimeout(() => {
this.get_tabs_height();
}, 100);
},
// 属性值改变监听
watch: {
// 数据
propTabsIsTop(value, old_value) {
this.init();
setTimeout(() => {
this.get_tabs_height();
}, 100);
},
propTop(val) {
this.init();
},
propStickyTop(val) {
this.init();
},
propKey(val) {
// 初始化
this.init();
},
propSpacingCommonStyle(val) {
// 初始化
this.init();
},
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
let new_tabs_data = JSON.parse(JSON.stringify(this.propValue));
new_tabs_data.content.tabs_list.unshift(new_tabs_data.content.home_data);
// 判断选项卡是否置顶
let other_style = this.propTop;
let new_tabs_top_style = this.propNavIsTop || this.propTabsIsTop || new_content.tabs_top_up == '1' ? (new_content.tabs_top_up == '1' ? 'top:calc(' + (this.propStickyTop > 0 ? this.propStickyTop + 'px + ' : '') + other_style * 2 + 'rpx);z-index:11;' : '') : '';
let new_top_up = new_content.tabs_top_up;
// #ifdef H5 || MP-TOUTIAO
// if (this.propTabsIsTop) {
// other_style = '0';
// }
new_tabs_top_style = 'top:calc(' + (this.propStickyTop > 0 ? this.propStickyTop + 'px + ' : '') + other_style * 2 + 'rpx);z-index:11;';
new_top_up = this.propNavIsTop || this.propTabsIsTop ? new_content.tabs_top_up : '0';
// #endif
let tabs_bg = new_style.common_style.color_list;
let new_tabs_background = '';
if (!Array.isArray(tabs_bg) || tabs_bg.length === 0 || !tabs_bg[0] || !tabs_bg[0].color) {
new_tabs_background = 'background:#fff;';
}
const newPropTop = this.propIsCommon ? (app.globalData.rpx_to_px(this.propTop) + this.propStickyTop) : (app.globalData.rpx_to_px(this.propTop + this.propStickyTop));
this.setData({
tabs_data: new_tabs_data,
tabs_sliding_fixed_bg: this.propIsCommon ? gradient_computer(new_style.common_style) : this.propTabsSlidingFixedBg,
style_container: this.propIsCommon ? new_tabs_background + common_styles_computer(new_style.common_style) : new_content.tabs_top_up == '1' ? new_tabs_background + gradient_computer(new_style.common_style) + margin_computer(this.propSpacingCommonStyle) : '', // 如果是选项卡轮播,不需要走默认样式
// 如果开了滑动置顶并且开了沉浸式不需要走传递过来的index否则的话就用传递过来的index
style_img_container: this.propIsCommon ? common_img_computer(new_style.common_style, this.propIndex) : new_content.tabs_top_up == '1' ? background_computer(new_style.common_style) + padding_computer(this.propSpacingCommonStyle, 1, false) + 'box-sizing: border-box;' : '', // 如果是选项卡轮播,不需要走默认样式
tabs_top_style: new_tabs_top_style,
// 沉浸模式下并且开通了安全距离 会显示-的margin
style_margin_container: this.propIsImmersionModel && this.propNewIsTabsSafeDistance ? `margin-top: -${ newPropTop }px;` : '',
// 判断是否置顶
top_up: new_top_up,
tabs_z_index: 'z-index: 11;'
});
},
// 获取选项卡高度
get_tabs_height() {
if (this.top_up == '1') {
// 选择我们想要的元素
const query = uni.createSelectorQuery();
query
.in(this)
.select('.tabs-top')
.boundingClientRect((res) => {
if ((res || null) != null) {
const newPropTop = app.globalData.rpx_to_px(this.propTop);
// data包含元素的宽度、高度等信息
this.setData({
tabs_seat_height: res.height + (this.propIsImmersionModel ? newPropTop + this.propStickyTop : 0),
tabs_carousel_seat_height: (res.height + (this.propIsImmersionModel ? newPropTop + this.propStickyTop : 0)) - this.propSpacingCommonStyle.padding_top - this.propSpacingCommonStyle.margin_top, // 轮播选项卡置顶时去掉顶部间距
});
this.$emit('onComputerHeight', this.tabs_seat_height);
}
})
.exec();
} else {
this.$emit('onComputerHeight', 0);
}
},
tabsZindex(val) {
this.setData({
tabs_z_index: `z-index: ${ val };`
});
},
// 选项卡回调
tabs_click_event(index, item) {
let tabs_id = '';
// 抽象出获取 tabs_id 的逻辑
tabs_id = this.get_tabs_id(item, index);
// 是否是商品分类页面
const is_micro_page = item.data_type == '0';
this.$emit('onTabsTap', tabs_id, is_micro_page);
},
// 获取 tabs_id
get_tabs_id(item, index) {
if (item.data_type === '0') {
return index !== 0 ? item.micro_page_list?.id : '';
} else {
return index !== 0 ? item.classify?.id : '';
}
},
},
};
</script>
<style lang="scss" scoped>
.tabs-container {
z-index: 11;
.tabs-top {
position: fixed;
left: 0;
right: 0;
max-width: 100%;
}
}
.tabs-contents {
max-width: 1600rpx !important;
}
@media only screen and (min-width: 1600rpx) {
.tabs-container .tabs-top {
left: calc(50% - 400px) !important;
}
}
</style>

143
components/diy/title.vue Normal file
View File

@@ -0,0 +1,143 @@
<template>
<view :style="style_container">
<view :style="style_img_container">
<view class="flex-col gap-10">
<view class="pr flex-row" :class="title_center">
<view class="z-i flex-row align-c gap-10">
<template v-if="!isEmpty(form.img_src) && !isEmpty(form.img_src[0].url)">
<view class="wh-auto" :style="{'height': new_style.img_height * 2 + 'rpx' }">
<image :src="form.img_src[0].url" class="ht-auto" mode="heightFix"></image>
</view>
</template>
<template v-else-if="!isEmpty(form.icon_class)">
<iconfont :name="'icon-' + form.icon_class" :size="new_style.icon_size * 2 + 'rpx'" :color="new_style.icon_color" propContainerDisplay="flex"></iconfont>
</template>
<view v-if="!isEmpty(form.title)" class="nowrap" :style="title_style" :data-value="!isEmpty(form.title_link) ? form.title_link.page : ''" @tap="url_event">{{ form.title }}</view>
<view v-if="!isEmpty(form.subtitle) && new_style.title_line == '1'" class="text-word-break nowrap" :style="subtitle_style">{{ form.subtitle }}</view>
</view>
<view class="flex-row gap-10 align-c right-0 pa">
<template v-if="form.keyword_show == '1'">
<view class="flex-row align-c" :style="keyword_gap">
<view v-for="item in keyword_list" :key="item.id" :style="keyword_style" :data-value="!isEmpty(item.link) ? item.link.page : ''" @tap="url_event">
{{ item.title }}
</view>
</view>
</template>
<view v-if="form.right_show == '1'" class="nowrap flex-row align-c" :style="right_style" :data-value="!isEmpty(form.right_link) ? form.right_link.page : ''" @tap="url_event"
>{{ form.right_title }}
<iconfont name="icon-arrow-right" :color="new_style.right_color" :size="new_style.right_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
<view v-if="!isEmpty(form.subtitle) && new_style.title_line != '1'" class="text-word-break" :style="subtitle_style">{{ form.subtitle }}</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, isEmpty } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
form: {},
new_style: {},
style_container: '',
style_img_container: '',
title_center: '',
subtitle_style: '',
keyword_list: [],
keyword_style: '',
keyword_gap: '',
right_style: '',
right_size: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 判断是否为空
isEmpty,
// 初始化数据
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
const { keyword_color, keyword_size, right_color, right_size, common_style, title_weight, title_color, title_size, keyword_spacing = 10 } = new_style;
// 标题样式设置
let common_styles = '';
if (title_weight == 'italic') {
common_styles += `font-style: italic`;
} else if (['bold', '500'].includes(title_weight)) {
common_styles += `font-weight: bold`;
}
// 是否居中
this.setData({
form: new_form,
new_style: new_style,
title_center: new_form.is_title_center == '1' ? 'jc-c' : '',
keyword_list: new_form.keyword_list.filter((item) => item.is_show == '1'), // 关键字
keyword_style: `color:${keyword_color}; font-size: ${keyword_size * 2}rpx;`, // 关键字设置
keyword_gap: !isEmpty(keyword_spacing) ? `gap: ${ keyword_spacing * 2}rpx` : 'gap: 20rpx;', // 关键字间距设置
right_size: right_size * 2 + 'rpx', // 右边按钮设置
right_style: `color:${right_color}; font-size: ${right_size * 2}rpx;`, //右侧按钮样式
title_style: `color:${title_color}; font-size: ${title_size * 2}rpx; ${common_styles}`, // 标题样式设置
subtitle_style: this.get_subtitle_style(new_style), // 副标题样式设置
style_container: common_styles_computer(common_style), // 通用样式区
style_img_container: common_img_computer(common_style, this.propIndex), // 通用图片样式区
});
},
// 副标题样式设置
get_subtitle_style(new_style) {
let common_styles = '';
if (new_style.subtitle_weight == 'italic') {
common_styles += `font-style: italic`;
} else if (['bold', '500'].includes(new_style.subtitle_weight)) {
common_styles += `font-weight: bold;`;
}
return `color:${new_style.subtitle_color}; font-size: ${new_style.subtitle_size * 2}rpx; ${common_styles}`;
},
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style scoped lang="scss">
.right-0 {
top: 50%;
transform: translateY(-50%);
}
.break {
word-break: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
}
</style>

View File

@@ -0,0 +1,164 @@
<template>
<!-- 用户信息 -->
<view :style="style_container">
<view :style="style_img_container">
<view class="pr padding-xxl" :style="style">
<view class="flex-row jc-sb align-c margin-bottom-xxl">
<view class="flex-1 flex-row align-c gap-12">
<image :src="(user_info.user || null) !== null ? user_info.user.avatar : user.avatar" class="circle" mode="widthFix" :style="'width:' + base_data.user_avatar_size * 2 + 'rpx;height:' + base_data.user_avatar_size * 2 + 'rpx;'" data-value="/pages/personal/personal" @tap="url_event" />
<view class="flex-col gap-8" data-value="/pages/personal/personal" @tap="url_event">
<view class="text-size fw-b" :style="user_name_style">{{ (user_info.user || null) !== null ? user_info.user.user_name_view : user.user_name_view }}</view>
<view v-if="id_bool && (user_info.user || null) !== null" class="padding-horizontal-sm padding-vertical-xsss border-radius-sm" :style="number_code_style">ID:{{ user_info.user.number_code }}</view>
</view>
</view>
<view class="flex-row align-c" :style="'gap:' + base_data.img_space * 2 + 'rpx;'">
<view v-for="(item, index) in icon_setting" :key="index" :style="{ width: base_data.img_size + 'px', height: base_data.img_size + 'px' }" :data-value="item.link.page || ''" @tap="url_event">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="border-radius-sm" mode="scaleToFill" :style="{ width: base_data.img_size + 'px', height: base_data.img_size + 'px' }" />
<iconfont v-else :name="'icon-' + item.icon" :size="base_data.img_size * 2 + 'rpx'" color="#666" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
<view class="flex-row jc-sa align-c">
<template v-for="(item, index) in stats_list">
<view v-if="config.includes(item.id)" :key="index" class="tc" :data-value="'/pages/' + item.url + '/' + item.url" @tap="url_event">
<view class="text-size fw-b margin-bottom-sm" :style="stats_number_style">{{ item.value }}</view>
<view class="text-size-xs" :style="stats_name_style">{{ item.name }}</view>
</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, gradient_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
id_bool: true,
stats_list: [
{ id: 'order_count', name: '订单总数', value: '100', url: 'user-order' },
{ id: 'goods_favor_count', name: '商品收藏', value: '10', url: 'user-favor' },
{ id: 'goods_browse_count', name: '我的足迹', value: '1000', url: 'user-goods-browse' },
{ id: 'integral_number', name: '我的积分', value: '10000', url: 'user-integral' },
],
config: ['order_count', 'goods_favor_count', 'goods_browse_count', 'integral_number'],
icon_setting: [
{ id: '1', img: [], icon: '', link: {} },
{ id: '2', img: [], icon: '', link: {} },
],
base_data: {},
// 样式
user_name_style: '',
number_code_style: '',
stats_name_style: '',
stats_number_style: '',
// 用户信息
user_info: {
user: null,
order_count: '0',
goods_favor_count: '0',
goods_browse_count: '0',
message_unread_count: '0',
integral_number: '0',
},
user: {
avatar: app.globalData.data.default_user_head_src,
user_name_view: '用户名',
number_code: '',
},
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
const temp_base_data = {
// 头像
user_avatar_size: new_style.user_avatar_size,
// 人物
user_name_color: new_style.user_name_color,
user_name_weight: new_style.user_name_weight,
user_name_size: new_style.user_name_size,
// id
number_code_color_list: new_style.number_code_color_list,
number_code_color: new_style.number_code_color,
number_code_direction: new_style.number_code_direction,
number_code_weight: new_style.number_code_weight,
number_code_size: new_style.number_code_size,
// 图标设置
img_size: new_style.img_size,
img_space: new_style.img_space,
stats_name_color: new_style.stats_name_color,
stats_name_weight: new_style.stats_name_weight,
stats_name_size: new_style.stats_name_size,
stats_number_color: new_style.stats_number_color,
stats_number_weight: new_style.stats_number_weight,
stats_number_size: new_style.stats_number_size,
};
// id样式
const new_gradient_obj = {
color_list: temp_base_data.number_code_color_list,
direction: temp_base_data.number_code_direction,
};
let temp_stats_list = this.stats_list;
temp_stats_list.map((item) => {
if (new_content.config.includes(item.id)) {
item.value = new_content.data[item.id];
}
});
this.setData({
user_info: new_content.data,
config: new_content.config,
icon_setting: new_content.icon_setting,
base_data: temp_base_data,
id_bool: new_content.config ? new_content.config.includes('number_code') : true,
stats_list: temp_stats_list,
// 人物名称样式
user_name_style: 'color:' + temp_base_data.user_name_color + ';' + 'font-size:' + temp_base_data.user_name_size * 2 + 'rpx;' + 'font-weight:' + temp_base_data.user_name_weight + ';',
number_code_style: gradient_computer(new_gradient_obj) + 'color:' + temp_base_data.number_code_color + ';' + 'font-size:' + temp_base_data.number_code_size * 2 + 'rpx;' + 'font-weight:' + temp_base_data.number_code_weight + ';',
// 统计名称样式
stats_name_style: 'color:' + temp_base_data.stats_name_color + ';' + 'font-size:' + temp_base_data.stats_name_size * 2 + 'rpx;' + 'font-weight:' + temp_base_data.stats_name_weight + ';',
stats_number_style: 'color:' + temp_base_data.stats_number_color + ';' + 'font-size:' + temp_base_data.stats_number_size * 2 + 'rpx;' + 'font-weight:' + temp_base_data.stats_number_weight + ';',
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style></style>

86
components/diy/video.vue Normal file
View File

@@ -0,0 +1,86 @@
<template>
<!-- 视频 -->
<view :style="style_container">
<view :style="style_img_container">
<view class="video pr" :style="style">
<video :src="video" class="wh-auto ht-auto" :poster="video_img" objectFit="cover" style="object-fit: cover"></video>
</view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
video_img: '',
video: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
// 视频比例
this.get_video_height(new_content.video_ratio);
this.setData({
video_img: new_content.video_img.length > 0 ? new_content.video_img[0].url : '',
video: new_content.video.length > 0 ? new_content.video[0].url : '',
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
// 获取视频高度
get_video_height(data) {
uni.getSystemInfo({
success: (res) => {
let video_ratio = ``;
const width = res.windowWidth;
if (data == '4:3') {
video_ratio = `height: ${(((width * 3) / 4) * 2).toFixed(2)}rpx;`;
} else if (data == '1:1') {
video_ratio = `height: ${width * 2}rpx;`;
} else {
// 16:9 保留两位小数
video_ratio = `height: ${(((width * 9) / 16) * 2).toFixed(2)}rpx;`;
}
this.setData({
style: video_ratio,
});
},
});
},
},
};
</script>
<style></style>

View File

@@ -0,0 +1,95 @@
<template>
<view :class="theme_view">
<component-popup :propShow="popup_status" propPosition="bottom" @onclose="popup_close_event">
<view class="emoji-popup bg-white">
<view class="close fr oh">
<view class="fr" @tap.stop="popup_close_event">
<iconfont name="icon-close-o" size="28rpx" color="#999"></iconfont>
</view>
</view>
<view class="emoji-popup-content oh tc">
<block v-if="emoji_list.length > 0">
<block v-for="(item, index) in emoji_list" :key="index">
<view class="emoji-item fl bs-bb text-size-xxxl" @tap="choice_confirm_event" :data-value="item.emoji">{{item.emoji}}</view>
</block>
</block>
</view>
</view>
</component-popup>
</view>
</template>
<script>
const app = getApp();
var common_static_url = app.globalData.get_static_url('common');
import componentPopup from "@/components/popup/popup";
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
common_static_url: common_static_url,
popup_status: false,
emoji_list: []
};
},
components: {
componentPopup
},
created: function() {},
methods: {
// 初始配置
init(config = {}) {
if(!app.globalData.is_single_page_check()) {
return false;
}
this.setData({
popup_status: config.status == undefined ? true : config.status,
emoji_list: config.emoji_list || [],
});
},
// 弹层关闭
popup_close_event(e) {
this.setData({
popup_status: false
});
},
// 选择确认事件
choice_confirm_event(e) {
// 关闭弹窗
this.setData({
popup_status: false
});
// 调用父级
this.$emit('choiceConfirmEvent', e.currentTarget.dataset.value);
}
}
};
</script>
<style>
.emoji-popup {
padding: 20rpx 10rpx 0 10rpx;
position: relative;
}
.emoji-popup .close {
position: absolute;
top: 20rpx;
right: 20rpx;
z-index: 2;
}
.emoji-popup-content {
max-height: 50vh;
overflow-y: scroll;
overflow-x: hidden;
margin-top: 20rpx;
}
.emoji-popup-content .emoji-item {
height: 130rpx;
line-height: 130rpx;
width: 25%;
}
</style>

View File

@@ -0,0 +1,576 @@
<template>
<view :class="theme_view">
<component-popup :propShow="popup_status" propPosition="bottom" @onclose="popup_close_event">
<view class="bg-white">
<view class="close oh padding-horizontal-main padding-top-main">
<view class="fr" @tap.stop="popup_close_event">
<iconfont name="icon-close-o" size="28rpx" color="#999"></iconfont>
</view>
</view>
<view class="plugins-batchbuy-container">
<!-- 批发规则 -->
<view v-if="(plugins_wholesale_data || null) != null" class="padding-main br-b-f5">
<component-wholesale-rules :propCurrencySymbol="propCurrencySymbol" :propData="plugins_wholesale_data"></component-wholesale-rules>
</view>
<!-- 下单规格选择 -->
<view v-if="(goods || null) != null && (batchbuy_data || null) != null && (batchbuy_data.goods_spec_data || null) != null && batchbuy_data.goods_spec_data.length > 0" :class="'spec-data-content oh '+((plugins_wholesale_data || null) != null ? 'wholesale' : '')">
<block v-if="batchbuy_data.is_only_level_one == 0">
<view class="left-nav ht-auto bg-base fl">
<scroll-view :scroll-y="true" class="ht-auto">
<block v-for="(item, index) in batchbuy_data.goods_spec_data" :key="index">
<view :class="'padding-vertical-main tc cp oh pr ' + (nav_active_index == index ? 'bg-white cr-main' : '')" :data-index="index" @tap="nav_event">
<block v-if="(item.images || null) != null">
<image class="dis-inline-block br-f5 radius dis-block spec-images" :src="item.images" mode="widthFix"></image>
<view class="icon-enlarge-submit pa padding-xs lh-sm radius" :data-value="item.images" @tap="spec_images_view_event">
<iconfont name="icon-enlarge" size="26rpx" color="#fff" propClass="lh-sm"></iconfont>
</view>
</block>
<view class="cr-base">{{ item.name }}</view>
<view v-if="(item.badge_total || 0) > 0" class="badge-icon pa">
<component-badge :propNumber="item.badge_total"></component-badge>
</view>
</view>
</block>
</scroll-view>
</view>
<view class="right-conent ht-auto bs-bb fr">
<scroll-view :scroll-y="true" class="ht-auto">
<block v-for="(item, index) in batchbuy_data.goods_spec_data[nav_active_index]['data']" :key="index">
<view class="padding-main oh">
<view class="fl item-left">
<view class="text-size-xs">{{ item.name }}</view>
<view class="sales-price text-size-xs">{{ propCurrencySymbol }}{{ item.base.price }}</view>
</view>
<text v-if="(item.base.inventory || 0) == 0" class="fr text-size-xs cr-grey">{{$t('goods-batch-buy.goods-batch-buy.dsfd98')}}</text>
<view v-else class="tc oh round fr item-right text-size-xs">
<view @tap="batchbuy_goods_buy_number_event" class="number-submit tc cr-grey fl va-m" data-type="0" :data-index="index">-</view>
<input @blur="batchbuy_goods_buy_number_blur" class="number-input tc cr-grey bg-white fl va-m radius-0" type="number" :value="item.buy_number || 0" :data-index="index" />
<view @tap="batchbuy_goods_buy_number_event" class="number-submit tc cr-grey fl va-m" data-type="1" :data-index="index">+</view>
</view>
</view>
</block>
</scroll-view>
</view>
</block>
<block v-else>
<view class="right-conent ht-auto padding-main bs-bb right-conent-only-level-one">
<scroll-view :scroll-y="true" class="ht-auto">
<block v-for="(item, index) in batchbuy_data.goods_spec_data" :key="index">
<view class="oh padding-vertical-main">
<view class="fl item-left">
<view class="pr">
<block v-if="(item.images || null) != null">
<image class="dis-inline-block va-m br-f5 radius margin-right-sm spec-images" :src="item.images" mode="widthFix"></image>
<view class="icon-enlarge-submit pa padding-xs lh-sm radius" :data-value="item.images" @tap="spec_images_view_event">
<iconfont name="icon-enlarge" size="26rpx" color="#fff" propClass="lh-sm"></iconfont>
</view>
</block>
<text class="text-size-xs va-m">{{ item.name }}</text>
</view>
<view class="sales-price text-size-xs margin-top-xs">{{ propCurrencySymbol }}{{ item.base.price }}</view>
</view>
<text v-if="(item.base.inventory || 0) == 0" class="fr text-size-xs cr-grey">{{$t('goods-batch-buy.goods-batch-buy.dsfd98')}}</text>
<view v-else :class="'tc oh round fr item-right text-size-xs margin-top'+((item.images || null) == null ? 'xs' : '')">
<view @tap="batchbuy_goods_buy_number_event" class="number-submit tc cr-grey fl va-m" data-type="0" :data-index="index">-</view>
<input @blur="batchbuy_goods_buy_number_blur" class="tc cr-grey bg-white fl va-m radius-0" type="number" :value="item.buy_number || 0" :data-index="index" />
<view @tap="batchbuy_goods_buy_number_event" class="number-submit tc cr-grey fl va-m" data-type="1" :data-index="index">+</view>
</view>
</view>
</block>
</scroll-view>
</view>
</block>
<view class="confirm-submit pa wh-auto bottom-line-exclude bg-white padding-top-main br-t-f5">
<view class="oh padding-horizontal-main padding-bottom-main cr-grey">
<text class="text-size-xs">
<text>{{$t('buy.buy.g2vt78')}}</text>
<text class="cr-red padding-left-xs padding-right-xs">{{ base_data.kind }}</text>
<text>{{$t('goods-batch-buy.goods-batch-buy.9ectyf')}}</text>
<text class="cr-red padding-left-xs padding-right-xs">{{ base_data.quantity }}</text>
<text>{{ goods.inventory_unit }}</text>
</text>
<text class="text-size-xs fr">{{$t('goods-batch-buy.goods-batch-buy.geq82x')}}<text class="fw-b sales-price">{{ propCurrencySymbol }}{{ base_data.amount_money }}</text></text>
</view>
<view v-if="(opt_button || null) != null && opt_button.length > 0" class="padding-bottom-main">
<view :class="'oh buy-nav-btn-number-' + (opt_button.length || 0)">
<block v-for="(item, index) in opt_button" :key="index">
<view v-if="(item.name || null) != null && (item.type || null) != null" class="item fl bs-bb padding-horizontal-main">
<button :class="'cr-white round text-size-sm bg-' + ((item.color || 'main') == 'main' ? 'main' : 'main-pair')" type="default" @tap="confirm_event" :data-type="item.type" hover-class="none">{{ item.name }}</button>
</view>
</block>
</view>
</view>
</view>
</view>
<block v-else>
<view class="cr-grey tc padding-top-xl padding-bottom-xxxl">{{$t('goods-batch-buy.goods-batch-buy.ypby1k')}}</view>
</block>
</view>
</view>
</component-popup>
</view>
</template>
<script>
const app = getApp();
import base64 from "@/common/js/lib/base64.js";
import componentPopup from "@/components/popup/popup";
import componentBadge from "@/components/badge/badge";
import componentWholesaleRules from '@/components/wholesale-rules/wholesale-rules';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
popup_status: false,
nav_active_index: 0,
goods: null,
batchbuy_data: null,
back_data: null,
opt_button: [],
base_data: {
kind: 0,
quantity: 0,
amount_money: "0.00",
},
// 批发数据
plugins_wholesale_data: null,
};
},
components: {
componentPopup,
componentBadge,
componentWholesaleRules
},
props: {
// 价格符号
propCurrencySymbol: {
type: String,
default: app.globalData.currency_symbol(),
},
// 批发数据
propPluginsWholesaleData: {
type: [Array, Object],
default: null,
}
},
// 页面被展示
created: function () {
this.setData({
plugins_wholesale_data: this.propPluginsWholesaleData,
});
},
methods: {
// 初始化
init(params = {}) {
if (!app.globalData.is_single_page_check()) {
return false;
}
// 购买按钮处理,仅展示购买和购物车
var opt_button = [];
var buy_button = params.buy_button || null;
if(buy_button != null && (buy_button.data || null) != null && buy_button.data.length > 0) {
var arr = ['plugins-batchbuy-button-cart', 'plugins-batchbuy-button-buy'];
for(var i in buy_button.data) {
if(arr.indexOf(buy_button.data[i]['type']) != -1) {
opt_button.push(buy_button.data[i]);
}
}
}
// 设置数据
this.setData({
popup_status: true,
opt_button: opt_button,
goods: params.goods || null,
batchbuy_data: params.batchbuy_data || null,
back_data: params.back_data || null,
plugins_wholesale_data: params.plugins_wholesale_data || null
});
},
// 弹层关闭
popup_close_event(e) {
this.setData({
popup_status: false,
});
},
// 弹层导航切换
nav_event(e) {
this.setData({
nav_active_index: e.currentTarget.dataset.index,
});
},
// 商品批量下单数量操作事件
batchbuy_goods_buy_number_event(e) {
var type = e.currentTarget.dataset.type;
var index = e.currentTarget.dataset.index;
var temp_data = this.batchbuy_data;
var temp_spec_data = parseInt(temp_data.is_only_level_one || 0) == 1 ? temp_data.goods_spec_data[index] : temp_data.goods_spec_data[this.nav_active_index]["data"][index];
var number = parseInt(temp_spec_data.buy_number || 0);
var min = parseInt(temp_spec_data.base.buy_min_number || 0);
// 首次增加使用起购数量
number = type == 0 ? number - 1 : number == 0 && min > 0 ? min : number + 1;
this.batch_goods_buy_number_handle(temp_data, temp_spec_data, index, number);
},
// 商品批量下单数量输入事件
batchbuy_goods_buy_number_blur(e) {
var number = parseInt(e.detail.value) || 0;
var index = e.currentTarget.dataset.index;
var temp_data = this.batchbuy_data;
var temp_spec_data = parseInt(temp_data.is_only_level_one || 0) == 1 ? temp_data.goods_spec_data[index] : temp_data.goods_spec_data[this.nav_active_index]["data"][index];
if (isNaN(number)) {
number = 0;
}
this.batch_goods_buy_number_handle(temp_data, temp_spec_data, index, number);
},
// 商品批量下单数量处理
batch_goods_buy_number_handle(temp_data, temp_spec_data, index, number) {
var min = parseInt(temp_spec_data.base.buy_min_number || 0);
var max = parseInt(temp_spec_data.base.buy_max_number || 0);
var inventory = parseInt(temp_spec_data.base.inventory || 0);
var inventory_unit = this.goods.inventory_unit;
// 是否负数
if (number < 0) {
number = 0;
}
// 不能小于起购数则0
if (number > 0 && min > 0 && number < min) {
number = 0;
app.globalData.showToast(this.$t('recommend-detail.recommend-detail.265vyu') + min + inventory_unit);
}
// 不能超过最大限购
if (max > 0 && number > max) {
number = max;
app.globalData.showToast(this.$t('goods-category.goods-category.z1eh3v') + max + inventory_unit);
}
// 不能超过库存
if (number > inventory) {
number = inventory;
app.globalData.showToast(this.$t('recommend-detail.recommend-detail.2sis3v') + inventory + inventory_unit);
}
temp_spec_data["buy_number"] = number;
if (parseInt(temp_data.is_only_level_one || 0) == 1) {
temp_data.goods_spec_data[index] = temp_spec_data;
} else {
temp_data.goods_spec_data[this.nav_active_index]["data"][index] = temp_spec_data;
}
// 非仅一级则处理一级总数
if (parseInt(temp_data.is_only_level_one || 0) != 1) {
var badge_total = 0;
temp_data.goods_spec_data[this.nav_active_index]["data"].forEach((item) => {
var temp_badge = parseInt(item.buy_number || 0);
if (temp_badge > 0) {
badge_total += temp_badge;
}
});
temp_data.goods_spec_data[this.nav_active_index]["badge_total"] = badge_total;
}
// 总数、汇总
var stock_total = 0;
var kind_total = 0;
var amount_money_total = 0;
temp_data.goods_spec_data.forEach((item) => {
if (parseInt(temp_data.is_only_level_one || 0) == 1) {
var temp_stock = parseInt(item.buy_number || 0);
if (temp_stock > 0) {
stock_total += temp_stock;
kind_total += 1;
amount_money_total += temp_stock * parseFloat(item.base.price);
}
} else {
item.data.forEach((item2) => {
var temp_stock = parseInt(item2.buy_number || 0);
if (temp_stock > 0) {
stock_total += temp_stock;
kind_total += 1;
amount_money_total += temp_stock * parseFloat(item2.base.price);
}
});
}
});
// 设置数据
this.setData({
batchbuy_data: temp_data,
base_data: {
kind: kind_total,
quantity: stock_total,
amount_money: app.globalData.price_two_decimal(amount_money_total),
},
});
// 下单数据
var goods_data = [];
var goods_id = this.goods.id;
temp_data.goods_spec_data.forEach((item) => {
if (parseInt(temp_data.is_only_level_one || 0) == 1) {
goods_data.push({
id: goods_id,
stock: stock_total,
spec: item.spec || "",
});
} else {
item.data.forEach((item2) => {
goods_data.push({
id: goods_id,
stock: stock_total,
spec: item2.spec || "",
});
});
}
});
// 数量更新获取最新数据
uni.request({
url: app.globalData.get_request_url("stock", "goods"),
method: "POST",
data: { goods_data: goods_data },
dataType: "json",
success: (res) => {
if (res.data.code == 0) {
var plugins_wholesale_data = null;
res.data.data.forEach((item) => {
if (item.code == 0) {
// 批发数据
if((item.data.plugins_wholesale_data || null) != null && temp_spec_data.base_id == item.data.spec_base.id) {
plugins_wholesale_data = item.data.plugins_wholesale_data;
}
// 规格数据处理
for (var i1 in temp_data.goods_spec_data) {
if (parseInt(temp_data.is_only_level_one || 0) == 1) {
if (temp_data.goods_spec_data[i1].base.id == item.data.spec_base.id) {
temp_data.goods_spec_data[i1].base.price = item.data.spec_base.price;
temp_data.goods_spec_data[i1].base.original_price = item.data.spec_base.original_price;
temp_data.goods_spec_data[i1].base.inventory = item.data.spec_base.inventory;
}
} else {
for (var i2 in temp_data.goods_spec_data[i1].data) {
if (temp_data.goods_spec_data[i1].data[i2].base.id == item.data.spec_base.id) {
temp_data.goods_spec_data[i1].data[i2].base.price = item.data.spec_base.price;
temp_data.goods_spec_data[i1].data[i2].base.original_price = item.data.spec_base.original_price;
temp_data.goods_spec_data[i1].data[i2].base.inventory = item.data.spec_base.inventory;
}
}
}
}
}
});
this.setData({
batchbuy_data: temp_data,
plugins_wholesale_data: plugins_wholesale_data,
});
// 调用父级
this.$emit("BatchStockSuccessEvent", {
current_spec: temp_spec_data,
goods_data: goods_data,
back_data: res.data.data,
plugins_wholesale_data: plugins_wholesale_data,
});
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
},
// 确认事件
confirm_event(e) {
var user = app.globalData.get_user_info(this, "confirm_event", e);
if (user != false) {
// 获取数据
var goods_data = [];
var goods_id = this.goods.id;
this.batchbuy_data.goods_spec_data.forEach((item) => {
if (parseInt(this.batchbuy_data.is_only_level_one || 0) == 1) {
var buy_number = parseInt(item.buy_number || 0);
if (buy_number > 0) {
goods_data.push({
goods_id: goods_id,
stock: buy_number,
spec: item.spec || "",
});
}
} else {
item.data.forEach((item2) => {
var buy_number = parseInt(item2.buy_number || 0);
if (buy_number > 0) {
goods_data.push({
goods_id: goods_id,
stock: buy_number,
spec: item2.spec || "",
});
}
});
}
});
if (goods_data.length <= 0) {
app.globalData.showToast(this.$t('goods-detail.goods-detail.6brk57'));
return false;
}
// 操作类型
switch (e.currentTarget.dataset.type) {
case "plugins-batchbuy-button-buy":
// 进入订单确认页面
var data = {
buy_type: "goods",
goods_data: encodeURIComponent(base64.encode(JSON.stringify(goods_data))),
};
app.globalData.url_open("/pages/buy/buy?data=" + encodeURIComponent(base64.encode(JSON.stringify(data))));
this.popup_close_event();
break;
// 加入购物车
case "plugins-batchbuy-button-cart":
this.goods_cart_event(goods_data);
break;
default:
app.globalData.showToast(this.$t('goods-batch-buy.goods-batch-buy.7tp1tc'));
}
}
},
// 加入购物车事件
goods_cart_event(goods_data) {
uni.showLoading({
title: this.$t('common.processing_in_text'),
});
uni.request({
url: app.globalData.get_request_url("save", "cart"),
method: "POST",
data: { goods_data: goods_data },
dataType: "json",
success: (res) => {
uni.hideLoading();
if (res.data.code == 0) {
app.globalData.showToast(res.data.msg, "success");
// 调用父级
this.$emit("BatchCartSuccessEvent", {
cart_number: res.data.data.buy_number,
back_data: this.back_data,
});
// 关闭购买弹窗窗口
this.popup_close_event();
} else {
if (app.globalData.is_login_check(res.data, this, "confirm_event")) {
app.globalData.showToast(res.data.msg);
}
}
},
fail: () => {
uni.hideLoading();
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
},
// 规格图片预览事件
spec_images_view_event(e) {
var value = e.currentTarget.dataset.value || null;
if (value != null) {
uni.previewImage({
current: value,
urls: [value],
});
}
}
},
};
</script>
<style>
.plugins-batchbuy-container {
height: 70vh;
padding-bottom: 160rpx;
}
.plugins-batchbuy-container .spec-data-content {
height: calc(100% - 15rpx);
}
.plugins-batchbuy-container .spec-data-content.wholesale {
height: calc(100% - 160rpx);
}
.plugins-batchbuy-container .left-nav {
width: 200rpx;
}
.plugins-batchbuy-container .left-nav .badge-icon {
top: 8rpx;
right: 36rpx;
}
.plugins-batchbuy-container .left-nav .spec-images {
width: 100rpx;
height: 100rpx !important;
}
.plugins-batchbuy-container .icon-enlarge-submit {
left: 55rpx;
top: 30rpx;
background: rgb(0, 0, 0, 30%);
}
.plugins-batchbuy-container .right-conent {
width: calc(100% - 210rpx);
}
.plugins-batchbuy-container .right-conent .item-left {
width: calc(100% - 290rpx);
}
.plugins-batchbuy-container .item-right {
background: #fbfbfb;
border: 1px solid #f0f0f0;
}
.plugins-batchbuy-container .item-right .number-submit {
width: 80rpx;
font-weight: bold;
}
.plugins-batchbuy-container .item-right .number-input {
width: 50px;
}
.plugins-batchbuy-container .item-right .number-submit,
.plugins-batchbuy-container .item-right .number-input {
padding: 0;
height: 60rpx;
line-height: 60rpx;
}
.plugins-batchbuy-container .right-conent-only-level-one {
width: 100%;
}
.plugins-batchbuy-container .right-conent-only-level-one .spec-images {
width: 100rpx;
height: 100rpx !important;
}
.plugins-batchbuy-container .right-conent-only-level-one .icon-enlarge-submit {
left: 5rpx;
top: 5rpx;
}
.plugins-batchbuy-container .confirm-submit {
left: 0;
bottom: 0;
}
.plugins-batchbuy-container .buy-nav-btn-number-0 .item,
.plugins-batchbuy-container .buy-nav-btn-number-1 .item {
width: 100% !important;
}
.plugins-batchbuy-container .buy-nav-btn-number-2 .item {
width: 50% !important;
}
.plugins-batchbuy-container .buy-nav-btn-number-3 .item {
width: 33.33% !important;
}
.plugins-batchbuy-container .buy-nav-btn-number-4 .item {
width: 25% !important;
}
</style>

View File

@@ -0,0 +1,945 @@
<template>
<view :class="theme_view" class="z-i-deep">
<component-popup :propShow="popup_status" propPosition="bottom" @onclose="popup_close_event" :propIndex="propIndex">
<view class="goods-spec-choice-container padding-main bg-white pr">
<view class="close fr oh">
<view class="fr" @tap.stop="popup_close_event">
<iconfont name="icon-close-o" size="28rpx" color="#999"></iconfont>
</view>
</view>
<!-- 规格基础信息 -->
<view class="goods-spec-base oh br-b pr">
<image :src="goods_spec_base_images" mode="scaleToFill" class="spec-images radius br" @tap="goods_detail_images_view_event" :data-value="goods_spec_base_images"></image>
<view class="goods-spec-base-content">
<view class="goods-price">
<view v-if="(goods.show_field_price_status || 0) == 1">
<text class="sales-price va-m">{{ goods.show_price_symbol }}{{ goods_spec_base_price }}</text>
<text class="cr-grey text-size-xs va-m">{{ goods.show_price_unit }}</text>
</view>
<view v-if="(goods.show_field_original_price_status || 0) == 1 && (goods_spec_base_original_price || null) != null && goods_spec_base_original_price != 0" class="original-price margin-top-sm">{{ goods.show_original_price_symbol }}{{ goods_spec_base_original_price }}{{ goods.show_original_price_unit }}</view>
</view>
<view v-if="(goods.show_inventory_status || 0) == 1" class="inventory text-size-xs margin-top">
<text class="cr-grey">{{ $t('goods-detail.goods-detail.1s79t4') }}</text>
<text class="cr-base">{{ goods_spec_base_inventory }}</text>
<text class="cr-grey">{{ goods.inventory_unit }}</text>
</view>
</view>
</view>
<block v-if="(goods.is_exist_many_spec || 0) == 1 && goods_spec_choose.length == 0">
<view class="padding-top-xxxl padding-bottom-xxxl tc cr-red">{{ $t('goods-buy.goods-buy.ufdm25') }}</view>
</block>
<block v-else>
<view class="goods-spec-choice-content">
<!-- 商品规格 -->
<view v-if="goods_spec_choose.length > 0" class="goods-spec-choose">
<view v-for="(item, key) in goods_spec_choose" :key="key" class="item padding-top-xxl padding-bottom-xxl">
<view class="text-size-sm">{{ item.name }}</view>
<view v-if="item.value.length > 0" class="spec margin-top-sm">
<block v-for="(items, keys) in item.value" :key="keys">
<button @tap.stop="goods_spec_choice_event" :data-key="key" :data-keys="keys" type="default" size="mini" hover-class="none" :class="'spec-btn round ' + items.is_active + ' ' + items.is_dont + ' ' + items.is_disabled">
<image v-if="(items.images || null) != null" :src="items.images" mode="scaleToFill" class="spec-images va-m dis-inline-block round margin-right-sm"></image>
<text class="va-m">{{ items.name }}</text>
</button>
</block>
</view>
</view>
</view>
<!-- 购买数量 -->
<view class="goods-buy-number oh pr margin-top-xl margin-bottom-xxl">
<view class="fl margin-top">{{ $t('goods-buy.goods-buy.737wzz') }}</view>
<view class="number-content tc oh round">
<view @tap="goods_buy_number_event" class="number-submit tc cr-grey fl va-m" data-type="0">-</view>
<input @blur="goods_buy_number_blur" class="number-input tc cr-grey bg-white fl va-m radius-0" type="number" :value="buy_number" />
<view @tap="goods_buy_number_event" class="number-submit tc cr-grey fl va-m" data-type="1">+</view>
</view>
</view>
</view>
<view v-if="(opt_button || null) != null && opt_button.length > 0" class="padding-bottom-main">
<view :class="'oh buy-nav-btn-number-' + (opt_button.length || 0)">
<block v-for="(item, index) in opt_button" :key="index">
<view v-if="(item.name || null) != null && (item.type || null) != null" class="item fl bs-bb padding-horizontal-main">
<button :class="'cr-white round text-size-sm bg-' + ((item.color || 'main') == 'main' ? 'main' : 'main-pair')" type="default" @tap="spec_confirm_event" :data-value="item.value" :data-type="item.type" :data-business="item.business || ''" hover-class="none">{{ item.name }}</button>
</view>
</block>
</view>
</view>
<button v-else class="bg-main br-main cr-white text-size-sm round" type="default" @tap.stop="spec_confirm_event" hover-class="none">{{ $t('index.index.7w75zb') }}</button>
</block>
</view>
</component-popup>
</view>
</template>
<script>
const app = getApp();
import base64 from '@/common/js/lib/base64.js';
import componentPopup from '@/components/popup/popup';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
params: {},
back_data: {},
popup_status: false,
goods_spec_base_price: 0,
goods_spec_base_original_price: 0,
goods_spec_base_inventory: 0,
goods_spec_base_buy_min_number: 0,
goods_spec_base_buy_max_number: 0,
goods_spec_base_images: '',
goods: {},
goods_spec_choose: [],
buy_number: 1,
buy_event_type: 'cart',
opt_button: [],
is_direct_cart: 0,
is_success_tips: 1,
// 选中规格临时定时变量
spec_selected_timer: null,
spec_selected_timerout: null,
// 智能工具插件
plugins_intellectstools_config: app.globalData.get_config('plugins_base.intellectstools.data'),
};
},
components: {
componentPopup,
},
props: {
propParams: {
type: [String, Object],
default: () => {
return {};
},
},
propCurrencySymbol: {
type: String,
default: app.globalData.currency_symbol(),
},
// 弹窗层级
propIndex: {
type: Number,
default: 100,
},
},
created: function () {},
methods: {
// 初始化
init(goods = {}, params = {}, back_data = null) {
if (!app.globalData.is_single_page_check()) {
return false;
}
params = params || {};
// 状态默认开启弹窗
var status = true;
// 商品可选规格
var goods_spec_choose = (goods.specifications || null) != null ? goods.specifications.choose || [] : [];
// 无规格是否直接操作
var is_direct_cart = 0;
if ((params.is_direct_cart || 0) == 1 && parseInt(goods.is_exist_many_spec || 0) == 0 && goods_spec_choose.length == 0) {
status = false;
is_direct_cart = 1;
}
// 是否成功提示、默认提示
var is_success_tips = params.is_success_tips == undefined ? 1 : params.is_success_tips || 0;
// 直接加购、并且用户已经存在购物车则依次+1
if (is_direct_cart == 1 && parseInt(goods.user_cart_count || 0) > 0) {
var buy_number = 1;
} else {
var buy_number = goods.buy_min_number || 1;
}
// 购买按钮处理,仅展示购买和购物车
var opt_button = [];
var buy_button = params.buy_button || null;
if (buy_button != null && (buy_button.data || null) != null && buy_button.data.length > 0) {
var arr = ['buy', 'cart', 'show'];
for (var i in buy_button.data) {
if (arr.indexOf(buy_button.data[i]['type']) != -1) {
opt_button.push(buy_button.data[i]);
}
}
}
// 设置数据
this.setData({
popup_status: status,
params: params || {},
back_data: back_data,
goods: goods || {},
goods_spec_choose: goods_spec_choose,
goods_spec_base_price: goods.price,
goods_spec_base_original_price: goods.original_price || 0,
goods_spec_base_inventory: goods.inventory,
goods_spec_base_images: goods.images,
buy_number: buy_number,
buy_event_type: params.buy_event_type || 'cart',
opt_button: opt_button,
is_direct_cart: is_direct_cart,
is_success_tips: is_success_tips,
});
// 初始化不能选择规格处理
this.spec_handle_dont(0, 1);
// 获取规格详情
this.get_spec_detail();
// 规格选中处理
this.selected_spec_handle();
// 是否直接操作加入购物车
if (is_direct_cart) {
this.spec_confirm_event();
}
},
// 规格选中处理
selected_spec_handle() {
var temp_spec_choose = this.goods_spec_choose;
if (temp_spec_choose.length > 0) {
// 是否已选择
var active_count = 0;
for (var i in temp_spec_choose) {
for (var k in temp_spec_choose[i]['value']) {
if ((temp_spec_choose[i]['value'][k]['is_active'] || null) != null) {
active_count++;
}
}
}
if (active_count > 0) {
return false;
}
// 是否指定规格初始化
var spec = (this.propParams || null) != null && (this.propParams.spec || null) != null ? this.propParams.spec : null;
if (spec != null) {
this.appoint_selected_spec_handle(temp_spec_choose, spec);
} else {
// 是否默认选中第一个规格、、已存在指定规格初始化则不走默认选择第一个规格
this.plugins_intellectstools_selected_spec_handle(temp_spec_choose);
}
}
},
// 指定规格初始化
appoint_selected_spec_handle(spec_choose, spec) {
spec = decodeURIComponent(spec).split('|');
if (spec.length == spec_choose.length) {
// 选择处理
var self = this;
// 销毁之前的任务
clearInterval(self.spec_selected_timer);
clearInterval(self.spec_selected_timerout);
// 必须存在购买和加入购物车任意一个、规格必须多个
var sku_count = app.globalData.get_length(spec_choose);
// 先清除价格展示信息
self.setData({
goods_spec_base_price: '...',
goods_spec_base_original_price: '...',
});
var num = 0;
var timer = setInterval(function () {
for (var i in spec_choose) {
// 清除价格展示信息、避免获取价格类型赋值
self.setData({
goods_spec_base_price: '...',
goods_spec_base_original_price: '...',
});
// 必须不存在已选择项
var active =
spec_choose[i]['value']
.map(function (v) {
return v.is_active;
})
.join('') || null;
if (active == null) {
// 不能选择规格处理
self.spec_handle_dont(i);
// 规格选择处理
var temp_spec = spec[i];
var status = false;
for (var k in spec_choose[i]['value']) {
// 必须是可选和未选
if (!status && (spec_choose[i]['value'][k]['is_disabled'] || null) == null && (spec_choose[i]['value'][k]['is_dont'] || null) == null && temp_spec == spec_choose[i]['value'][k]['name']) {
self.goods_spec_choice_handle(i, k);
status = true;
num++;
}
}
}
}
if (num >= sku_count) {
clearInterval(self.spec_selected_timer);
}
}, 100);
var timerout = setTimeout(function () {
clearInterval(self.spec_selected_timerout);
}, 20000);
self.setData({
spec_selected_timer: timer,
spec_selected_timerout: timerout,
});
}
},
// 默认选中第一个规格 - 智能工具箱插件
plugins_intellectstools_selected_spec_handle(spec_choose) {
// 选择处理
var self = this;
// 销毁之前的任务
clearInterval(self.spec_selected_timer);
clearInterval(self.spec_selected_timerout);
// 读取智能工具插件配置、是否开启
var config = self.plugins_intellectstools_config || null;
if (config != null && (config.is_goods_detail_selected_first_spec || 0) == 1) {
// 必须存在购买和加入购物车任意一个、规格必须多个
var sku_count = app.globalData.get_length(spec_choose);
// 先清除价格展示信息
self.setData({
goods_spec_base_price: '...',
goods_spec_base_original_price: '...',
});
var num = 0;
var timer = setInterval(function () {
for (var i in spec_choose) {
// 清除价格展示信息、避免获取价格类型赋值
self.setData({
goods_spec_base_price: '...',
goods_spec_base_original_price: '...',
});
// 必须不存在已选择项
var active =
spec_choose[i]['value']
.map(function (v) {
return v.is_active;
})
.join('') || null;
if (active == null) {
// 不能选择规格处理
self.spec_handle_dont(i);
// 规格选择处理
var status = false;
for (var k in spec_choose[i]['value']) {
// 必须是可选和未选
if (!status && (spec_choose[i]['value'][k]['is_disabled'] || null) == null && (spec_choose[i]['value'][k]['is_dont'] || null) == null) {
self.goods_spec_choice_handle(i, k);
status = true;
num++;
}
}
}
}
if (num >= sku_count) {
clearInterval(self.spec_selected_timer);
}
}, 100);
var timerout = setTimeout(function () {
clearInterval(self.spec_selected_timerout);
}, 20000);
self.setData({
spec_selected_timer: timer,
spec_selected_timerout: timerout,
});
}
},
// 弹层关闭
popup_close_event(e) {
this.setData({
popup_status: false,
});
},
// 规格事件
goods_spec_choice_event(e) {
var key = e.currentTarget.dataset.key || 0;
var keys = e.currentTarget.dataset.keys || 0;
this.goods_spec_choice_handle(key, keys);
},
// 规格选择处理
goods_spec_choice_handle(key, keys) {
var temp_spec = this.goods_spec_choose;
var temp_images = this.goods_spec_base_images;
// 不能选择和禁止选择跳过
if ((temp_spec[key]['value'][keys]['is_dont'] || null) == null && (temp_spec[key]['value'][keys]['is_disabled'] || null) == null) {
// 规格选择
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if ((temp_spec[i]['value'][k]['is_dont'] || null) == null && (temp_spec[i]['value'][k]['is_disabled'] || null) == null) {
if (key == i) {
if (keys == k && (temp_spec[i]['value'][k]['is_active'] || null) == null) {
temp_spec[i]['value'][k]['is_active'] = 'cr-white bg-main br-main';
if ((temp_spec[i]['value'][k]['images'] || null) != null) {
temp_images = temp_spec[i]['value'][k]['images'];
}
} else {
temp_spec[i]['value'][k]['is_active'] = '';
}
}
}
}
}
this.setData({
goods_spec_choose: temp_spec,
goods_spec_base_images: temp_images,
});
// 不能选择规格处理
this.spec_handle_dont(key);
// 获取下一个规格类型
this.get_spec_type(key);
// 获取规格详情
this.get_spec_detail();
// 规格选择回调
this.$emit('SpecChoiceEvent', {
goods_id: this.goods.id,
spec: this.choice_spec_data(),
goods_spec_choose: this.goods_spec_choose,
});
}
},
// 获取下一个规格类型
get_spec_type(key) {
var temp_spec = this.goods_spec_choose;
var active_index = parseInt(key) + 1;
var sku_count = app.globalData.get_length(temp_spec);
if (active_index <= 0 || active_index >= sku_count) {
return false;
}
// 获取规格值
var spec = this.choice_spec_data();
if (spec.length <= 0) {
return false;
}
// 获取数据
var data = this.params;
data['id'] = this.goods.id;
data['spec'] = JSON.stringify(spec);
uni.request({
url: app.globalData.get_request_url('spectype', 'goods'),
method: 'POST',
data: data,
dataType: 'json',
success: (res) => {
if (res.data.code == 0) {
var spec_type = res.data.data.spec_type;
var spec_count = spec.length;
var index = spec_count > 0 ? spec_count : 0;
if (index < sku_count) {
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if (index == i) {
temp_spec[i]['value'][k]['is_dont'] = '';
var temp_value = temp_spec[i]['value'][k]['name'];
var temp_status = false;
for (var t in spec_type) {
if (spec_type[t] == temp_value) {
temp_status = true;
break;
}
}
if (temp_status == true) {
temp_spec[i]['value'][k]['is_disabled'] = '';
} else {
temp_spec[i]['value'][k]['is_disabled'] = 'spec-items-disabled';
}
}
}
}
this.setData({
goods_spec_choose: temp_spec,
});
}
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
},
// 选择规格数据
choice_spec_data() {
var spec = [];
var temp_spec = this.goods_spec_choose;
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if ((temp_spec[i]['value'][k]['is_active'] || null) != null) {
spec.push({
type: temp_spec[i]['name'],
value: temp_spec[i]['value'][k]['name'],
});
break;
}
}
}
return spec;
},
// 获取规格详情
get_spec_detail() {
// 获取规格值
var spec = this.goods_selected_spec();
// 存在规格的时候是否已完全选择规格
var sku_count = this.goods_spec_choose.length;
var active_count = spec.length;
if (spec.length <= 0 || active_count < sku_count) {
var buy_number = parseInt(this.buy_number);
var buy_min_number = parseInt(this.goods.buy_min_number || 1);
var buy_max_number = parseInt(this.goods.buy_max_number || 0);
if (buy_number < buy_min_number) {
buy_number = buy_min_number;
}
if (buy_max_number > 0 && buy_number > buy_max_number) {
buy_number = buy_max_number;
}
this.setData({
goods_spec_base_price: this.goods.price,
goods_spec_base_original_price: this.goods.original_price || 0,
goods_spec_base_inventory: this.goods.inventory,
goods_spec_base_buy_min_number: 0,
goods_spec_base_buy_max_number: 0,
buy_number: buy_number,
});
return false;
}
// 获取数据
var data = this.params;
data['id'] = this.goods.id;
data['spec'] = JSON.stringify(spec);
data['stock'] = this.buy_number;
uni.request({
url: app.globalData.get_request_url('specdetail', 'goods'),
method: 'POST',
data: data,
dataType: 'json',
success: (res) => {
if (res.data.code == 0) {
this.goods_spec_detail_back_handle(res.data.data);
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
},
// 商品规格详情返回数据处理
goods_spec_detail_back_handle(data) {
var spec_base = data.spec_base;
var buy_number = parseInt(this.buy_number);
var spec_buy_min_number = parseInt(spec_base.buy_min_number || 1);
var spec_buy_max_number = parseInt(spec_base.buy_max_number || 0);
if (spec_buy_min_number > 0 && buy_number < spec_buy_min_number) {
buy_number = spec_buy_min_number;
}
if (spec_buy_max_number > 0 && buy_number > spec_buy_max_number) {
buy_number = spec_buy_max_number;
}
this.setData({
goods_spec_base_price: spec_base.price,
goods_spec_base_original_price: spec_base.original_price || 0,
goods_spec_base_inventory: parseInt(spec_base.inventory || 0),
goods_spec_base_buy_min_number: spec_buy_min_number,
goods_spec_base_buy_max_number: spec_buy_max_number,
buy_number: buy_number,
});
// 调用父级
this.$emit("BackSuccessEvent", {
buy_number: this.buy_number,
base_price: this.goods_spec_base_price,
base_original_price: this.goods_spec_base_original_price,
base_inventory: this.goods_spec_base_inventory,
base_buy_min_number: this.goods_spec_base_buy_min_number,
base_buy_max_number: this.goods_spec_base_buy_max_number,
back_data: data
});
},
// 已选的商品规格
goods_selected_spec() {
var spec = [];
var temp_spec = this.goods_spec_choose;
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if ((temp_spec[i]['value'][k]['is_active'] || null) != null) {
spec.push({
type: temp_spec[i]['name'],
value: temp_spec[i]['value'][k]['name'],
});
break;
}
}
}
return spec;
},
// 不能选择规格处理
spec_handle_dont(key, is_init = 0) {
var temp_spec = this.goods_spec_choose || [];
if (temp_spec.length <= 0) {
return false;
}
// 是否不能选择
key = parseInt(key);
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if (i > key) {
temp_spec[i]['value'][k]['is_dont'] = 'spec-dont-choose';
temp_spec[i]['value'][k]['is_disabled'] = '';
temp_spec[i]['value'][k]['is_active'] = '';
} else {
if (is_init == 1) {
temp_spec[i]['value'][k]['is_active'] = '';
}
}
// 当只有一个规格的时候
if (key == 0 && temp_spec.length == 1) {
temp_spec[i]['value'][k]['is_disabled'] = (temp_spec[i]['value'][k]['is_only_level_one'] || null) != null && parseInt(temp_spec[i]['value'][k]['inventory'] || 0) <= 0 ? 'spec-items-disabled' : '';
}
}
}
this.setData({
goods_spec_choose: temp_spec,
});
},
// 数量输入事件
goods_buy_number_blur(e) {
var number = parseInt(e.detail.value) || 1;
if (isNaN(number)) {
number = this.goods.buy_min_number || 1;
}
this.goods_buy_number_func(number);
},
// 数量操作事件
goods_buy_number_event(e) {
var type = parseInt(e.currentTarget.dataset.type || 0);
var temp_number = parseInt(this.buy_number);
var number = type == 0 ? temp_number - 1 : temp_number + 1;
this.goods_buy_number_func(number);
},
// 数量处理方法
goods_buy_number_func(number) {
var buy_min_number = parseInt(this.goods.buy_min_number || 1);
var buy_max_number = parseInt(this.goods.buy_max_number || 0);
var spec_buy_min_number = parseInt(this.goods_spec_base_buy_min_number || 0);
var spec_buy_max_number = parseInt(this.goods_spec_base_buy_max_number || 0);
var inventory = parseInt(this.goods_spec_base_inventory || 0);
var inventory_unit = this.goods.inventory_unit;
// 最小起购数量
var min = spec_buy_min_number > 0 ? spec_buy_min_number : buy_min_number;
if (min > 0 && number < min) {
number = min;
app.globalData.showToast(this.$t('recommend-detail.recommend-detail.265vyu') + min + inventory_unit);
}
// 最大购买数量
var max = spec_buy_max_number > 0 ? spec_buy_max_number : buy_max_number;
if (max > 0 && number > max) {
number = max;
app.globalData.showToast(this.$t('goods-category.goods-category.z1eh3v') + max + inventory_unit);
}
// 是否超过库存数量
if (number > inventory) {
number = inventory;
app.globalData.showToast(this.$t('recommend-detail.recommend-detail.2sis3v') + inventory + inventory_unit);
}
this.setData({ buy_number: number });
// 存在规格的时候是否已完全选择规格
var spec = this.goods_selected_spec();
var sku_count = this.goods_spec_choose.length;
var active_count = spec.length;
if (sku_count > 0 && active_count < sku_count) {
return false;
}
// 获取数据
var data = this.params;
data['id'] = this.goods.id;
data['spec'] = spec;
data['stock'] = this.buy_number;
uni.request({
url: app.globalData.get_request_url('stock', 'goods'),
method: 'POST',
data: data,
dataType: 'json',
success: (res) => {
if (res.data.code == 0) {
this.goods_spec_detail_back_handle(res.data.data);
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
},
// 详情图片查看
goods_detail_images_view_event(e) {
var value = e.currentTarget.dataset.value || null;
if (value != null) {
uni.previewImage({
current: value,
urls: [value],
});
}
},
// 规格确认事件
spec_confirm_event(e = null) {
var user = app.globalData.get_user_info(this, 'spec_confirm_event');
if (user != false) {
// 规格
var temp_data = this.goods_spec_choose;
var sku_count = temp_data.length;
var active_count = 0;
var spec = [];
if (sku_count > 0) {
for (var i in temp_data) {
for (var k in temp_data[i]['value']) {
if ((temp_data[i]['value'][k]['is_active'] || null) != null) {
active_count++;
spec.push({
type: temp_data[i]['name'],
value: temp_data[i]['value'][k]['name'],
});
}
}
}
if (active_count < sku_count) {
app.globalData.showToast(this.$t('goods-detail.goods-detail.6brk57'));
return false;
}
}
// 操作类型
var type = (e == null) ? this.buy_event_type : (e.currentTarget.dataset.type || this.buy_event_type);
var value = (e == null) ? null : (e.currentTarget.dataset.value || null);
var business = (e == null) ? null : (e.currentTarget.dataset.business || null);
switch (type) {
// 展示型、商品页面规格选择展示型 拨打电话操作
case 'show':
case 'spec-show':
app.globalData.call_tel(value || app.globalData.get_config('config.common_app_customer_service_tel'));
break;
// 购买
case 'buy':
// 进入订单确认页面
var data = {
buy_type: 'goods',
goods_data: encodeURIComponent(
base64.encode(
JSON.stringify([
{
goods_id: this.goods.id,
stock: this.buy_number,
spec: spec,
},
])
)
)
};
// 转换数据
var data_params = encodeURIComponent(base64.encode(JSON.stringify(data)));
// 是否互联网医院插件-开处方
if(business == 'plugins-hospital') {
app.globalData.url_open('/pages/plugins/hospital/prescription/prescription?data=' + data_params);
} else {
// 默认进去订单确认页面
app.globalData.url_open('/pages/buy/buy?data=' + data_params);
}
// 关闭弹窗
this.popup_close_event();
break;
// 加入购物车
case 'cart':
this.goods_cart_event(spec);
break;
default:
app.globalData.showToast(this.$t('goods-buy.goods-buy.4maexq') + type + ')');
}
}
},
// 加入购物车事件
goods_cart_event(spec) {
var data = this.params;
data['goods_id'] = this.goods.id;
data['spec'] = JSON.stringify(spec);
data['stock'] = this.buy_number;
uni.request({
url: app.globalData.get_request_url('save', 'cart'),
method: 'POST',
data: data,
dataType: 'json',
success: (res) => {
if (res.data.code == 0) {
// 是否成功提示
if (this.is_success_tips == 1) {
app.globalData.showToast(res.data.msg, 'success');
}
var cart_number = res.data.data.buy_number;
// 调用父级
this.$emit('CartSuccessEvent', {
goods_id: this.goods.id,
spec: spec,
stock: this.buy_number,
cart_number: cart_number,
back_data: this.back_data,
goods_spec_choose: this.goods_spec_choose,
});
// 是否返回定义来源返回
if (parseInt(this.params.is_opt_buy_status || 0) == 1 && this.is_opt_back == 1) {
setTimeout(function () {
uni.navigateBack();
}, 1000);
} else {
// 关闭购买弹窗窗口
this.popup_close_event();
}
} else {
if (app.globalData.is_login_check(res.data, this, 'spec_confirm_event')) {
app.globalData.showToast(res.data.msg);
}
}
},
fail: () => {
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
},
},
};
</script>
<style>
.goods-spec-choice-container .close {
position: absolute;
top: 20rpx;
right: 20rpx;
z-index: 2;
}
.goods-spec-base {
height: 230rpx;
}
.goods-spec-base .spec-images {
width: 200rpx;
height: 200rpx;
position: absolute;
left: 0;
top: 0;
}
.goods-spec-base-content {
position: absolute;
left: 220rpx;
top: 0;
}
.goods-spec-choice-content {
max-height: 50vh;
overflow-y: scroll;
overflow-x: hidden;
margin-top: 20rpx;
}
.goods-spec-choice-container .item .spec .spec-btn {
background-color: #f5f5f5;
color: #666;
border: 1px solid #ccc;
}
.goods-spec-choice-container .item .spec .spec-btn:not(:last-child) {
margin-right: 25rpx;
}
.goods-spec-choice-container .item .spec .spec-btn .spec-images {
width: 40rpx;
height: 40rpx !important;
}
.goods-spec-choice-container .spec-dont-choose {
color: #b4b3b3 !important;
background-color: #ffffff !important;
border: 1px solid #ebeaea !important;
}
.goods-spec-choice-container .spec-dont-choose .spec-images {
opacity: 0.5;
}
.goods-spec-choice-container .spec-items-disabled {
color: #d2cfcf !important;
background-color: #ffffff !important;
border: 1px dashed #d5d5d5 !important;
}
.goods-spec-choice-container .spec-items-disabled .spec-images {
opacity: 0.3;
}
.goods-spec-choice-container .goods-buy-number {
height: 70rpx;
}
.goods-spec-choice-container .number-content {
position: absolute;
right: 20rpx;
top: 0;
background: #eee;
border: 1px solid #eee;
}
.goods-spec-choice-container .number-content .number-submit {
width: 80rpx;
font-weight: bold;
}
.goods-spec-choice-container .number-content .number-input {
width: 50px;
}
.goods-spec-choice-container .number-content .number-submit,
.goods-spec-choice-container .number-content .number-input {
padding: 0;
height: 60rpx;
line-height: 60rpx;
}
.goods-spec-choice-container .buy-nav-btn-number-0 .item,
.goods-spec-choice-container .buy-nav-btn-number-1 .item {
width: 100% !important;
}
.goods-spec-choice-container .buy-nav-btn-number-2 .item {
width: 50% !important;
}
.goods-spec-choice-container .buy-nav-btn-number-3 .item {
width: 33.33% !important;
}
.goods-spec-choice-container .buy-nav-btn-number-4 .item {
width: 25% !important;
}
</style>

View File

@@ -0,0 +1,127 @@
<template>
<view :class="theme_view">
<block v-if="(propData || null) != null && propData.length > 0">
<view v-for="(item, index) in propData" :key="index" class="goods-comment-item flex-row" :class="propClass">
<image class="avatar dis-block margin-right-xs" :src="item.user.avatar || common_static_url + 'default-user.png'" mode="aspectFit"></image>
<view class="base-nav flex-1 flex-width margin-left-sm" :class="propIsReply ? 'is-reply' : ''">
<view class="oh nav padding-bottom-sm">
<view class="">
<text class="va-m">{{ item.user.user_name_view }}</text>
<view class="dis-inline-block va-m margin-left-sm">
<uni-rate :value="item.rating" :readonly="true" :is-fill="false" :size="14" />
</view>
<view class="fr">
<text class="cr-grey text-size-xs">{{ item.add_time_date }}</text>
</view>
</view>
</view>
<view class="base-content oh padding-vertical-sm">
<view class="content cr-base text-size-sm">{{ item.content }}</view>
<view v-if="(item.images || null) != null && item.images.length > 0" class="image-list oh margin-top-lg">
<block v-for="(iv, ix) in item.images" :key="ix">
<image class="image br radius" @tap="comment_images_show_event" :data-index="index" :data-ix="ix" :src="iv" mode="aspectFit"></image>
</block>
</view>
<view v-if="(item.msg || null) != null" class="spec cr-grey margin-top-lg">{{ item.msg }}</view>
<block v-if="propIsReply">
<view v-if="item.is_reply == 1 && (item.reply || null) != null" class="reply br-t-dashed margin-top-sm padding-top-sm text-size-sm">
<text class="cr-base">{{$t('goods-comments.goods-comments.s65197')}}</text>
<text class="reply-desc cr-main-pair">{{ item.reply }}</text>
</view>
</block>
</view>
</view>
</view>
</block>
<block v-else>
<view class="tc spacing-mb flex-row jc-c align-c margin-top-xxxxl">
<image :src="common_static_url + 'no-comment.png'" mode="widthFix" class="no-comment margin-right-main" />
<view class="cr-grey-d">{{$t('goods-comments.goods-comments.1p1r2e')}}</view>
</view>
</block>
</view>
</template>
<script>
const app = getApp();
var common_static_url = app.globalData.get_static_url('common');
export default {
props: {
propData: {
type: [Array,String],
default: '',
},
// 是否需要显示管理员回复
propIsReply: {
type: Boolean,
default: false,
},
// 额外样式控制
propClass: {
type: String,
default: '',
},
},
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
common_static_url: common_static_url,
};
},
created: function () {},
methods: {
// 评价图片预览
comment_images_show_event(e) {
var index = e.currentTarget.dataset.index;
var ix = e.currentTarget.dataset.ix;
uni.previewImage({
current: this.propData[index]['images'][ix],
urls: this.propData[index]['images'],
});
},
},
};
</script>
<style scoped>
/**
* 商品评价
*/
.goods-comment-item {
padding-bottom: 10rpx;
margin-bottom: 20rpx;
}
.goods-comment-item .avatar {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
border: 1px solid #e2e2e2;
}
.goods-comment-item .base-nav {
border-bottom: 2rpx solid #f5f5f5;
}
.goods-comment-item .base-nav.is-reply {
border: 0;
}
.goods-comment-item:last-of-type {
margin-bottom: 0;
}
.goods-comment-item:last-of-type .base-nav {
border: 0;
}
.goods-comment-item .base-content .content,
.goods-comment-item .base-content .reply {
line-height: 46rpx;
}
.goods-comment-item .base-content .image-list .image {
width: 100rpx;
height: 100rpx;
}
.goods-comment-item .base-content .image-list .image:not(:last-child) {
margin-right: 10rpx;
}
.no-comment {
width: 174rpx;
}
</style>

View File

@@ -0,0 +1,395 @@
<template>
<view :class="theme_view">
<view class="plugins-goods" :class="data.style_type == 2 ? propStyleTypeTowClass : ''" v-if="(data || null) != null && (data.goods_list || null) != null && data.goods_list.length > 0">
<view v-if="(data.title || null) != null || (data.vice_title || null) != null" class="spacing-nav-title flex-row align-c jc-sb text-size-xs">
<view class="title-left">
<text v-if="(data.title || null) != null" class="text-wrapper" :class="data.style_type == 2 ? '' : 'title-left-border'" :style="'color:' + (data.color || '#333') + ';'">{{ data.title }}</text>
<text v-if="(data.vice_title || null) != null" class="vice-name margin-left-sm cr-grey-9">{{ data.vice_title }}</text>
</view>
<text v-if="(data[propMoreUrlKey] || null) != null" :data-value="data[propMoreUrlKey]" @tap="url_event" class="arrow-right padding-right cr-grey cp">{{ $t('common.more') }}</text>
</view>
<view class="wh-auto oh pr goods-list">
<!-- 默认图文 -->
<block v-if="(data.style_type || 0) == 0">
<view class="goods-data-list">
<view v-for="(item, index) in data.goods_list" :key="index" class="item oh padding-main border-radius-main bg-white oh pr spacing-mb">
<!-- 商品主体内容 -->
<view class="cp" :data-index="index" :data-value="item.goods_url" @tap="goods_event">
<image class="goods-img fl radius" :src="item.images" mode="aspectFit"></image>
<view class="base fr">
<view class="multi-text">{{ item.title }}</view>
<view v-if="(item.simple_desc || null) != null" class="cr-grey single-text margin-top-sm text-size-sm">{{ item.simple_desc }}</view>
<view v-if="(item.show_field_price_status || 0) == 1"class="flex-row jc-sb align-c margin-top-main pr">
<block v-if="(propPriceField || null) != null && item[propPriceField] != undefined">
<view class="base-bottom">
<text v-if="propIsShowPriceIcon && (item.price_icon || null) != null" class="bg-red br-red cr-white text-size-xs padding-left-sm padding-right-sm round va-m margin-right-xs">{{ item.price_icon }}</text>
<text class="sales-price va-m text-size-xs va-m">{{ item.show_price_symbol }}</text>
<text class="sales-price va-m">{{ item[propPriceField] }}</text>
<text class="cr-grey text-size-xs va-m">{{ item.show_price_unit }}</text>
</view>
</block>
<block v-if="(item.is_error || 0) == 0 && is_show_cart">
<view v-if="propOpenCart" class="bg-white right-cart-icon pr" :data-index="index" @tap.stop="goods_cart_event">
<iconfont name="icon-cart-inc" size="48rpx" :color="theme_color"></iconfont>
<view class="cart-badge-icon pa">
<component-badge :propNumber="item.user_cart_count || 0"></component-badge>
</view>
</view>
</block>
</view>
</view>
</view>
<!-- 标签插件 -->
<view v-if="(propLabel || null) != null && propLabel.data.length > 0" :class="'plugins-label oh pa plugins-label-' + ((propLabel.base.is_user_goods_label_icon || 0) == 0 ? 'text' : 'img') + ' plugins-label-' + (propLabel.base.user_goods_show_style || 'top-left')">
<block v-for="(lv, li) in propLabel.data" :key="li">
<view v-if="(lv.goods_ids || null) != null && lv.goods_ids.indexOf(item.id) != -1" class="lv dis-inline-block va-m" :data-value="(propLabel.base.is_user_goods_label_url || 0) == 1 ? lv.url || '' : ''" @tap="url_event">
<view v-if="(propLabel.base.is_user_goods_label_icon || 0) == 0" class="round cr-white bg-main text-size-xs fl" :style="('background-color:'+(lv.bg_color || '#666'))+' !important;'+('color:'+(lv.text_color || '#fff'))+' !important;'">
{{ lv.name }}
</view>
<block v-else>
<image v-if="(lv.icon || null) != null" class="dis-block" :src="lv.icon" mode="scaleToFill"></image>
</block>
</view>
</block>
</view>
</view>
</view>
</block>
<!-- 九方格 -->
<block v-else-if="data.style_type == 1">
<view class="goods-data-grid-list flex-row flex-wrap">
<view v-for="(item, index) in data.goods_list" :key="index" class="item oh border-radius-main bg-white oh pr spacing-mb">
<!-- 商品主体内容 -->
<view class="cp" :data-index="index" :data-value="item.goods_url" @tap="goods_event">
<image class="goods-img dis-block wh-auto" :src="item.images" mode="widthFix"></image>
<view class="base padding-horizontal-main margin-top-sm">
<view class="goods-title multi-text">{{ item.title }}</view>
<view v-if="(item.show_field_price_status || 0) == 1" class="margin-top-sm flex-row jc-sb align-c pr">
<view :class="propIsOpenGridBtnSet ? 'open-grid-btn' : ''">
<block v-if="(propPriceField || null) != null && item[propPriceField] != undefined">
<text v-if="propIsShowPriceIcon && (item.price_icon || null) != null" class="bg-red br-red cr-white text-size-xs padding-left-sm padding-right-sm round va-m margin-right-xs">{{ item.price_icon }}</text>
<text class="sales-price va-m text-size-xs va-m">{{ item.show_price_symbol }}</text>
<text class="sales-price va-m">{{ item[propPriceField] }}</text>
<text class="cr-grey text-size-xs va-m">{{ item.show_price_unit }}</text>
</block>
</view>
<block v-if="propIsOpenGridBtnSet">
<view class="pa bottom-0 right-0" :disabled="grid_btn_config.disabled" @tap="url_event" :style="[{ color: grid_btn_config.color }, { 'background-color': grid_btn_config.bg_color }, { padding: grid_btn_config.padding }, { 'border-radius': grid_btn_config.border_radius }, { 'font-size': grid_btn_config.font_size }]">
{{ grid_btn_config.name }}
</view>
</block>
<block v-else>
<block v-if="(item.is_error || 0) == 0 && is_show_cart">
<view v-if="propOpenCart" class="bg-white pr" :data-index="index" @tap.stop="goods_cart_event">
<iconfont name="icon-cart-inc" size="48rpx" :color="theme_color"></iconfont>
<view class="cart-badge-icon pa">
<component-badge :propNumber="item.user_cart_count || 0"></component-badge>
</view>
</view>
</block>
</block>
</view>
</view>
</view>
<!-- 标签插件 -->
<view v-if="(propLabel || null) != null && propLabel.data.length > 0" :class="'plugins-label oh pa plugins-label-' + ((propLabel.base.is_user_goods_label_icon || 0) == 0 ? 'text' : 'img') + ' plugins-label-' + (propLabel.base.user_goods_show_style || 'top-left')">
<block v-for="(lv, li) in propLabel.data" :key="li">
<view v-if="(lv.goods_ids || null) != null && lv.goods_ids.indexOf(item.id) != -1" class="lv dis-inline-block va-m" :data-value="(propLabel.base.is_user_goods_label_url || 0) == 1 ? lv.url || '' : ''" @tap="url_event">
<view v-if="(propLabel.base.is_user_goods_label_icon || 0) == 0" class="round cr-white bg-main text-size-xs fl" :style="('background-color:'+(lv.bg_color || '#666'))+' !important;'+('color:'+(lv.text_color || '#fff'))+' !important;'">
{{ lv.name }}
</view>
<block v-else>
<image v-if="(lv.icon || null) != null" class="dis-block" :src="lv.icon" mode="scaleToFill"></image>
</block>
</view>
</block>
</view>
</view>
</view>
</block>
<!-- 滚动 -->
<view v-else-if="data.style_type == 2" class="rolling-horizontal border-radius-main oh">
<view class="goods-data-rolling-list scroll-view-horizontal">
<swiper class="swiper" :vertical="false" :autoplay="propIsAutoPlay" :circular="false" :display-multiple-items="(data.multiple_items || null) == null ? (data.goods_list.length < 3 ? data.goods_list.length : 3) : data.goods_list.length < data.multiple_items ? data.goods_list.length : data.multiple_items" interval="3000">
<block v-for="(item, index) in data.goods_list" :key="index">
<swiper-item>
<view class="item bg-white border-radius-main margin-right-main oh pr ht-auto pr">
<!-- 商品主体内容 -->
<view class="cp" :data-index="index" :data-value="item.goods_url" @tap="goods_event">
<image class="goods-img dis-block wh-auto" :src="item.images" mode="scaleToFill"></image>
<view class="padding-left-sm padding-right-sm margin-top-sm">
<view class="single-text text-size-xs">{{ item.title }}</view>
<view v-if="(item.show_field_price_status || 0) == 1" class="margin-top-xs flex-row align-c">
<block v-if="(item.is_error || 0) == 0 && is_show_cart">
<view v-if="propOpenCart" class="bg-white right-cart-icon pr" :data-index="index" @tap.stop="goods_cart_event">
<iconfont name="icon-cart-inc" size="28rpx" :color="theme_color" propClass="pr top-xs margin-right-xs"></iconfont>
<view class="cart-badge-icon pa">
<component-badge :propNumber="item.user_cart_count || 0"></component-badge>
</view>
</view>
</block>
<block v-if="(propPriceField || null) != null && item[propPriceField] != undefined">
<view class="flex-1 flex-width">
<text v-if="propIsShowPriceIcon && (item.price_icon || null) != null" class="bg-red br-red cr-white text-size-xs padding-left-sm padding-right-sm round va-m margin-right-xs">{{ item.price_icon }}</text>
<text class="sales-price va-m text-size-xs va-m">{{ item.show_price_symbol }}</text>
<text class="sales-price va-m">{{ item[propPriceField] }}</text>
<text class="cr-grey text-size-xs va-m">{{ item.show_price_unit }}</text>
</view>
</block>
</view>
</view>
</view>
<!-- 标签插件 -->
<view v-if="(propLabel || null) != null && propLabel.data.length > 0" :class="'plugins-label oh pa plugins-label-' + ((propLabel.base.is_user_goods_label_icon || 0) == 0 ? 'text' : 'img') + ' plugins-label-' + (propLabel.base.user_goods_show_style || 'top-left')">
<block v-for="(lv, li) in propLabel.data" :key="li">
<view v-if="(lv.goods_ids || null) != null && lv.goods_ids.indexOf(item.id) != -1" class="lv dis-inline-block va-m" :data-value="(propLabel.base.is_user_goods_label_url || 0) == 1 ? lv.url || '' : ''" @tap="url_event">
<view v-if="(propLabel.base.is_user_goods_label_icon || 0) == 0" class="round cr-white bg-main text-size-xs fl" :style="('background-color:'+(lv.bg_color || '#666'))+' !important;'+('color:'+(lv.text_color || '#fff'))+' !important;'">
{{ lv.name }}
</view>
<block v-else>
<image v-if="(lv.icon || null) != null" class="dis-block" :src="lv.icon" mode="scaleToFill"></image>
</block>
</view>
</block>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
</view>
</view>
</view>
<!-- 商品购买 -->
<component-goods-buy v-if="is_show_cart" ref="goods_buy" v-on:CartSuccessEvent="goods_cart_back_event"></component-goods-buy>
<!-- 购物车抛物线 -->
<component-cart-para-curve v-if="is_show_cart" ref="cart_para_curve"></component-cart-para-curve>
</view>
</template>
<script>
const app = getApp();
import componentBadge from '@/components/badge/badge';
import componentGoodsBuy from '@/components/goods-buy/goods-buy';
import componentCartParaCurve from '@/components/cart-para-curve/cart-para-curve';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
data: null,
is_show_cart: false,
theme_color: app.globalData.get_theme_color(),
grid_btn_config: {
bg_color: '#D8D8D8',
color: '#fff',
name: this.$t('goods-list.goods-list.nr77jf'),
disabled: false,
border_radius: '24rpx',
padding: '6rpx 16rpx',
font_size: '24rpx',
},
};
},
components: {
componentBadge,
componentGoodsBuy,
componentCartParaCurve,
},
props: {
// 价格符号
propCurrencySymbol: {
type: String,
default: app.globalData.currency_symbol(),
},
// 列表数据
propData: {
type: [Array, Object],
default: [],
},
// 模式2默认class
propStyleTypeTowClass: {
type: String,
default: 'bg-white border-radius-main padding-main spacing-mb',
},
// 更多url地址
propMoreUrlKey: {
type: String,
default: 'url',
},
// 关键字url地址
propKeywordsUrl: {
type: String,
default: '/pages/goods-search/goods-search?keywords=',
},
// 滚动自动播放
propIsAutoPlay: {
type: Boolean,
default: true,
},
// 标签数据
propLabel: {
type: [Array, Object, String],
default: null,
},
// 展示加购抛物线
propIsCartParaCurve: {
type: Boolean,
default: false,
},
// 展示价格icon图标
propIsShowPriceIcon: {
type: Boolean,
default: false,
},
// 展示价格字段
propPriceField: {
type: String,
default: 'min_price',
},
// 来源
propSource: {
type: String,
default: '',
},
// 当开启时以前的按钮将失效
propIsOpenGridBtnSet: {
type: Boolean,
default: false,
},
// 九宫格 按钮样式, ----- 需要和propIsOpenGridBtnSet一起使用
propGridBtnConfig: {
type: Object,
default: () => {
return {};
},
},
// 是否开启购物车按钮 ------ 滚动使用
propOpenCart: {
type: Boolean,
default: true,
},
// 是否开启购物车数量同步到底部导航
propIsCartNumberTabBarBadgeSync: {
type: Boolean,
default: true,
},
},
// 属性值改变监听
watch: {
// 数据
propData(value, old_value) {
this.setData({
data: value,
});
},
propIsOpenGridBtnSet(newVal, oldVal) {
if (newVal !== oldVal) {
this.setData({
grid_btn_config: Object.assign({}, this.grid_btn_config, this.propGridBtnConfig),
});
}
},
},
// 页面被展示
created: function () {
var is_app_mourning = app.globalData.is_app_mourning();
var is_show_cart = app.globalData.data.is_goods_list_show_cart_opt == 1 ? (is_app_mourning && this.propSource == 'index' ? false : true) : false;
this.setData({
data: this.propData,
is_show_cart: is_show_cart,
grid_btn_config: Object.assign({}, this.grid_btn_config, this.propGridBtnConfig),
});
},
methods: {
// 加入购物车
goods_cart_event(e) {
if ((this.$refs.goods_buy || null) != null) {
var index = e.currentTarget.dataset.index || 0;
var goods = this.propData.goods_list[index];
// 开启购物车抛物线效果则展示提示操作
var is_success_tips = this.propIsCartParaCurve ? 0 : 1;
this.$refs.goods_buy.init(
goods,
{
buy_event_type: 'cart',
is_direct_cart: 1,
is_success_tips: is_success_tips,
},
{
index: index,
pos: e,
}
);
}
},
// 加入购物车成功回调
goods_cart_back_event(e) {
// 增加数量
var back = e.back_data;
var new_data = this.data;
var goods = new_data['goods_list'][back.index];
goods['user_cart_count'] = parseInt(goods['user_cart_count'] || 0) + parseInt(e.stock);
if (goods['user_cart_count'] > 99) {
goods['user_cart_count'] = '99+';
}
new_data['goods_list'][back.index] = goods;
this.setData({
data: new_data,
});
// 抛物线
if (this.propIsCartParaCurve && (this.$refs.cart_para_curve || null) != null) {
this.$refs.cart_para_curve.init(null, back.pos, goods.images);
}
// 购物车总数
var cart_total = e.cart_number || 0;
// 购物车导航角标
if (this.propIsCartNumberTabBarBadgeSync) {
app.globalData.set_tab_bar_badge('cart', cart_total);
}
// 当前页面
var page = app.globalData.current_page().split('?');
switch (page[0]) {
// 商品详情页面
case 'pages/goods-detail/goods-detail':
// 商品搜索
case 'pages/goods-search/goods-search':
var res = app.globalData.get_page_object(page[0]);
if (res.length > 0) {
for (var i in res) {
res[i].$vm.goods_cart_count_handle(cart_total);
}
}
break;
}
this.$emit('CartSuccessEvent', { ...e, ...{ goods_list: new_data.goods_list, goods: goods } });
},
// 商品事件
goods_event(e) {
// 商品数据缓存处理
var goods = this.data.goods_list[e.currentTarget.dataset.index];
app.globalData.goods_data_cache_handle(goods.id, goods);
// 调用公共打开url地址
app.globalData.url_event(e);
},
// url事件
url_event(e) {
app.globalData.url_event(e);
},
// 购物车角标变化回调
goods_badge_change() {
this.$emit('goods_badge_change');
},
},
};
</script>
<style></style>

View File

@@ -0,0 +1,362 @@
<template>
<view :class="theme_view">
<component-popup :propShow="popup_status" propPosition="bottom" @onclose="popup_close_event" :propIndex="propIndex">
<view class="goods-spec-choice-container padding-main bg-white pr">
<view class="close fr oh">
<view class="fr" @tap.stop="popup_close_event">
<iconfont name="icon-close-o" size="28rpx" color="#999"></iconfont>
</view>
</view>
<view class="goods-spec-choice-content">
<!-- 商品规格 -->
<view v-if="spec.length > 0" class="goods-spec-choose">
<view v-for="(item, key) in spec" :key="key" class="item padding-top-xxl padding-bottom-xxl">
<view class="text-size-sm">{{item.name}}</view>
<view v-if="item.value.length > 0" class="spec margin-top-sm">
<block v-for="(items, keys) in item.value" :key="keys">
<button @tap.stop="goods_spec_choice_event" :data-key="key" :data-keys="keys" type="default" size="mini" hover-class="none" :class="'round '+items.is_active + ' ' + items.is_dont + ' ' + items.is_disabled">
<image v-if="(items.images || null) != null" :src="items.images" mode="scaleToFill" class="va-m dis-inline-block round margin-right-sm"></image>
<text class="va-m">{{items.name}}</text>
</button>
</block>
</view>
</view>
</view>
</view>
<button class="bg-main br-main cr-white text-size-sm round" type="default" @tap.stop="spec_confirm_event" hover-class="none">{{$t('index.index.7w75zb')}}</button>
</view>
</component-popup>
</view>
</template>
<script>
const app = getApp();
import componentPopup from "@/components/popup/popup";
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
popup_status: false,
goods_id: 0,
spec: [],
buy_min_number: 1,
out_value: ''
};
},
components: {
componentPopup
},
props: {
propIndex: {
type: Number,
default: 100
}
},
created: function() {},
methods: {
// 获取数据
init(goods_id, spec, buy_min_number = 1, out_value = '') {
this.setData({
popup_status: true,
goods_id: goods_id,
spec: spec || [],
buy_min_number: buy_min_number || 1,
out_value: out_value,
});
// 不能选择规格处理
this.spec_handle_dont(0, 1);
},
// 购买弹层关闭
popup_close_event(e) {
this.setData({
popup_status: false
});
},
// 规格事件
goods_spec_choice_event(e) {
var key = e.currentTarget.dataset.key || 0;
var keys = e.currentTarget.dataset.keys || 0;
this.goods_spec_choice_handle(key, keys);
},
// 规格选择处理
goods_spec_choice_handle(key, keys) {
var temp_spec = this.spec;
// 不能选择和禁止选择跳过
if ((temp_spec[key]['value'][keys]['is_dont'] || null) == null && (temp_spec[key]['value'][keys]['is_disabled'] || null) == null) {
// 规格选择
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if ((temp_spec[i]['value'][k]['is_dont'] || null) == null && (temp_spec[i]['value'][k]['is_disabled'] || null) == null) {
if (key == i) {
if (keys == k && (temp_spec[i]['value'][k]['is_active'] || null) == null) {
temp_spec[i]['value'][k]['is_active'] = 'cr-white bg-main br-main';
} else {
temp_spec[i]['value'][k]['is_active'] = '';
}
}
}
}
}
this.setData({
spec: temp_spec
});
// 不能选择规格处理
this.spec_handle_dont(key);
// 获取下一个规格类型
this.get_spec_type(key);
// 获取规格详情
this.get_spec_detail();
}
},
// 获取下一个规格类型
get_spec_type(key) {
var temp_spec = this.spec;
var active_index = parseInt(key) + 1;
var sku_count = app.globalData.get_length(temp_spec);
if (active_index <= 0 || active_index >= sku_count) {
return false;
}
// 获取规格值
var spec = [];
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if ((temp_spec[i]['value'][k]['is_active'] || null) != null) {
spec.push({
type: temp_spec[i]['name'],
value: temp_spec[i]['value'][k]['name']
});
break;
}
}
}
if (spec.length <= 0) {
return false;
}
// 获取数据
uni.request({
url: app.globalData.get_request_url('spectype', 'goods'),
method: 'POST',
data: {
id: this.goods_id,
spec: JSON.stringify(spec)
},
dataType: 'json',
success: (res) => {
if (res.data.code == 0) {
var spec_type = res.data.data.spec_type;
var spec_count = spec.length;
var index = spec_count > 0 ? spec_count : 0;
if (index < sku_count) {
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if (index == i) {
temp_spec[i]['value'][k]['is_dont'] = '';
var temp_value = temp_spec[i]['value'][k]['name'];
var temp_status = false;
for (var t in spec_type) {
if (spec_type[t] == temp_value) {
temp_status = true;
break;
}
}
if (temp_status == true) {
temp_spec[i]['value'][k]['is_disabled'] = '';
} else {
temp_spec[i]['value'][k]['is_disabled'] = 'spec-items-disabled';
}
}
}
}
this.setData({
spec: temp_spec
});
}
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
app.globalData.showToast(this.$t('common.internet_error_tips'));
}
});
},
// 获取规格详情
get_spec_detail() {
// 获取规格值
var spec = this.goods_selected_spec();
// 存在规格的时候是否已完全选择规格
var sku_count = this.spec.length;
var active_count = spec.length;
if (spec.length <= 0 || active_count < sku_count) {
return false;
}
// 获取数据
uni.request({
url: app.globalData.get_request_url('specdetail', 'goods'),
method: 'POST',
data: {
id: this.goods_id,
spec: JSON.stringify(spec),
stock: this.buy_min_number
},
dataType: 'json',
success: res => {
if (res.data.code != 0) {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
app.globalData.showToast(this.$t('common.internet_error_tips'));
}
});
},
// 已选的商品规格
goods_selected_spec() {
var spec = [];
var temp_spec = this.spec;
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if ((temp_spec[i]['value'][k]['is_active'] || null) != null) {
spec.push({
type: temp_spec[i]['name'],
value: temp_spec[i]['value'][k]['name']
});
break;
}
}
}
return spec;
},
// 不能选择规格处理
spec_handle_dont(key, is_init = 0) {
var temp_spec = this.spec || [];
if (temp_spec.length <= 0) {
return false;
}
// 是否不能选择
key = parseInt(key);
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if (i > key) {
temp_spec[i]['value'][k]['is_dont'] = 'spec-dont-choose';
temp_spec[i]['value'][k]['is_disabled'] = '';
temp_spec[i]['value'][k]['is_active'] = '';
} else {
if(is_init == 1) {
temp_spec[i]['value'][k]['is_active'] = '';
}
}
// 当只有一个规格的时候
if (key == 0 && temp_spec.length == 1) {
temp_spec[i]['value'][k]['is_disabled'] = (temp_spec[i]['value'][k]['is_only_level_one'] || null) != null && (temp_spec[i]['value'][k]['inventory'] || 0) <= 0 ? 'spec-items-disabled' : '';
}
}
}
this.setData({
spec: temp_spec
});
},
// 规格确认事件
spec_confirm_event(e) {
var temp_spec = this.spec;
var sku_count = temp_spec.length;
var active_count = 0;
var spec = [];
if (sku_count > 0) {
for (var i in temp_spec) {
for (var k in temp_spec[i]['value']) {
if ((temp_spec[i]['value'][k]['is_active'] || null) != null) {
active_count++;
spec.push({
type: temp_spec[i]['name'],
value: temp_spec[i]['value'][k]['name']
});
}
}
}
if (active_count < sku_count) {
app.globalData.showToast(this.$t('goods-detail.goods-detail.6brk57'));
return false;
}
}
// 关闭弹窗
this.setData({
popup_status: false
});
// 调用父级
this.$emit('specConfirmEvent', {
goods_id: this.goods_id,
spec: spec,
stock: this.buy_min_number,
out_value: this.out_value,
});
}
}
};
</script>
<style>
.goods-spec-choice-container .close {
position: absolute;
top: 20rpx;
right: 20rpx;
z-index: 2;
}
.goods-spec-choice-content {
max-height: 50vh;
overflow-y: scroll;
overflow-x: hidden;
margin-top: 20rpx;
}
.goods-spec-choice-container .item .spec button {
background-color: #f5f5f5;
color: #666;
border: 1px solid #ccc;
}
.goods-spec-choice-container .item .spec button:not(:last-child) {
margin-right: 25rpx;
}
.goods-spec-choice-container .item .spec button image {
width: 40rpx;
height: 40rpx !important;
}
.goods-spec-choice-container .spec-dont-choose {
color: #b4b3b3 !important;
background-color: #ffffff !important;
border: 1px solid #ebeaea !important;
}
.goods-spec-choice-container .spec-dont-choose image {
opacity: 0.5;
}
.goods-spec-choice-container .spec-items-disabled {
color: #d2cfcf !important;
background-color: #ffffff !important;
border: 1px dashed #d5d5d5 !important;
}
.goods-spec-choice-container .spec-items-disabled image {
opacity: 0.3;
}
</style>

View File

@@ -0,0 +1,140 @@
<template>
<view :class="theme_view">
<view v-if="(propData || null) != null && (propData.data || null) != null && propData.data.length > 0 && swiper_data.length > 0" class="icon-nav-list" :class="propData.data.length > 5 ? 'swiper-height-max' : 'swiper-height-min'">
<uni-swiper-dot class="uni-swiper-dot-box" mode="default" :dots-styles="dots_styles" @clickItem="click_item" :info="swiper_data" :current="current">
<swiper class="swiper-box" :autoplay="autoplay" :duration="duration" @change="swiper_change" :current="swiper_dot_index">
<swiper-item v-for="(swiper_item_data, i) in swiper_data" :key="i">
<view class="swiper-item flex-row flex-wrap" :class="'swiper-item' + i">
<view v-for="(item, j) in swiper_item_data" :key="j" class="swiper-item item">
<view :class="'item-content ' + ((item.bg_color || null) == null ? 'item-exposed' : '')" :data-value="item.event_value" :data-type="item.event_type" @tap="navigation_event" :style="(item.bg_color || null) == null ? '' : 'background-color:' + item.bg_color + ';'">
<image class="image" :src="item.images_url" mode="aspectFit"></image>
</view>
<view class="title">{{ item.name }}</view>
</view>
</view>
</swiper-item>
</swiper>
</uni-swiper-dot>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
swiper_data: [],
autoplay: false,
duration: 500,
styleIndex: -1,
current: 0,
swiper_dot_index: 0,
dots_styles: {
backgroundColor: '#eee',
bottom: '0',
border: '0',
color: '#fff',
selectedBackgroundColor: app.globalData.get_theme_color(),
selectedBorder: '0',
},
};
},
components: {},
props: {
propData: {
type: [Array, Object],
default: [],
},
},
// 属性值改变监听
watch: {
// 数据
propData(value, old_value) {
this.handle_data();
}
},
beforeMount() {
this.handle_data();
},
methods: {
navigation_event(e) {
app.globalData.operation_event(e);
},
// 数据处理
handle_data() {
if((this.propData || null) != null && (this.propData.data || null) != null && this.propData.data.length > 0) {
this.swiper_data = app.globalData.group_arry(this.propData.data, 10);
}
},
swiper_change(e) {
this.current = e.detail.current;
},
click_item(e) {
this.swiper_dot_index = e;
},
},
};
</script>
<style scoped>
.icon-nav-list {
overflow: hidden;
padding: 20rpx;
}
.icon-nav-list .item {
width: 20%;
float: left;
padding: 16rpx 0;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.icon-nav-list .item .item-content {
border-radius: 50%;
padding: 20rpx;
text-align: center;
margin: 0 auto;
-webkit-box-shadow: 0 2px 12px rgb(226 226 226 / 95%);
box-shadow: 0 2px 12px rgb(226 226 226 / 95%);
}
.icon-nav-list .item .item-content,
.icon-nav-list .item .image {
width: 50rpx !important;
height: 50rpx !important;
}
.icon-nav-list .item .item-exposed {
padding: 0;
-webkit-box-shadow: none;
box-shadow: none;
}
.icon-nav-list .item .item-exposed,
.icon-nav-list .item .item-exposed .image {
width: 90rpx !important;
height: 90rpx !important;
}
.icon-nav-list .item .title {
margin-top: 12rpx;
font-size: 28rpx;
text-align: center;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
max-width: 100%;
color: #666;
}
.swiper-height-min /deep/ .swiper-box {
height: 180rpx;
}
.swiper-height-max /deep/ .swiper-box {
height: 360rpx;
}
</style>

View File

@@ -0,0 +1,49 @@
<template>
<view class="iconfont-container" :class="propClass" :style="'display:'+propContainerDisplay+';'">
<text class="iconfont" :class="name" :style="[{ color: color }, { 'font-size': size }]" @tap="$emit('click', $event)"></text>
</view>
</template>
<script>
export default {
props: {
name: {
type: String,
default: '',
},
color: {
type: String,
default: '',
},
size: {
type: String,
default: '28rpx',
},
propClass: {
type: String,
default: '',
},
propContainerDisplay: {
type: String,
default: 'inline-block',
}
},
};
</script>
<style scoped>
/* iconfont.css全局注册需要将src切换成绝对路径 */
/* @/static/icon/ */
@import url('@/static/icon/iconfont.css');
/* @import url('https://at.alicdn.com/t/c/font_4227145_kbr2f9jt68b.css'); */
.iconfont {
display: flex;
font-size: inherit;
overflow: hidden;
/* 因icon大小被设置为和字体大小一致而span等标签的下边缘会和字体的基线对齐故需设置一个往下的偏移比例来纠正视觉上的未对齐效果 */
vertical-align: -0.15em;
outline: none;
/* 定义元素的颜色currentColor是一个变量这个变量的值就表示当前元素的color值如果当前元素未设置color值则从父元素继承 */
fill: currentcolor;
}
</style>

1359
components/layout/layout.vue Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,246 @@
<template>
<view :class="theme_view">
<view v-if="(data_goods_list || null) != null && data_goods_list.length > 0">
<view v-for="(item, index) in data_goods_list" :key="index" class="plugins-magic-content border-radius-main oh spacing-mb">
<view v-if="(item.data || null) != null && item.data.length > 0" :style="(item.bg_images || null) !== null ? 'background-image: url(' + item.bg_images + ');background-size: auto 100%;' : (((item.is_text_white || 0) == 1) ? 'background: linear-gradient(180deg, '+theme_color+' 0%, '+theme_color_light+' 80%);' : 'background: #fff;')">
<!-- 上下滚动 -->
<view v-if="item.show_style == 0" :class="'hot-list flex-row flex-wrap padding-vertical-main '+(((item.is_text_white || 0) == 1) ? 'is-text-white' : '')">
<block v-for="(items, indexs) in item.data" :key="indexs">
<block v-if="(items.data || null) != null && items.data.length > 0">
<view :class="'group-item '+(item.data.length%2 != 0 && item.data.length-1 === indexs ? 'wh-auto' : 'flex-width-half')">
<view class="padding-horizontal-main">
<view class="flex-row align-c" :data-value="items.url" @tap="url_event">
<text :class="'text-size fw-b single-text cr-'+(((item.is_text_white || 0) == 1) ? 'white' : 'black')">{{ items.title }}</text>
<view class="hot-go margin-left-sm">
<block v-if="(items.icon || null) !== null">
<image :src="items.icon" mode="heightFix" class="ht-auto"></image>
</block>
</view>
</view>
<view :class="'single-text text-size-xs margin-top-xs cr-'+(((item.is_text_white || 0) == 1) ? 'white' : 'grey-9')">{{ items.describe }}</view>
<swiper class="swiper-list border-radius-main oh" circular :autoplay="(items.rolling_time || null) !== null ? true : false" :vertical="propVertical" :interval="(items.rolling_time || null) !== null ? Number(items.rolling_time) * 1000 : '6000'" :duration="propDuration">
<swiper-item v-for="(itemss, indexss) in items.data" :key="indexss">
<view class="swiper-item">
<view :class="'flex-row gap-10 goods-item-number-'+itemss.length">
<view v-for="(gv, gi) in itemss" :key="gi" :class="'bg-white border-radius-main oh flex-width-half '+(itemss.length % 2 != 0 && itemss.length-1 == gi ? 'wh-auto' : '')">
<view class="tc" :data-index="index" :data-indexs="indexs" :data-indexss="indexss" :data-gi="gi" :data-value="(gv.goods_url || null) !== null ? gv.goods_url : ''" @tap="goods_event">
<image :src="(gv.images || null) !== null ? gv.images : ''" :mode="itemss.length % 2 != 0 && itemss.length-1 == gi ? 'aspectFit' : 'scaleToFill'" :class="'swiper-img wh-auto dis-block '+(((item.is_text_white || 0) == 1) ? '' : 'border-radius-main')"> </image>
<view v-if="(gv.show_field_price_status || 0) == 1" :class="'price tc single-text text-line-1 '+(((item.is_text_white || 0) == 1) ? 'padding-horizontal-xs padding-bottom-xs' : '')">
<text class="sales-price va-m text-size-xss va-b">{{ gv.show_price_symbol }}</text>
<text class="sales-price va-m text-size-xs">{{ gv.min_price }}</text>
<text class="va-m text-size-xss cr-grey">{{ gv.show_price_unit }}</text>
</view>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</block>
</block>
</view>
<!-- 1切换滚动2切换九宫格3切换滚动 -->
<view v-else-if="item.show_style == 1 || item.show_style == 2 || item.show_style == 3" class="switch-tabs-item-list pr padding-top-main">
<view class="scroll-view-horizontal padding-left-main">
<scroll-view :scroll-x="true" :show-scrollbar="false" :scroll-with-animation="true" :scroll-into-view="'switch-tabs-item-' + (show_style1_active_index[index] || 0)">
<block v-for="(items, indexs) in item.data" :key="indexs">
<view :class="'tc cp dis-inline-block '+(indexs > 0 ? 'margin-left-xxl' : '')" :id="'switch-tabs-item-' + indexs" :data-index="index" :data-indexs="indexs" @tap="switch_tabs_event">
<image v-if="(items.icon || null) != null" :src="items.icon" class="switch-tabs-item-icon va-m margin-right-xs" mode="aspectFit"></image>
<text :class="'text-size va-m cr-'+(((item.is_text_white || 0) == 1) ? 'white' : 'black')+' '+((show_style1_active_index[index] || 0) == indexs ? 'fw-b cr-'+(((item.is_text_white || 0) == 1) ? 'white' : 'main') : '')">{{ items.title }} </text>
<view class="lh-xs">
<iconfont name="icon-down-mark" size="36rpx" :color="(show_style1_active_index[index] || 0) == indexs ? (((item.is_text_white || 0) == 1) ? '#fff' : theme_color) : 'transparent'" propClass="lh-xs"></iconfont>
</view>
</view>
</block>
</scroll-view>
</view>
<block v-for="(items, indexs) in item.data" :key="indexs">
<view v-if="(show_style1_active_index[index] || 0) == indexs">
<view v-if="(items.url || null) != null" :data-value="items.url" @tap="url_event" class="padding-right cp pa top-xxxxxl right-0">
<text :class="'text-size-xs va-m cr-'+(((item.is_text_white || 0) == 1) ? 'white' : 'grey')">{{ $t('common.more') }}</text>
<view class="dis-inline-block va-m lh-xs">
<iconfont name="icon-arrow-right" size="24rpx" :color="((item.is_text_white || 0) == 1) ? '#f5f5f5' : '#999'"></iconfont>
</view>
</view>
<view v-if="(items.describe || null) != null" :class="'text-size-xs single-text padding-vertical-sm padding-horizontal-main cr-'+(((item.is_text_white || 0) == 1) ? 'white' : 'grey')">{{items.describe}}</view>
<view :class="(item.show_style == 1 || item.show_style == 2) ? 'padding-horizontal-main padding-top-sm' : (item.show_style == 3 ? 'padding-top-xs padding-bottom-main' : '')">
<component-goods-list :propData="{ style_type: parseInt(item.show_style)-1, goods_list: items.goods_list }" :propLabel="propLabel" :propCurrencySymbol="propCurrencySymbol" :propIsAutoPlay="(items.rolling_time || null) != null" :propIsCartParaCurve="true" propSource="index" propStyleTypeTowClass="border-radius-main padding-horizontal-main padding-top-xs"></component-goods-list>
</view>
</view>
</block>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import componentGoodsList from '@/components/goods-list/goods-list';
export default {
name: 'recommend-hot',
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
theme_color: app.globalData.get_theme_color(),
theme_color_light: app.globalData.get_theme_color(null, true),
data_goods_list: [],
show_style1_active_index: {},
};
},
props: {
propCurrencySymbol: {
type: String,
default: app.globalData.currency_symbol(),
},
propData: {
type: Object,
default: () => {
return {};
},
},
propVertical: {
type: Boolean,
default: true,
},
propDuration: {
type: Number,
default: 1000,
},
// 标签数据
propLabel: {
type: [Array, Object, String],
default: null,
}
},
components: {
componentGoodsList
},
// 属性值改变监听
watch: {
// 数据
propData(value, old_value) {
this.set_data(value);
}
},
mounted() {
this.set_data(this.propData);
},
methods: {
// 商品事件
goods_event(e) {
// 商品数据缓存处理
var index = e.currentTarget.dataset.index;
var indexs = e.currentTarget.dataset.indexs;
var indexss = e.currentTarget.dataset.indexss;
var gi = e.currentTarget.dataset.gi;
var goods = this.data_goods_list[index]['data'][indexs]['data'][indexss][gi];
app.globalData.goods_data_cache_handle(goods.id, goods);
// 调用公共打开url地址
app.globalData.url_event(e);
},
// url事件
url_event(e) {
app.globalData.url_event(e);
},
// tabs切换
switch_tabs_event(e) {
var temp = this.show_style1_active_index || {};
temp[e.currentTarget.dataset.index] = e.currentTarget.dataset.indexs;
this.setData({
show_style1_active_index: temp
});
},
// 轮播数据处理
set_data(data) {
let goods = data.goods;
goods.forEach((item) => {
switch(parseInt(item.show_style || 0)) {
// 上下滚动数据处理
case 0 :
item.data.forEach((items, indexs) => {
let swiper_data = [];
if (item.data.length % 2 == 0) {
// 偶数
swiper_data = app.globalData.group_arry(items.goods_list, 2);
} else {
// 奇数
if (item.data.length === indexs + 1) {
swiper_data = app.globalData.group_arry(items.goods_list, 4);
} else {
swiper_data = app.globalData.group_arry(items.goods_list, 2);
}
}
items.data = swiper_data;
});
break;
}
});
this.setData({
data_goods_list: goods,
});
},
},
};
</script>
<style scoped>
.plugins-magic-content .hot-list {
gap: 10rpx 0;
}
.plugins-magic-content .hot-list > .flex-width-half {
margin-bottom: 0;
}
.plugins-magic-content .hot-list > .flex-width-half:nth-last-of-type(1),
.plugins-magic-content .hot-list > .flex-width-half:nth-last-of-type(2) {
margin-bottom: 0;
}
.plugins-magic-content .hot-list > .flex-width-half:nth-child(even) {
position: relative;
}
.plugins-magic-content .hot-list > .flex-width-half:nth-child(even)::before {
content: '';
height: 80%;
width: 2rpx;
border-left: 2rpx solid rgb(232 232 232 / 28%);
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
}
.plugins-magic-content .hot-list.is-text-white > .flex-width-half:nth-child(even)::before {
border-left: 2rpx solid rgb(255 245 245 / 9%);
}
.plugins-magic-content .hot-list .swiper-list {
height: 216rpx;
}
.plugins-magic-content .hot-list .swiper-list .swiper-item {
margin-top: 16rpx;
}
.plugins-magic-content .hot-list .swiper-list .swiper-img {
height: 140rpx !important;
}
.plugins-magic-content .hot-go {
height: 34rpx;
line-height: 34rpx;
}
/**
* 切换滚动、切换九宫格、切换滚动
*/
.plugins-magic-content .switch-tabs-item-list > .scroll-view-horizontal {
padding-right: 140rpx;
}
.plugins-magic-content .switch-tabs-item-list .switch-tabs-item-icon {
width: 32rpx !important;
height: 32rpx !important;
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<view :class="theme_view">
<view class="pa-w" :class="(propFixed ? 'pf z-i left-0 top-0 right-0' : '') + ' ' + propClass" :style="'padding-top:' + (status_bar_height > 0 ? status_bar_height + 5 : 0) + 'px;background-color:rgba(255,255,255,' + opacity + ');' + propStyle">
<view v-if="(propName || null) != null || propIsRightSlot || is_show_back" class="nav-back padding-horizontal-main round va-m flex-row align-c" :class="(opacity > 0.3 ? 'cr-black ' : 'cr-white ') + (status_bar_height > 0 ? '' : 'padding-vertical-main')">
<view v-if="(propName || null) != null" :class="'text-size-md tc pa left-0 right-0 padding-top-xs ' + propNameClass" :style="propNameOpacity ? (opacity ? 'color:rgba(51,51,51,' + opacity + ')' : '') : ''">{{ propName }}</view>
<view v-if="is_show_back" @tap="top_nav_left_back_event" class="dis-inline-block">
<iconfont name="icon-arrow-left" size="40rpx" propClass="pr top-xs z-i" :color="(client_value == 'alipay' || client_value == 'baidu') ? 'transparent' : propColor"></iconfont>
</view>
<slot v-if="propIsRightSlot" name="right"></slot>
</view>
<slot name="content"></slot>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
name: 'back',
props: {
// 是否显示返回按钮
propIsShowBack: {
type: Boolean,
default: true,
},
// 最外层class
propClass: {
type: String,
default: '',
},
// 最外层style
propStyle: {
type: String,
default: '',
},
// 是否需要定位
propFixed: {
type: Boolean,
default: true,
},
// 箭头颜色
propColor: {
type: String,
default: '',
},
// 标题名称
propName: {
type: String,
default: '',
},
// 标题名称class
propNameClass: {
type: String,
default: '',
},
// 标题是否需要透明
propNameOpacity: {
type: Boolean,
default: false,
},
// 是否有左侧卡槽内容
propIsRightSlot: {
type: Boolean,
default: true,
},
},
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
client_value: app.globalData.application_client_type(),
status_bar_height: 0,
// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-KUAISHOU || MP-ALIPAY || H5 || APP
status_bar_height: parseInt(app.globalData.get_system_info('statusBarHeight', 0, true)),
// #endif
// 顶部返回导航背景透明度
opacity: 0,
};
},
// 页面被展示
created: function () {
this.setData({
is_show_back: this.propIsShowBack && !app.globalData.is_tabbar_pages()
});
},
mounted() {
var self = this;
uni.$on('onPageScroll', function (e) {
var top = e.scrollTop > 47 ? 1 : e.scrollTop / 47;
self.setData({
opacity: top,
});
});
},
methods: {
top_nav_left_back_event() {
app.globalData.page_back_prev_event();
},
},
};
</script>
<style scoped>
.nav-back {
height: 30px;
padding-bottom: 10px !important;
}
</style>

View File

@@ -0,0 +1,101 @@
<template>
<view :class="theme_view">
<view class="more cr-black padding-top-main padding-bottom-sm flex-row flex-wrap align-c" :class="propClass" @tap="open_popup">
<text v-if="isMoreText">{{ $t('common.more_null') }}</text>
<iconfont name="icon-category-more"></iconfont>
</view>
<!-- 弹窗 -->
<component-popup :propShow="popup_status" :propIsBar="propIsBar" propPosition="top" :propMask="true" :propTop="propTop" @onclose="quick_close_event">
<view class="padding-top-lg">
<view class="padding-left-main padding-bottom-main">{{ $t('recommend-form.recommend-form.7gc30l') }}</view>
<view class="divider-b">
<slot></slot>
</view>
<view class="tc padding-vertical-lg" @tap="quick_close_event">
<text class="padding-right-sm">{{ $t('nav-more.nav-more.h9g4b1') }}</text>
<iconfont name="icon-arrow-top" color="#ccc"></iconfont>
</view>
</view>
</component-popup>
</view>
</template>
<script>
const app = getApp();
import componentPopup from '@/components/popup/popup';
export default {
name: 'more',
components: {
componentPopup,
},
props: {
propData: {
type: Array,
default: () => {
return [];
},
},
// 顶部定位的距离
propTop: {
type: String,
default: '',
},
propStatus: {
type: Boolean,
default: false,
},
propClass: {
type: String,
default: '',
},
themeBtn: {
type: String,
default: '1',
},
isMoreText: {
type: Boolean,
default: true,
},
},
watch: {
propStatus(newVal, oldVal) {
if (newVal !== oldVal) {
this.setData({
popup_status: newVal,
});
}
},
},
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
popup_status: false,
propIsBar: false,
};
},
methods: {
// 打开弹窗
open_popup() {
this.$emit('open-popup', true);
},
// 关闭弹窗
quick_close_event(e) {
this.$emit('open-popup', false);
},
},
};
</script>
<style scoped>
.more {
width: 30rpx;
padding: 15rpx 20rpx;
position: absolute;
top: 0;
bottom: 0;
right: 0;
z-index: 101;
white-space: normal;
word-break: break-all;
}
</style>

View File

@@ -0,0 +1,329 @@
<template>
<view :class="theme_view">
<!-- 是否有网络 -->
<view v-if="network_type_value == 'none' && not_network_await_status == 0" class="network-type-tips wh-auto tc bs-bb padding-horizontal-main">
<view class="cr-base text-size">{{$t('no-data.no-data.1u202v')}}</view>
<view class="cr-grey margin-top-sm">{{$t('no-data.no-data.imw8f1')}}{{title}}{{$t('no-data.no-data.q87572')}}</view>
<view class="margin-top-lg tc">
<button type="default" class="br-main bg-main cr-white round padding-horizontal-xxl" size="mini" @tap="open_setting_event">{{ $t('setup.setup.377uwg') }}</button>
</view>
</view>
<view v-else>
<!-- 1 加载中(0loog, 1名称) -->
<view v-if="propStatus == 1 && network_type_value != 'none'" :class="'no-data-box tc no-data-loading '+(is_loading_use_skeleton == 1 && (propPage || null) != null ? 'skeleton' : '')">
<block v-if="is_loading_use_skeleton == 1 && (propPage || null) != null">
<!-- 是否展示头站位 -->
<view v-if="propIsHeader" class="skeleton-header"></view>
<!-- 首页 -->
<block v-if="propPage == 'home'">
<x-skeleton propType="banner"></x-skeleton>
<x-skeleton propType="menu"></x-skeleton>
<x-skeleton propType="waterfall"></x-skeleton>
<x-skeleton propType="list" propRowNumber="4"></x-skeleton>
</block>
<!-- 商品分类-整体内容 -->
<block v-else-if="propPage == 'goods-category'">
<x-skeleton propType="menu" propRowNumber="1"></x-skeleton>
<view class="goods-category-content flex-row jc-sb">
<view class="left">
<x-skeleton :propConfig="skeleton_goods_category_left_config"></x-skeleton>
</view>
<view class="right">
<x-skeleton propType="list" propRowNumber="7"></x-skeleton>
</view>
</view>
</block>
<!-- 商品分类-内容项 -->
<block v-else-if="propPage == 'goods-category-item'">
<x-skeleton propType="list" propRowNumber="6"></x-skeleton>
</block>
<!-- 购物车 -->
<block v-else-if="propPage == 'cart'">
<x-skeleton propType="list" propRowNumber="3"></x-skeleton>
<x-skeleton propType="waterfall" propRowNumber="4"></x-skeleton>
</block>
<!-- 商品详情 -->
<block v-else-if="propPage == 'goods'">
<x-skeleton propType="banner" propHeightNumber="600"></x-skeleton>
<x-skeleton propType="text"></x-skeleton>
<x-skeleton propType="info"></x-skeleton>
<x-skeleton propType="waterfall" propRowNumber="4"></x-skeleton>
</block>
</block>
<block v-else>
<view v-if="loading_content_type == 1" class="loading-title-animation">
<text class="title">{{title}}</text>
</view>
<view v-else class="loading-logo-content" :style="'margin-top: '+propLoadingLogoTop+';'">
<view class="loading-logo" :style="'background-image: url('+loading_logo+')'"></view>
<view class="loading-border" :style="'background-image: url('+loading_logo_border+')'"></view>
</view>
</block>
</view>
<!-- 2 处理错误 -->
<view v-else-if="propStatus == 2" class="no-data-box tc">
<image class="image" :src="static_dir + 'error.png'" mode="widthFix"></image>
<view class="no-data-tips">{{propMsg || $t('form.form.bniyyt')}}</view>
<view v-if="propBackBtn" class="margin-top-xxxl tc">
<button type="default" size="mini" class="bg-grey-e br-grey cr-base round" @tap="back_event">{{$t('common.return')}}</button>
</view>
</view>
<!-- 0 默认没有数据 -->
<view v-else-if="propStatus == 0" class="no-data-box tc">
<image class="image" :src="propUrl ? propUrl : static_dir + 'empty.png'" mode="widthFix"></image>
<view class="no-data-tips">{{propMsg || $t('common.no_relevant_data_tips')}}</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
static_dir: '/static/images/common/',
is_loading_use_skeleton: app.globalData.data.is_loading_use_skeleton,
loading_logo_border: app.globalData.data.static_url+'static/common/svg/loading-border.svg',
loading_logo: app.globalData.get_application_logo_square() || app.globalData.data.static_url+'favicon.ico',
loading_content_type: app.globalData.data.loading_content_type,
title: app.globalData.get_application_title(),
network_type_value: '',
not_network_await_status: 0,
// 骨架屏配置
// 商品分类内容-左侧
skeleton_goods_category_left_config: {
padding: '30rpx',
gridRows: 19,
gridColumns: 1,
gridRowsGap: '30rpx',
headShow: true,
headWidth: '200rpx',
headHeight: '60rpx',
headBorderRadius: '16rpx',
textShow: false,
},
};
},
components: {},
props: {
propStatus: {
type: [Number, String],
default: 0,
},
propMsg: {
type: String,
default: '',
},
propUrl: {
type: String,
default: '',
},
propBackBtn: {
type: Boolean,
default: true,
},
propNetworkTimeNum: {
type: Number,
default: 40,
},
propPage: {
type: String,
default: '',
},
propIsHeader: {
type: Boolean,
default: false,
},
propLoadingLogoTop: {
type: String,
default: '50%',
}
},
// 页面被展示
created: function () {
self = this;
uni.getNetworkType({
success: function (res) {
// 当前网络
self.network_type_value = res.networkType;
// 无网络进入等待网络中
if(self.network_type_value == 'none') {
self.not_network_await_status = 1;
}
// 定时处理
self.countdown(self);
}
});
},
// #ifndef VUE2
destroyed() {
clearInterval(this.timer);
},
// #endif
// #ifdef VUE3
unmounted() {
clearInterval(this.timer);
},
// #endif
methods: {
// 定时任务
countdown(self) {
// 销毁之前的任务
clearInterval(self.timer);
// 没有网络则启动定时任务
if(self.network_type_value == 'none') {
var temp_num = self.propNetworkTimeNum;
self.timer = setInterval(function () {
// 读取网络状态
uni.getNetworkType({
success: function (res) {
self.network_type_value = res.networkType;
// 已经有网络了则结束定时任务、并正常继续等待走加载过程
if(self.network_type_value != 'none') {
clearInterval(self.timer);
}
}
});
// 每次减1
temp_num--;
// 0则结束
if(temp_num <= 0) {
// 销毁任务
clearInterval(self.timer);
// 无需等待网络
self.not_network_await_status = 0;
}
}, 500);
}
},
// 返回事件
back_event(e) {
app.globalData.page_back_prev_event();
},
// 打开权限管理中心
open_setting_event() {
app.globalData.open_setting_event();
},
},
};
</script>
<style scoped>
.no-data-box {
padding: 15% 0;
}
.no-data-box .image {
width: 160rpx;
margin-bottom: 30rpx;
}
.no-data-box .no-data-tips {
font-size: 24rpx;
color: #999;
}
.no-data-loading .title {
color: #999;
}
/**
* 名称加载
*/
.loading-title-animation,
.network-type-tips {
padding-top: 25%;
}
.loading-title-animation {
background: #e7e7e7 -webkit-linear-gradient(left, #c6c6c6 0%, #c6c6c6 90%) no-repeat 0 0;
background-size: 20% 100%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 60rpx;
font-weight: bold;
-webkit-animation: loading-text-animation 2s linear infinite;
animation: loading-text-animation 2s linear infinite;
}
@-webkit-keyframes loading-text-animation {
0% {
background-position: 0 0;
}
100% {
background-position: 100% 100%;
}
}
@keyframes loading-text-animation {
0% {
background-position: 0 0;
}
100% {
background-position: 100% 100%;
}
}
/**
* logo加载
*/
.loading-logo-content {
position: absolute;
width: 140rpx;
height: 140rpx;
left: calc(50% - 70rpx);
top: 0;
border-radius: 50%;
overflow: hidden;
background: #fff;
margin-top: 50%;
}
.loading-logo-content .loading-logo {
content: '';
display: block;
position: absolute;
left: 30rpx;
top: 30rpx;
width: 80rpx;
height: 80rpx;
opacity: 0.8;
background-size: contain;
background-position: center center;
background-repeat: no-repeat;
}
.loading-logo-content .loading-border {
content: '';
display: block;
position: absolute;
width: 168rpx;
height: 168rpx;
left: -14rpx;
top: -14rpx;
opacity: 0.8;
background-size: contain;
background-position: center center;
background-repeat: no-repeat;
}
/**
* 骨架屏
*/
.skeleton-header {
/* #ifndef H5 || APP */
padding-top: var(--status-bar-height);
padding-bottom: 55px;
/* #endif */
}
.no-data-box.skeleton {
padding: 0;
}
.no-data-loading .goods-category-content > .left {
width: 30%;
}
.no-data-loading .goods-category-content > .right {
width: 70%;
}
</style>

View File

@@ -0,0 +1,444 @@
<template>
<view :class="theme_view">
<block v-if="online_service_status == 1">
<!-- 是否商品页样式 -->
<view v-if="propIsGoods == true" class="goods-chat-container fl cp">
<block v-if="is_chat == 1">
<view @tap="chat_event">
<image class="icon" :src="chat_icon" mode="scaleToFill"></image>
<text class="text dis-block text-size-xs cr-grey">{{$t('online-service.online-service.4l6k22')}}</text>
</view>
</block>
<block v-else>
<!-- #ifdef MP-WEIXIN || MP-TOUTIAO || MP-BAIDU || MP-KUAISHOU -->
<button class="chat-btn" open-type="contact" :show-message-card="propCard" :send-message-title="propTitle" :send-message-path="propPath" :send-message-img="propImg">
<image class="icon" :src="chat_icon" mode="scaleToFill"></image>
<text class="text dis-block text-size-xs cr-grey">{{$t('online-service.online-service.4l6k22')}}</text>
</button>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<button class="chat-btn alipay-contact" open-type="contact">
<contact-button class="alipay-chat-btn" :tnt-inst-id="mini_alipay_tnt_inst_id" :scene="mini_alipay_scene" :alipay-card-no="mini_alipay_openid || ''" :icon="chat_icon" size="40rpx*40rpx" />
<text class="text dis-block text-size-xs cr-grey">{{$t('online-service.online-service.4l6k22')}}</text>
</button>
<!-- #endif -->
<!-- #ifdef H5 || APP -->
<button class="chat-btn" type="default" @tap="call_event">
<image class="icon" :src="chat_icon" mode="scaleToFill"></image>
<text class="text dis-block text-size-xs cr-grey">{{$t('online-service.online-service.4l6k22')}}</text>
</button>
<!-- #endif -->
</block>
</view>
<!-- 默认浮动展示-可拖拽位置 -->
<view v-else>
<block v-if="is_online_service_fixed == 1">
<block v-if="propIsMovable">
<movable-area class="online-service-movable-container" :style="'height: calc(100% - '+height_dec+'rpx);top:'+top+'rpx;'">
<movable-view direction="all" :x="x" :y="y" :animation="false" :class="'online-service-event-submit '+(propIsSpread ? ' spread' : '')">
<block v-if="propIsSpread">
<view class="ring"></view>
<view class="ring"></view>
</block>
<block v-if="is_chat == 1">
<button class="chat-btn" type="default" :class="common_ent" @tap="chat_event">
<image class="icon dis-block" :src="chat_image"></image>
</button>
</block>
<block v-else>
<!-- #ifdef MP-WEIXIN || MP-TOUTIAO || MP-BAIDU -->
<button class="chat-btn" open-type="contact" :class="common_ent" :show-message-card="propCard" :send-message-title="propTitle" :send-message-path="propPath" :send-message-img="propImg">
<image class="icon dis-block" :src="chat_image"></image>
</button>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<button class="chat-btn" open-type="contact" :class="'alipay-contact '+common_ent">
<contact-button class="alipay-chat-btn" :tnt-inst-id="mini_alipay_tnt_inst_id" :scene="mini_alipay_scene" :alipay-card-no="mini_alipay_openid || ''" :icon="chat_image" size="40rpx*40rpx" />
</button>
<!-- #endif -->
<!-- #ifdef H5 || APP -->
<button class="chat-btn" type="default" :class="common_ent" @tap="call_event">
<image class="icon dis-block" :src="chat_image"></image>
</button>
<!-- #endif -->
</block>
</movable-view>
</movable-area>
</block>
<block v-else>
<view class="online-service-movable-container" :style="'height: calc(100% - '+height_dec+'rpx);top:'+top+'rpx;'">
<view :class="'online-service-event-submit '+(propIsSpread ? ' spread' : '')">
<block v-if="propIsSpread">
<view class="ring"></view>
<view class="ring"></view>
</block>
<block v-if="is_chat == 1">
<button class="chat-btn" type="default" :class="common_ent" @tap="chat_event">
<image class="icon dis-block" :src="chat_image"></image>
</button>
</block>
<block v-else>
<!-- #ifdef MP-WEIXIN || MP-TOUTIAO || MP-BAIDU -->
<button class="chat-btn" open-type="contact" :class="common_ent" :show-message-card="propCard" :send-message-title="propTitle" :send-message-path="propPath" :send-message-img="propImg">
<image class="icon dis-block" :src="chat_image"></image>
</button>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<button class="chat-btn alipay-contact" :class="common_ent" open-type="contact">
<contact-button class="alipay-chat-btn" :tnt-inst-id="mini_alipay_tnt_inst_id" :scene="mini_alipay_scene" :alipay-card-no="mini_alipay_openid || ''" :icon="chat_image" size="40rpx*40rpx" />
</button>
<!-- #endif -->
<!-- #ifdef H5 || APP -->
<button class="chat-btn" type="default" :class="common_ent" @tap="call_event">
<image class="icon dis-block" :src="chat_image"></image>
</button>
<!-- #endif -->
</block>
</view>
</view>
</block>
</block>
</view>
</block>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
common_static_url: app.globalData.get_static_url('common'),
client_value: app.globalData.application_client_type(),
is_chat: 0,
chat_url: null,
chat_icon: '',
chat_image: '',
common_app_customer_service_tel: null,
common_app_customer_service_custom: null,
common_app_customer_service_company_weixin_corpid: null,
common_app_customer_service_company_weixin_url: null,
online_service_status: 0,
is_online_service_fixed: app.globalData.data.is_online_service_fixed,
mini_alipay_tnt_inst_id: null,
mini_alipay_scene: null,
mini_alipay_openid: null,
system: null,
x: 0,
y: 0,
top: 0,
height_dec: 0,
is_first: 1,
common_ent: ''
};
},
components: {},
props: {
propIsGoods: {
type: Boolean,
default: false
},
propIsBar: {
type: Boolean,
default: false
},
propIsNav: {
type: Boolean,
default: false
},
propCard: {
type: Boolean,
default: false
},
propTitle: {
type: String,
default: ''
},
propImg: {
type: String,
default: ''
},
propPath: {
type: String,
default: ''
},
propIsGrayscale: {
type: Boolean,
default: false
},
propIsChat: {
type: Number,
default: 0
},
propChatUrl: {
type: String,
default: ''
},
propChatIcon: {
type: String,
default: ''
},
propChatImage: {
type: String,
default: ''
},
propIsSpread: {
type: Boolean,
default: true
},
propIsMovable: {
type: Boolean,
default: true
},
},
// 属性值改变监听
watch: {
// 是否灰度
propIsGrayscale(value, old_value) {
this.common_ent = value ? 'grayscale' : '';
}
},
// 页面被展示
created: function(e) {
this.init_config();
// 非首次进入则重新初始化配置接口
if (this.is_first == 0) {
app.globalData.init_config();
}
// 数据设置
var system = app.globalData.get_system_info(null, null, true);
var width = app.globalData.window_width_handle(system.windowWidth);
var height = app.globalData.window_height_handle(system);
// 页面是否定义导航
var top_h = this.propIsNav ? 130 : 0;
this.setData({
is_first: 0,
system: system,
chat_icon: this.propChatIcon || this.common_static_url+'chat-icon.png',
chat_image: this.propChatImage || this.common_static_url+'online-service-icon.png',
// 位置坐标
x: width - 65,
y: height - 380,
// 展示位置处理
top: top_h,
height_dec: top_h,
// #ifdef H5 || APP
top: 210,
height_dec: this.propIsBar ? 310 : 210,
// #endif
// 是否灰度
common_ent: this.propIsGrayscale ? 'grayscale' : ''
});
},
methods: {
// 初始化配置
init_config(status) {
// 客服优先级顺序( 客服系统 -> 自定义客服 -> 企业微信客服(仅app+h5+微信小程序生效) -> 各端平台客服 -> 电话客服 )
if ((status || false) == true) {
// 是否使用客服系统
var is_chat = app.globalData.get_config('plugins_base.chat.data.is_mobile_chat', 0);
var chat_url = app.globalData.get_config('plugins_base.chat.data.chat_url');
var is_online_service = app.globalData.get_config('config.common_app_is_online_service', 0);
if(is_chat == 1 && (chat_url != null || (this.propChatUrl || null) != null)) {
this.setData({
is_chat: is_chat,
chat_url: this.propChatUrl || chat_url,
online_service_status: is_online_service,
});
} else {
var online_service_url = app.globalData.get_config('config.common_app_customer_service_custom', null);
this.setData({
common_app_customer_service_tel: app.globalData.get_config('config.common_app_customer_service_tel', null),
common_app_customer_service_custom: (online_service_url == null || (online_service_url[this.client_value] || null) == null) ? null : online_service_url[this.client_value],
common_app_customer_service_company_weixin_corpid: app.globalData.get_config('config.common_app_customer_service_company_weixin_corpid', null),
common_app_customer_service_company_weixin_url: app.globalData.get_config('config.common_app_customer_service_company_weixin_url', null),
online_service_status: is_online_service,
});
// 存在自定义客服和微信企业客服则走客服模式
if((this.common_app_customer_service_custom || null) != null || ((this.common_app_customer_service_company_weixin_corpid || null) != null && (this.common_app_customer_service_company_weixin_url || null) != null)) {
this.setData({
is_chat: 1
});
}
// 对应平台没有提供客服的、[电话,自定义客服,企业微信客服]必须存在一个,则关闭在线客服
if(['qq', 'h5', 'ios', 'android'].indexOf(this.client_value) != -1 && (this.common_app_customer_service_tel || null) == null && (this.common_app_customer_service_custom || null) == null) {
var temp_service_status = this.online_service_status;
if(this.client_value == 'qq') {
temp_service_status = 0;
} else {
// h5,app是否配置企业微信客服
if((this.common_app_customer_service_company_weixin_corpid || null) == null && (this.common_app_customer_service_company_weixin_url || null) == null) {
temp_service_status = 0;
}
}
this.setData({
online_service_status: temp_service_status
});
}
// #ifdef MP-ALIPAY
// 在线客服开启获取用户openid
if(this.online_service_status == 1)
{
this.setData({
mini_alipay_tnt_inst_id: app.globalData.get_config('config.common_app_mini_alipay_tnt_inst_id'),
mini_alipay_scene: app.globalData.get_config('config.common_app_mini_alipay_scene'),
mini_alipay_openid: app.globalData.get_user_cache_info('alipay_openid')
});
}
// #endif
}
} else {
app.globalData.is_config(this, 'init_config');
}
},
// 客服事件
chat_event() {
// 在线客服系统
if((this.chat_url || null) != null) {
app.globalData.chat_entry_handle(this.chat_url);
} else {
// 自定义客服
if((this.common_app_customer_service_custom || null) != null) {
app.globalData.url_open(this.common_app_customer_service_custom);
} else {
// 企业微信客服
if((this.common_app_customer_service_company_weixin_corpid || null) != null && (this.common_app_customer_service_company_weixin_url || null) != null) {
// #ifdef APP
// app打开企业微信客服
plus.share.getServices(res => {
var wechat = res.find(i => i.id === 'weixin')
if(wechat) {
wechat.openCustomerServiceChat({
corpid: this.common_app_customer_service_company_weixin_corpid,
url: this.common_app_customer_service_company_weixin_url,
});
}
});
// #endif
// #ifdef MP-WEIXIN
// 微信小程序打开企业微信客服
uni.openCustomerServiceChat({
extInfo: {url: this.common_app_customer_service_company_weixin_url},
corpId: this.common_app_customer_service_company_weixin_corpid,
showMessageCard: this.propCard,
sendMessageTitle: this.propTitle,
sendMessagePath: this.propPath,
sendMessageImg: this.propImg,
});
// #endif
// #ifdef H5
app.globalData.url_open(this.common_app_customer_service_company_weixin_url);
// #endif
} else {
// 电话客服
this.call_event();
}
}
}
},
// 客服电话
call_event() {
app.globalData.call_tel(this.common_app_customer_service_tel);
}
}
};
</script>
<style scoped>
.online-service-movable-container {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: transparent;
pointer-events: none;
z-index: 2;
}
.online-service-event-submit {
pointer-events: auto;
}
.online-service-event-submit,
.online-service-event-submit .chat-btn {
width: 50px;
height: 50px;
border-radius: 50%;
}
.online-service-event-submit .chat-btn {
border: 0;
padding: 0;
}
.online-service-event-submit .icon {
width: 50px !important;
height: 50px !important;
position: relative;
}
.goods-chat-container .chat-btn {
padding: 0;
border: 0;
line-height: initial;
font-size: 24rpx;
background: transparent;
}
.goods-chat-container .icon {
width: 40rpx;
height: 40rpx;
margin: 10rpx 0 5rpx 0;
}
.goods-chat-container .text {
margin-top: -10rpx;
}
/* #ifdef MP-ALIPAY */
.goods-chat-container .alipay-contact {
margin-top: 10rpx;
}
.goods-chat-container .alipay-contact .text {
margin-top: -5rpx;
}
.online-service-event-submit .alipay-chat-btn {
line-height: initial;
display: block;
}
/* #endif */
/**
* 呼吸灯
*/
.spread {
background-color: rgba(238, 73, 70,0.4);
border-radius: 100%;
width: 50px;
height: 50px;
position: relative;
z-index: 1;
}
.spread .ring {
/* 速度为1.5 * 层数 = 实际运行速度,速度修改则 animation-delay 属性也修改相同速度 */
animation: pulsing 1.5s ease-out infinite;
}
/* 速度为1*层数 */
.spread .ring:nth-of-type(1) {
-webkit-animation-delay: -1.5s;
animation-delay: -1.5s;
}
/* 速度为1*层数 */
.spread .ring:nth-of-type(2) {
-webkit-animation-delay: -2s;
animation-delay: -2s;
}
@keyframes pulsing {
100% {
transform: scale(1.35);
opacity: 0
}
}
</style>

View File

@@ -0,0 +1,173 @@
<template>
<view :class="theme_view">
<!-- 简洁的数据一般列表展示使用 -->
<view v-if="propIsTerse" class="content margin-top cp">
<block v-if="data != null && data_field.length > 0">
<block v-for="(item, index) in data_field" :key="index">
<view v-if="(item.is_hide || 0) == 0" class="single-text margin-top-xs">
<text class="cr-grey margin-right-xl">{{ item.name }}</text>
<text class="cr-base">
<block v-if="item.type == 'images'">
<image :src="data[item.field]" mode="aspectFit" class="radius panel-item-images"></image>
</block>
<text v-else>{{ data[item.field] }}</text>
</text>
<view v-if="(item.is_copy || 0) == 1" class="dis-inline-block margin-left" data-event="copy" :data-value="data[item.field]" @tap.stop="text_event_handle">
<iconfont name="icon-copy" size="28rpx" class="cr-grey"></iconfont>
</view>
</view>
</block>
<view v-if="propIsItemShowMax > 0 && propIsItemShowMax < data_field.length" @tap.stop="item_more_event" class="margin-top-sm tc">
<text class="cr-grey-c margin-right-sm">{{ $t('common.view_more') }}</text>
<iconfont :name="'icon-arrow-' + (more_status ? 'top' : 'bottom')" size="28rpx" color="#ccc"></iconfont>
</view>
</block>
<slot></slot>
</view>
<!-- 详情面板数据 -->
<view v-else class="padding-horizontal-main padding-top-main">
<view class="panel-item padding-main border-radius-main bg-white spacing-mb">
<view v-if="(propTitle || null) != null" class="br-b padding-bottom-main fw-b text-size">{{ propTitle }}</view>
<view class="panel-content oh">
<block v-if="data != null && data_field.length > 0">
<block v-for="(item, index) in data_field" :key="index">
<view v-if="(item.is_hide || 0) == 0" class="item br-b-f5 oh padding-vertical-main">
<view class="title fl padding-right-main cr-grey">{{ item.name }}</view>
<view class="content fl br-l padding-left-main">
<block v-if="item.type == 'images'">
<image :src="data[item.field]" mode="aspectFit" class="panel-item-images"></image>
</block>
<text v-else>{{ data[item.field] }}</text>
<view v-if="(item.is_copy || 0) == 1" class="dis-inline-block margin-left" data-event="copy" :data-value="data[item.field]" @tap.stop="text_event_handle">
<iconfont name="icon-copy" size="28rpx" class="cr-grey lh-il"></iconfont>
</view>
</view>
</view>
</block>
<view v-if="propIsItemShowMax > 0 && propIsItemShowMax < data_field.length" @tap="item_more_event" class="margin-top-sm tc">
<text class="cr-grey-c margin-right-sm">{{ $t('common.view_more') }}</text>
<iconfont :name="'icon-arrow-' + (more_status ? 'top' : 'bottom')" size="28rpx" color="#ccc"></iconfont>
</view>
</block>
<slot></slot>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import componentNoData from '@/components/no-data/no-data';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
data: null,
data_field: [],
more_status: false,
};
},
components: {
componentNoData,
},
props: {
// 标题
propTitle: {
type: String,
default: '',
},
// 数据
propData: {
type: [Object, String],
default: '',
},
// 数据字段
propDataField: {
type: Array,
default: [],
},
// 数据项最大展示数量0则显示全部
propIsItemShowMax: {
type: Number,
default: 0,
},
// 指定字段
propAppointField: {
type: String,
default: '',
},
// 排除字段
propExcludeField: {
type: String,
default: '',
},
// 无数据提示状态
propNoDataStatus: {
type: [Number, String],
default: 3,
},
// 无数据提示内容
propNoDataMsg: {
type: [String],
default: '',
},
// 是否简洁的模式展示
propIsTerse: {
type: Boolean,
default: false,
},
},
// 属性值改变监听
watch: {
// 数据改变
propData(value, old_value) {
this.data = value;
},
},
// 页面被展示
created: function (e) {
this.setData({
data: this.propData,
});
this.data_field_handle(this.propDataField);
},
methods: {
// 数据字段处理
data_field_handle(data) {
var appoint = (this.propAppointField || null) == null ? [] : this.propAppointField.split(',');
var exclude = (this.propExcludeField || null) == null ? [] : this.propExcludeField.split(',');
var temp_data = [];
var index = 0;
for (var i in data) {
if ((exclude.length == 0 && appoint.length > 0 && appoint.indexOf(data[i]['field']) != -1) || (appoint.length == 0 && (exclude.length == 0 || exclude.indexOf(data[i]['field']) == -1))) {
data[i]['is_hide'] = (data[i]['is_hide'] || 0) == 0 ? (index >= this.propIsItemShowMax && this.propIsItemShowMax > 0 ? 1 : 0) : 0;
temp_data.push(data[i]);
index++;
}
}
this.setData({
data_field: temp_data,
});
},
// 文本事件
text_event_handle(e) {
app.globalData.text_event_handle(e);
},
// 数据项更多事件
item_more_event(e) {
this.data_field_handle(this.data_field);
this.setData({
more_status: !this.more_status,
});
},
},
};
</script>
<style scoped>
.panel-item-images {
width: 40rpx;
height: 40rpx;
}
</style>

View File

@@ -0,0 +1,922 @@
<template>
<view :class="theme_view">
<!-- 支付二维码展示 -->
<component-popup :propShow="popup_view_pay_qrcode_is_show" propPosition="bottom" @onclose="popup_view_pay_qrcode_event_close">
<view class="padding-top-xxxl padding-bottom-xxxl padding-left-xxxl padding-right-xxxl tc">
<block v-if="(popup_view_pay_data || null) == null || (popup_view_pay_data.qrcode_url || null) == null || (popup_view_pay_data.name || null) == null || (popup_view_pay_data.order_no || null) == null">
<text class="cr-grey">{{$t('payment.payment.973g2e')}}</text>
</block>
<block v-else>
<view class="fw-b text-size cr-base margin-bottom-sm">{{ popup_view_pay_data.name }}</view>
<image :src="popup_view_pay_data.qrcode_url" mode="aspectFit" class="dis-block auto max-w"></image>
<view v-if="(popup_view_pay_data.msg || null) != null" class="cr-yellow margin-top-sm">{{ popup_view_pay_data.msg }}</view>
<!-- #ifdef H5 -->
<view v-if="popup_view_pay_data.pay_url != null" class="margin-top-xl">
<a :href="popup_view_pay_data.pay_url" target="_blank" class="dis-inline-block cr-green">{{$t('payment.payment.z3y296')}}</a>
</view>
<!-- #endif -->
</block>
</view>
</component-popup>
<!-- 支付方式 popup -->
<component-popup :propShow="is_show_payment_popup" propPosition="bottom" @onclose="payment_popup_event_close">
<view class="poupon-title padding-main tc text-size-md pr">{{$t('payment.payment.iu792d')}}<iconfont name="icon-close-o" propClass="pa right-0 margin-right-main margin-top-xs" size="30rpx" color="#999" @tap="payment_popup_event_close"></iconfont>
</view>
<view class="payment-price tc padding-top-sm padding-bottom-sm br-b">
<text class="text-size-md">{{ propCurrencySymbol }}</text>
{{ propPayPrice }}
</view>
<view v-if="payment_list.length > 0" class="oh">
<view class="payment-list">
<scroll-view scroll-y="true" class="scroll-y wh-auto">
<view v-for="(item, index) in payment_list" :key="index" class="item br-b flex-row jc-sb align-c" :data-value="item.id" @tap="checked_payment">
<view class="flex-1">
<image v-if="(item.logo || null) != null" class="icon va-m margin-right-sm" :src="item.logo" mode="widthFix"></image>
<text class="va-m">{{ item.name }}</text>
<text v-if="(item.tips || null) !== null" class="va-m cr-red">{{ item.tips }}</text>
</view>
<iconfont :name="payment_id == item.id ? 'icon-zhifu-yixuan' : 'icon-zhifu-weixuan'" size="44rpx" :color="payment_id == item.id ? '#E22C08' : '#ccc'"></iconfont>
</view>
</scroll-view>
</view>
<view class="payment-submit">
<view class="bottom-line-exclude">
<button class="bg-main br-main cr-white round text-size" type="default" hover-class="none" @tap="popup_payment_event" :disabled="submit_disabled_status">{{$t('payment.payment.25r53g')}}</button>
</view>
</view>
</view>
<view v-else class="padding-top-xxxl padding-bottom-xxxl oh bg-white tc cr-grey">{{$t('payment.payment.058a46')}}</view>
</component-popup>
<!-- 支付html展示 -->
<component-popup :propShow="popup_view_pay_html_is_show" propPosition="bottom" @onclose="popup_view_pay_html_event_close">
<view class="popup-pay-html-content padding-top-xxxl padding-bottom-xxxl padding-left-xxxl padding-right-xxxl tc">
<block v-if="(popup_view_pay_data || null) == null">
<text class="cr-grey">{{$t('payment.payment.973g2e')}}</text>
</block>
<block v-else>
<mp-html :content="popup_view_pay_data" />
</block>
</view>
</component-popup>
<!-- 支付中提示弹窗 -->
<view v-if="payment_confirm_modal_status" class="payment-confirm-modal">
<view class="content padding-xl margin-xxl tc bg-white border-radius-main">
<view class="padding-vertical-xxxxl">{{$t('common.payment_in_text')}}</view>
<view class="margin-top-lg">
<button type="default" size="mini" class="bg-white br-black cr-black text-size-sm round margin-right-xxxxl" data-type="0" @tap="payment_confirm_event">{{$t('common.not_have_name')}}</button>
<button type="default" size="mini" class="bg-main br-main cr-white text-size-sm round margin-left-xxxxl" data-type="1" @tap="payment_confirm_event">{{$t('order.order.s8g966')}}</button>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import base64 from '@/common/js/lib/base64.js';
import componentPopup from '@/components/popup/popup';
export default {
name: 'pay',
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
// 支付方式列表
payment_list: [],
// 弹窗开关
is_show_payment_popup: false,
popup_view_pay_qrcode_is_show: false,
// 支付弹窗展示数据
popup_view_pay_data: null,
// 定时器
pay_statuc_check_timer: null,
// 支付id
payment_id: 0,
submit_disabled_status: true,
order_id: 0,
popup_view_pay_html_is_show: false,
// 打开url地址定时任务和状态
open_pay_url_timer: null,
open_pay_url_status: true,
// 支付返回数据
pay_response_data: {},
// 支付确认弹窗
payment_confirm_modal_status: false,
};
},
props: {
propCurrencySymbol: {
type: String,
default: app.globalData.currency_symbol(),
},
propPayUrl: {
type: String,
default: '',
},
propQrcodeUrl: {
type: String,
default: '',
},
propPaymentList: {
type: Array,
default: () => {
return [];
},
},
propIsShowPayment: {
type: Boolean,
default: false,
},
// 订单id
propTempPayValue: {
type: [String, Number],
default: '',
},
// 订单id参数名默认为id
propPayDataKey: {
type: String,
default: 'id',
},
// 订单下标 ---- 用于处理支付成功后前端修改成功状态
propTempPayIndex: {
type: [Number, String],
default: 0,
},
// 支付id 值为0表示没有配置支付方式
propPaymentId: {
type: [Number, String],
default: 0,
},
// 默认支付id 值为0表示没有默认
propDefaultPaymentId: {
type: [Number, String],
default: 0,
},
// 付款金额
propPayPrice: {
type: [Number, String],
default: 0,
},
// 支付跳转页面------跳转成功页面---后返回的页面
propToPageBack: {
type: Object,
default: () => {
return {};
},
},
// 指定所有页面跳转到指定页面------除现金支付外
propToPage: {
type: String,
default: '',
},
// 现金支付-指定跳转页面----不进入充值成功页面:不配置则表示不跳转
propToAppointPage: {
type: String,
default: '',
},
// 支付失败跳转页面------不传则停留在当前页面
propToFailPage: {
type: String,
default: '',
},
//是否需要关闭页面进行跳转
propIsRedirectTo: {
type: Boolean,
default: false,
},
// 判断错误时是否需要弹窗提示
propIsFailAlert: {
type: Boolean,
default: true,
},
},
components: {
componentPopup,
},
watch: {
// 支付方式是否改变
propPaymentList(value, old_value) {
this.setData({
payment_list: value,
});
},
// 是否显示支付方式
propIsShowPayment(new_val, old_val) {
if (new_val !== old_val) {
let bool = true;
if (this.payment_list.length == 1) {
bool = false;
this.setData({
payment_id: this.payment_list[0].id,
});
} else {
let self = this;
self.payment_list.forEach((item) => {
let new_payment_id = Number(self.propPaymentId) == 0 ? self.propDefaultPaymentId : Number(self.propPaymentId);
if (item.id == new_payment_id) {
bool = false;
}
});
this.setData({
payment_id: Number(this.propPaymentId) == 0 ? this.propDefaultPaymentId : Number(this.propPaymentId),
});
}
this.setData({
is_show_payment_popup: new_val,
submit_disabled_status: bool,
});
}
}
},
// 页面被展示
created: function () {
this.setData({
payment_list: this.propPaymentList,
payment_id: Number(this.propPaymentId) == 0 ? this.propDefaultPaymentId : Number(this.propPaymentId),
});
},
methods: {
// 支付弹窗关闭
payment_popup_event_close(e) {
this.setData({
is_show_payment_popup: false,
});
this.$emit('close-payment-popup', false);
},
// 支付二维码展示窗口事件
popup_view_pay_qrcode_event_close(e) {
// 关闭弹窗
this.setData({
popup_view_pay_qrcode_is_show: false,
});
// 清除定时和支付数据
clearInterval(this.pay_statuc_check_timer);
},
// 选择支付方式
checked_payment(e) {
this.setData({
payment_id: e.currentTarget.dataset.value,
submit_disabled_status: false,
});
},
// 支付弹窗发起支付
popup_payment_event() {
if (this.submit_disabled_status) {
app.globalData.showToast(this.$t('payment.payment.x6d585'));
return false;
}
this.setData({
is_show_payment_popup: false,
});
this.pay_handle(this.propTempPayValue, this.payment_id);
this.$emit('close-payment-popup', false);
},
// 支付方法
pay_handle(order_id, payment_id = 0, payment_list = []) {
// 没有指定支付方式则使用属性传过来的值
if((payment_list || null) != null && payment_list.length > 0) {
this.setData({
payment_list: payment_list
});
}
// 没有支付方式
if((payment_id || 0) == 0) {
this.pay_handle_event(order_id, payment_id);
} else {
// 循环匹配支付方式
this.payment_list.forEach((item) => {
if (item.id == payment_id) {
if (item.payment == 'WalletPay') {
var self = this;
uni.showModal({
title: self.$t('common.warm_tips'),
content: self.$t('payment.payment.011cj4'),
confirmText: self.$t('common.confirm'),
cancelText: self.$t('common.not_yet'),
success(res) {
if (res.confirm) {
self.pay_handle_event(order_id, payment_id);
} else {
self.order_item_pay_fail_handle(null, order_id, self.$t('paytips.paytips.6mpsl7'));
}
},
});
} else {
this.pay_handle_event(order_id, payment_id);
}
}
});
}
},
// 支付处理
pay_handle_event(order_id, payment_id = 0) {
// 没有指定支付方式则不匹配支付标识
var payment = null;
if((payment_id || 0) != 0) {
// #ifdef H5
// 微信环境判断是否已有web_openid、不存在则不继续执行跳转到插件进行授权
if (!app.globalData.is_user_weixin_web_openid(order_id, payment_id || this.payment_id, this.propToAppointPage)) {
return false;
}
// #endif
// 支付方式
for (var i in this.payment_list) {
if (this.payment_list[i]['id'] == (payment_id || this.payment_id)) {
payment = this.payment_list[i];
}
}
if (payment == null) {
app.globalData.showToast(this.$t('payment.payment.7ihx9u'));
return false;
}
}
// 请求数据
var post_data = {
[this.propPayDataKey]: order_id,
payment_id: payment_id || this.payment_id,
};
// h5自定义重定向地址
// #ifdef H5
var redirect_url = app.globalData.page_url_protocol(this.propToAppointPage || app.globalData.get_page_url(false));
post_data['redirect_url'] = encodeURIComponent(base64.encode(redirect_url));
// 存在支付标识、指定支付方式使用respond_url返回地址、移除重定向地址
if(payment != null) {
var respond_arr = ['PayPal', 'UniPayment'];
if (respond_arr.indexOf(payment.payment) != -1) {
post_data['respond_url'] = post_data['redirect_url'];
delete post_data['redirect_url'];
}
}
// #endif
// 请求支付接口
uni.showLoading({
title: this.$t('payment.payment.e1f54e'),
mask: true
});
if (this.propPayUrl) {
uni.request({
url: this.propPayUrl,
method: 'POST',
data: post_data,
dataType: 'json',
success: (res) => {
uni.hideLoading();
var data = res.data.data;
this.setData({
pay_response_data: data || {}
});
if (res.data.code == 0) {
// 是否直接支付成功
if ((data.is_success || 0) == 1) {
// 数据设置
this.order_item_pay_success_handle(data, order_id, false);
app.globalData.showToast(this.$t('paytips.paytips.679rxu'), 'success');
setTimeout(() => {
this.to_success_page_event();
}, 2000);
} else {
// 支付方式类型
let payment_type = Number(data.is_payment_type || 0);
switch (payment_type) {
// 正常线上支付
case 0:
// #ifdef APP
this.app_pay_handle(this, data, order_id);
// #endif
// #ifdef MP-TOUTIAO
// 头条是否非普通版本支持
if(parseInt(data.data.pay_type || 0) == 1) {
this.toutiao_transaction_pay_handle(this, data, order_id);
} else {
this.mp_pay_handle(this, data, order_id);
}
// #endif
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU
this.mp_pay_handle(this, data, order_id);
// #endif
// #ifdef MP-KUAISHOU
this.kuaishou_pay_handle(this, data, order_id);
// #endif
// #ifdef MP-QQ
this.qq_pay_handle(this, data, order_id);
// #endif
// #ifdef H5
this.h5_pay_handle(this, data, order_id);
// #endif
break;
// 线下支付
case 1:
// 现金支付
let self = this;
uni.showModal({
content: res.data.msg,
showCancel: false,
confirmText: self.$t('common.confirm'),
success(res) {
if (res.confirm) {
self.to_other(order_id);
} else {
self.order_item_pay_fail_handle(data, order_id, self.$t('paytips.paytips.6mpsl7'));
}
},
});
break;
// 钱包支付
case 2:
this.order_item_pay_success_handle(data, order_id);
break;
// 默认
default:
app.globalData.showToast(this.$t('payment.payment.vhx5dv'));
}
}
} else {
// 是否返回html代码展示、则提示错误
if (res.data.code == -6666 && (data || null) != null) {
this.setData({
popup_view_pay_data: data,
popup_view_pay_html_is_show: true,
});
} else {
this.order_item_pay_fail_handle(data, order_id, res.data.msg);
}
}
},
fail: (res) => {
uni.hideLoading();
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
} else {
app.globalData.showToast(this.$t('payment.payment.597s8b'));
}
},
// APP支付
app_pay_handle(self, data, order_id) {
var arr = {
Alipay: 'alipay',
Weixin: 'wxpay',
PayPal: 'paypal'
}
var pay_value = arr[data.payment.payment] || null;
if(pay_value != null) {
uni.getProvider({
service: 'payment',
success: function (res) {
if(~res.provider.indexOf(pay_value)) {
var pay_data = ((data.data.pay_data || null) == null) ? data.data : data.data.pay_data;
uni.requestPayment({
provider: pay_value,
orderInfo: pay_data,
success: function (res) {
// 是否需要回调捕获
var call_back_url = data.data.call_back_url || null;
if(call_back_url != null) {
uni.request({url: call_back_url, method: 'GET'});
}
// 成功处理数据
self.order_item_pay_success_handle(data, order_id);
},
fail: function (err) {
self.order_item_pay_fail_handle(data, order_id, self.$t('paytips.paytips.6y488i'));
}
});
} else {
app.globalData.showToast(data.payment.payment+self.$t('payment.payment.bv637f'));
}
}
});
} else {
// 先清除定时任务
if(self.open_pay_url_timer != null) {
clearTimeout(self.open_pay_url_timer);
}
// 显示加载层
uni.showLoading({
title: self.$t('common.loading_in_text'),
mask: true
});
// 设置打开url状态
self.setData({
open_pay_url_status: true
});
// 打开url
plus.runtime.openURL(data.data, function(error) {
uni.hideLoading();
// 打开url失败、并进入提示失败环节
self.setData({
open_pay_url_status: false
});
self.order_item_pay_fail_handle(data, order_id, error.message+'('+error.code+')');
});
// 定时3秒后提示用户确认支付状态
self.open_pay_url_timer = setTimeout(function() {
if(self.open_pay_url_status) {
uni.hideLoading();
uni.showModal({
content: self.$t('payment.payment.sdfs31'),
showCancel: true,
cancelText: self.$t('common.not_have_name'),
confirmText: self.$t('order.order.s8g966'),
success(res) {
if (res.confirm) {
self.order_item_pay_success_handle(data, order_id);
} else {
self.order_item_pay_fail_handle(data, order_id, self.$t('paytips.paytips.6y488i'));
}
},
});
}
}, 3000);
}
},
// 快手小程序
kuaishou_pay_handle(self, data, order_id) {
uni.pay({
orderInfo: data.data,
serviceId: '1',
success: (res) => {
// 数据设置
self.order_item_pay_success_handle(data, order_id);
},
fail: (res) => {
self.order_item_pay_fail_handle(data, order_id, self.$t('paytips.paytips.6y488i'));
},
});
},
// 头条小程序非普通交易支付处理
toutiao_transaction_pay_handle(self, data, order_id) {
if(!uni.canIUse('requestOrder') || !uni.canIUse('getOrderPayment')) {
app.globalData.showToast(self.$t('payment.payment.4dszme'));
return false;
}
uni.requestOrder({
data: data.data.data,
byteAuthorization: data.data.auth,
success: (res) => {
uni.getOrderPayment({
orderId: res.orderId,
success: (res) => {
// 数据设置
self.order_item_pay_success_handle(data, order_id);
},
fail: (res) => {
self.order_item_pay_fail_handle(data, order_id, self.$t('paytips.paytips.6y488i'));
}
});
},
fail: (res) => {
app.globalData.showToast(res.errMsg+'('+res.errNo+')');
}
});
},
// 小程序: 微信、支付宝、百度、头条、QQ
mp_pay_handle(self, data, order_id) {
// 是否打开另一个小程序
if (typeof data.data != 'string' && (data.data.appid || null) != null && (data.data.path || null) != null && (data.data.order_no || null) != null) {
uni.navigateToMiniProgram({
appId: data.data.appid,
path: data.data.path,
extraData: data.data.extra_data || {},
success(res) {
// 支付状态验证
self.pay_status_check_handle(self, data, order_id);
// 提示弹窗
self.setData({
payment_confirm_modal_status: true,
});
},
fail(res) {
app.globalData.showToast(self.$t('paytips.paytips.6y488i'));
}
});
} else {
uni.requestPayment({
// #ifdef MP-ALIPAY || MP-BAIDU || MP-TOUTIAO
orderInfo: data.data,
// #endif
// #ifdef MP-QQ
package: data.data,
// #endif
// #ifdef MP-WEIXIN
timeStamp: data.data.timeStamp,
nonceStr: data.data.nonceStr,
package: data.data.package,
signType: data.data.signType,
paySign: data.data.paySign,
// #endif
// #ifdef MP-TOUTIAO
service: 5,
// #endif
success: (res) => {
// #ifdef MP-ALIPAY
if (res.resultCode != 9000) {
self.order_item_pay_fail_handle(data, order_id, res.memo || self.$t('paytips.paytips.6y488i'));
return false;
}
// #endif
// #ifdef MP-TOUTIAO
if (res.code != 0) {
self.order_item_pay_fail_handle(data, order_id, self.$t('paytips.paytips.6y488i'));
return false;
}
// #endif
// 数据设置
self.order_item_pay_success_handle(data, order_id);
},
fail: (res) => {
self.order_item_pay_fail_handle(data, order_id, self.$t('paytips.paytips.6y488i'));
},
});
}
},
// QQ支付处理
qq_pay_handle(self, data, order_id) {
// 是否微信支付
if (data.payment.payment == 'Weixin') {
uni.requestWxPayment({
url: data.data,
referer: app.globalData.data.request_url,
success: function (res) {
app.globalData.alert({
msg: self.$t('payment.payment.k2i010'),
is_show_cancel: 0,
});
// 支付接口调用成功,但是不知道是否支付成功,所以需要重新获取列表数据
self.$emit('reset-event');
},
fail: function (res) {
self.order_item_pay_fail_handle(data, order_id, self.$t('paytips.paytips.6y488i'));
},
});
} else {
self.mp_pay_handle(self, data, order_id);
}
},
// h5支付处理
h5_pay_handle(self, data, order_id) {
// 字符串则为跳转地址直接进入
if (typeof data.data == 'string') {
window.location.href = data.data;
} else {
var status = false;
// 微信jsapi
if (data.payment.payment == 'Weixin' && (data.data.appId || null) != null && (data.data.timeStamp || null) != null && (data.data.nonceStr || null) != null && (data.data.package || null) != null && (data.data.signType || null) != null && (data.data.paySign || null) != null) {
status = true;
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{
appId: data.data.appId,
timeStamp: data.data.timeStamp,
nonceStr: data.data.nonceStr,
package: data.data.package,
signType: data.data.signType,
paySign: data.data.paySign,
},
function (res) {
if (res.err_msg == 'get_brand_wcpay_request:ok') {
// 数据设置
self.order_item_pay_success_handle(data, order_id);
} else {
self.order_item_pay_fail_handle(data, order_id, res.err_msg);
}
}
);
}
if (typeof WeixinJSBridge == 'undefined') {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
}
// 二维码展示
if ((data.data.qrcode_url || null) != null && (data.data.name || null) != null && (data.data.order_no || null) != null) {
status = true;
// 显示支付窗口
self.setData({
popup_view_pay_data: data.data,
popup_view_pay_qrcode_is_show: true,
});
// 状态验证
self.pay_status_check_handle(self, data, order_id);
}
// 返回html表单
if ((data.data.html || null) != null) {
status = true;
var div = document.createElement('paydivform');
div.innerHTML = data.data.html;
document.body.appendChild(div);
var fm = document.forms;
var fm_len = fm.length;
if (fm_len > 0) {
fm[fm_len - 1].submit();
}
}
// 未匹配到的支付处理方式
if (!status) {
app.globalData.showToast(data.payment.name + self.$t('payment.payment.2rw3qh'));
}
}
},
// 支付状态验证处理
pay_status_check_handle(self, data, order_id) {
// 先清除已存在的定时
clearInterval(self.pay_statuc_check_timer);
// 定时校验支付状态
var timer = setInterval(function () {
uni.request({
url: self.propQrcodeUrl,
method: 'POST',
data: {
order_no: data.data.order_no,
},
dataType: 'json',
success: (res) => {
if (res.data.code == 0) {
// 清除定时、支付数据、弹窗
clearInterval(self.pay_statuc_check_timer);
self.setData({
popup_view_pay_data: null,
popup_view_pay_qrcode_is_show: false,
payment_confirm_modal_status: false,
});
// 数据设置
self.order_item_pay_success_handle(data, order_id);
} else {
// -333支付中、其它状态则提示错误
if (res.data.code != -333) {
clearInterval(self.pay_statuc_check_timer);
app.globalData.showToast(res.data.msg);
}
}
},
fail: () => {
clearInterval(self.pay_statuc_check_timer);
self.order_item_pay_fail_handle(data, order_id, self.$t('common.internet_error_tips'));
},
});
}, 3000);
self.setData({
pay_statuc_check_timer: timer,
});
},
// 支付确认弹窗事件
payment_confirm_event(e) {
// 关闭弹窗清除定时任务
this.setData({
payment_confirm_modal_status: false,
});
clearInterval(this.pay_statuc_check_timer);
// 回调处理
if(parseInt(e.currentTarget.dataset.type || 0) == 1) {
this.order_item_pay_success_handle(this.pay_response_data, this.order_id);
} else {
this.order_item_pay_fail_handle(this.pay_response_data, this.order_id, this.$t('paytips.paytips.6y488i'));
}
},
// 支付成功数据设置 data:后台返回的参数, order_id: 订单idis_to_page是否需要跳转页面的参数控制
order_item_pay_success_handle(data, order_id, is_to_page = true) {
let back_data = {
data: data,
order_id: order_id,
temp_pay_index: this.propTempPayIndex,
payment_id: this.payment_id,
is_to_page: is_to_page,
};
this.$emit('pay-success', back_data);
if (is_to_page) {
this.to_success_page_event();
}
},
// 支付失败数据设置 data:后台返回的参数, order_id: 订单id, msg: 错误提示信息
order_item_pay_fail_handle(data, order_id, msg) {
let back_data = {
data: data,
order_id: order_id,
temp_pay_index: this.propTempPayIndex,
payment_id: this.payment_id,
};
this.$emit('pay-fail', back_data);
this.to_fail_page_event(msg);
},
// 成功跳转
to_success_page_event() {
if (this.propToPage) {
// 跳转支付页面
app.globalData.url_open(this.propToPage, true);
} else {
let url_data = {
code: '9000',
};
url_data = Object.assign({}, url_data, this.propToPageBack);
// 跳转支付页面
app.globalData.url_open('/pages/paytips/paytips?params=' + encodeURIComponent(base64.encode(JSON.stringify(url_data))), this.propIsRedirectTo);
}
},
// 失败跳转
to_fail_page_event(msg) {
let to_fail_page = this.propToFailPage || null;
if (to_fail_page != null) {
let join = (to_fail_page.indexOf('?') == -1) ? '?' : '&';
to_fail_page += join+'msg='+msg;
if (this.propIsFailAlert) {
// 现金支付
uni.showModal({
content: msg,
showCancel: false,
confirmText: this.$t('common.confirm'),
success(res) {
if (res.confirm) {
// 跳转支付页面
app.globalData.url_open(to_fail_page, true);
}
},
});
} else {
// 跳转支付页面
app.globalData.url_open(to_fail_page, true);
}
} else {
if (msg) {
app.globalData.showToast(msg);
}
}
},
to_other(order_id) {
if (this.propToAppointPage) {
// 跳转订单列表页
app.globalData.url_open(this.propToAppointPage, true);
}
},
// 页面卸载
onUnload(e) {
clearInterval(this.pay_statuc_check_timer);
},
// 支付html展示窗口事件
popup_view_pay_html_event_close(e) {
this.setData({
popup_view_pay_html_is_show: false,
});
this.to_other();
},
},
};
</script>
<style scoped>
/**
* 支付方式
*/
.payment-price {
font-size: 80rpx;
}
.payment-list .scroll-y {
max-height: 430rpx;
}
.payment-list .item {
padding: 28rpx 28rpx 28rpx 32rpx;
}
.payment-list .icon {
width: 50rpx;
height: 50rpx !important;
}
.payment-submit {
padding: 40rpx;
}
/**
* 支付确认弹窗
*/
.payment-confirm-modal {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.6);
z-index: 100;
width: 100%;
height: 100%;
}
.payment-confirm-modal .content {
margin-top: 60%;
z-index: 101;
}
</style>

214
components/popup/popup.vue Normal file
View File

@@ -0,0 +1,214 @@
<template>
<view :class="theme_view + ' ' + propMostClass">
<view :class="'popup ' + (propClassname || '') + ' ' + (propShow ? 'popup-show' : 'popup-hide') + ' ' + (propAnimation ? 'animation' : '')" :disable-scroll="propDisablescroll">
<view class="popup-mask" :style="'z-index: ' + propIndex + ';'" v-if="propMask" @tap="on_mask_tap"></view>
<view :class="'popup-content popup-' + (propPosition || 'bottom') + ' ' + (propIsRadius ? '' : 'popup-radius-0') + ' ' + (propIsBar ? 'popup-bar' : '') + ' ' + (propPosition === 'bottom' ? 'bottom-line-exclude' : '')" :style="popup_content_style + propStyle">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
popup_content_style: '',
};
},
components: {},
props: {
// 最外层的class
propMostClass: {
type: String,
default: '',
},
// 内层class
propClassname: {
type: String,
default: '',
},
propShow: {
type: Boolean,
default: false,
},
propPosition: {
type: String,
default: 'bottom',
},
propMask: {
type: Boolean,
default: true,
},
propAnimation: {
type: Boolean,
default: true,
},
propDisablescroll: {
type: Boolean,
default: false,
},
propIsBar: {
type: Boolean,
default: false,
},
// 弹窗是否需要圆角 默认需要
propIsRadius: {
type: Boolean,
default: true,
},
propIndex: {
type: Number,
default: 100,
},
// 需要携带单位后缀
propTop: {
type: [String, Number],
default: '',
},
propBottom: {
type: String,
default: '',
},
propStyle: {
type: String,
default: '',
},
},
// 属性值改变监听
watch: {
// 监听状态
propShow(value, old_value) {
this.init_handle();
},
},
// 组建创建
created: function () {
this.init_handle();
},
methods: {
// 事件处理
on_mask_tap: function on_mask_tap() {
this.$emit(
'onclose',
{
detail: {},
},
{}
);
},
// 初初始化处理
init_handle() {
var tabbar_height = 0;
if(this.propPosition == 'bottom') {
// 弹窗从底部弹出,获取底部菜单高度、如果当前为底部菜单页面则增加底部间距
if(app.globalData.data.is_use_native_tabbar != 1 && app.globalData.is_tabbar_pages()) {
tabbar_height = (app.globalData.app_system_tabbar_height_value()*2)+20;
} else {
var height = (app.globalData.current_page(false) == 'pages/diy/diy') ? app.globalData.app_diy_tabbar_height_value() : 0;
tabbar_height = (height > 0) ? (height*2)+20 : 0;
}
}
// 左边距位置处理
var left = 0;
// #ifdef H5
// 处理内容左边距、避免父级设置内边距影响
var width = uni.getSystemInfoSync().windowWidth;
if (width > 960) {
left = (width - 800) / 2;
}
// #endif
this.setData({
popup_content_style: 'left:' + left + 'px;' + (this.propTop ? 'top:' + this.propTop : '') + ';' + (this.propBottom ? 'bottom:' + this.propBottom : '') + ';padding-bottom:' + tabbar_height + 'rpx;',
});
},
},
};
</script>
<style>
.popup {
opacity: 0;
}
.popup-content {
position: fixed;
background: #fff;
z-index: 101;
overflow: hidden;
}
.popup-mask {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.6);
opacity: 0;
pointer-events: none;
z-index: 100;
}
.popup-left {
transform: translateX(-100%);
left: 0;
top: 0;
bottom: 0;
}
.popup-right {
transform: translateX(100%);
right: 0;
top: 0;
bottom: 0;
}
.popup-top {
top: 0;
width: 100vw;
transform: translateY(-100%);
}
.popup-bottom {
bottom: var(--window-bottom);
width: 100vw;
transform: translateY(100%);
}
.popup-show {
opacity: 1;
}
.popup-hide {
transition: all 1s linear;
}
.popup-show .popup-content {
transform: none;
}
.popup-show .popup-mask {
opacity: 1;
pointer-events: auto;
}
.popup.animation .popup-mask,
.popup.animation .popup-content {
transition: all 0.35s linear;
}
.popup-top {
border-bottom-right-radius: 20rpx;
border-bottom-left-radius: 20rpx;
}
.popup-bottom {
border-top-right-radius: 20rpx;
border-top-left-radius: 20rpx;
}
.popup-left {
border-top-right-radius: 20rpx;
border-bottom-right-radius: 20rpx;
}
.popup-right {
border-top-left-radius: 20rpx;
border-bottom-left-radius: 20rpx;
}
.popup-radius-0 {
border-radius: 0 !important;
}
.popup-bar {
/* #ifdef H5 || APP */
bottom: var(--window-bottom) !important;
/* #endif */
}
</style>

View File

@@ -0,0 +1,131 @@
<template>
<view :class="theme_view">
<view v-if="(data || null) != null && status == 1" class="plugins-popupscreen wh-auto ht-auto">
<view class="content pr">
<image class="dis-block auto" :src="data.images" mode="widthFix" :data-value="data.images_url || ''" @tap="url_event"></image>
<view class="tc margin-top-xl">
<view class="close cp round padding-sm auto" @tap.stop="close_event">
<iconfont name="icon-close-o" size="28rpx" color="#cacaca"></iconfont>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
data: null,
status: 0,
cache_key: 'plugins_popupscreen_cache_key',
timer: null,
};
},
props: {},
// 页面被展示
created: function () {
this.init_config();
},
methods: {
// 初始化配置
init_config(status) {
if ((status || false) == true) {
this.setData({
data: app.globalData.get_config('plugins_base.popupscreen.data') || null,
});
this.init();
} else {
app.globalData.is_config(this, 'init_config');
}
},
// 初始化
init() {
var data = this.data || null;
if (data != null && parseInt(data.is_app_enable || 0) == 1 && parseInt(data.is_valid || 0) == 1) {
// 是否全局
var temp_status = true;
if(parseFloat(data.is_overall || 0) == 0) {
// 非首页则不展示
if(!app.globalData.is_tabbar_home()) {
temp_status = false;
}
}
if(temp_status) {
// 不存在关闭缓存或者超过间隔时间则显示
var key = this.cache_key;
var cv = parseInt(uni.getStorageSync(key)) || 0;
var pv = parseInt(data.interval_time) || 86400;
if (cv == 0 || cv + pv < app.globalData.get_timestamp()) {
// 是否开启自动关闭
var timer = null;
var ct = parseInt(data.close_time) || 0;
if (ct > 0) {
var self = this;
timer = setTimeout(function () {
self.setData({
status: 0,
});
uni.setStorage({
key: key,
data: app.globalData.get_timestamp(),
});
}, ct * 1000);
}
this.setData({
status: 1,
timer: timer,
});
}
}
}
},
// 关闭事件
close_event(e) {
this.setData({
status: 0,
});
uni.setStorage({
key: this.cache_key,
data: app.globalData.get_timestamp(),
});
clearInterval(this.timer);
},
// url事件
url_event(e) {
app.globalData.url_event(e);
}
}
};
</script>
<style scoped>
.plugins-popupscreen {
position: fixed;
left: 0;
top: 0;
z-index: 20;
background-color: rgb(0 0 0 / 0.3);
}
.plugins-popupscreen .close {
right: 10%;
top: 0;
z-index: 1;
width: 46rpx;
height: 46rpx;
line-height: 46rpx;
background-color: rgb(4 4 4 / 0.3);
border: solid 1px #a9a9a9;
}
.plugins-popupscreen .content {
margin-top: calc(50vh - 200rpx) !important;
}
.plugins-popupscreen .content image {
width: 600rpx;
}
</style>

View File

@@ -0,0 +1,265 @@
<template>
<view :class="theme_view">
<!-- 开启事件 -->
<movable-area v-if="propIsBtn && quick_status == 1" :class="'quick-movable-container ' + common_ent" :style="'height: calc(100% - ' + height_dec + 'rpx);top:' + top + 'rpx;'">
<movable-view direction="all" :x="x" :y="y" :animation="false" class="quick-event-submit" @tap="quick_open_event">
<image class="image" :src="common_static_url + 'quick-icon.png'" mode="widthFix"></image>
</movable-view>
</movable-area>
<!-- 弹窗 -->
<component-popup :propShow="popup_status" :propIsBar="propIsBar" propPosition="bottom" @onclose="quick_close_event">
<view :class="'nav-popup-container ' + common_ent">
<view class="close oh">
<view class="fr" @tap.stop="quick_close_event">
<iconfont name="icon-close-o" size="28rpx" color="#999"></iconfont>
</view>
</view>
<view class="nav-popup-content">
<view v-if="data_list.length > 0" class="nav-data-list">
<view v-for="(item, index) in data_list" :key="index" class="item cp">
<view :class="'item-content ' + ((item.bg_color || null) == null ? 'item-exposed' : '')" :data-value="item.event_value" :data-type="item.event_type" @tap="navigation_event" :style="(item.bg_color || null) == null ? '' : 'background-color:' + item.bg_color + ';'">
<image class="image" :src="item.images_url" mode="aspectFit"></image>
</view>
<view class="title">{{ item.name }}</view>
</view>
</view>
<view v-else>
<!-- 提示信息 -->
<component-no-data :propStatus="0"></component-no-data>
</view>
</view>
</view>
</component-popup>
</view>
</template>
<script>
const app = getApp();
import componentPopup from '@/components/popup/popup';
import componentNoData from '@/components/no-data/no-data';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
common_static_url: app.globalData.get_static_url('common'),
popup_status: false,
quick_status: 0,
data_list: [],
system: null,
x: 0,
y: 0,
top: 0,
height_dec: 0,
is_first: 1,
common_ent: '',
};
},
components: {
componentPopup,
componentNoData,
},
props: {
propIsBtn: {
type: Boolean,
default: true,
},
propIsBar: {
type: Boolean,
default: false,
},
propIsNav: {
type: Boolean,
default: false,
},
propIsGrayscale: {
type: Boolean,
default: false,
},
},
// 属性值改变监听
watch: {
// 是否灰度
propIsGrayscale(value, old_value) {
this.common_ent = value ? 'grayscale' : '';
},
},
// 页面被展示
created: function () {
this.init_config();
// 页面是否定义导航
var value = this.propIsNav ? 100 : 0;
this.top = value;
this.height_dec = value;
// #ifdef H5 || APP
this.top = 140;
this.height_dec = this.propIsBar ? 280 : 140;
// #endif
// 非首次进入则重新初始化配置接口
if (this.is_first == 0) {
app.globalData.init_config();
}
// 数据设置
var system = app.globalData.get_system_info(null, null, true);
var height = app.globalData.window_height_handle(system);
var width = app.globalData.window_width_handle(system.windowWidth);
this.setData({
is_first: 0,
system: system,
x: width - 65,
y: height - 280,
// 是否灰度
common_ent: this.propIsGrayscale ? 'grayscale' : '',
});
},
methods: {
// 初始化配置
init_config(status) {
if ((status || false) == true) {
var data_list = app.globalData.get_config('quick_nav') || [];
this.setData({
data_list: data_list,
quick_status: (data_list.length > 0) ? (app.globalData.get_config('config.home_navigation_main_quick_status') || 0) : 0,
});
} else {
app.globalData.is_config(this, 'init_config');
}
},
// 弹层开启
quick_open_event(e) {
this.setData({
popup_status: true,
data_list: app.globalData.get_config('quick_nav') || [],
});
},
// 弹层关闭
quick_close_event(e) {
this.setData({
popup_status: false,
});
},
// 操作事件
navigation_event(e) {
this.setData({
popup_status: false,
});
app.globalData.operation_event(e);
},
},
};
</script>
<style>
/**
* 按钮
*/
.quick-movable-container {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: transparent;
pointer-events: none;
z-index: 2;
}
.quick-event-submit {
pointer-events: auto;
width: 50px;
height: 50px;
border-radius: 50%;
}
.quick-event-submit .image {
width: 50px !important;
height: 50px !important;
display: block;
position: relative;
z-index: 10;
border-radius: 100%;
box-shadow: 0 0 6rpx 10rpx rgba(89, 181, 255, 15%);
background: #59b5ff;
}
/**
* 弹窗
*/
.nav-popup-container {
padding: 20rpx 10rpx 0 10rpx;
background: #fff;
}
.nav-popup-container .close {
position: absolute;
top: 20rpx;
right: 20rpx;
z-index: 2;
}
.nav-popup-content {
max-height: 80vh;
overflow-y: scroll;
overflow-x: hidden;
padding-bottom: 20rpx;
}
/**
* 内容
*/
.nav-data-list {
overflow: hidden;
background: #fff;
}
.nav-data-list .item {
width: calc(25% - 60rpx);
float: left;
padding: 30rpx;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.nav-data-list .item-content {
border-radius: 50%;
padding: 20rpx;
text-align: center;
margin: 0 auto;
-webkit-box-shadow: 0 2px 12px rgb(226 226 226 / 95%);
box-shadow: 0 2px 12px rgb(226 226 226 / 95%);
}
.nav-data-list .item-content,
.nav-data-list .item .image {
width: 70rpx !important;
height: 70rpx !important;
}
.nav-data-list .item .item-exposed {
padding: 0;
-webkit-box-shadow: none;
box-shadow: none;
}
.nav-data-list .item .item-exposed,
.nav-data-list .item .item-exposed .image {
width: 110rpx !important;
height: 110rpx !important;
}
.nav-data-list .item .title {
margin-top: 10rpx;
font-size: 28rpx !important;
text-align: center;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
max-width: 100%;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,233 @@
<template>
<view :class="theme_view">
<view v-if="(data_list || null) != null && data_list.length > 0" class="plugins-realstore-data-list oh">
<block v-for="(item, index) in data_list" :key="index">
<view class="item bg-white padding-top-xl padding-bottom-sm padding-horizontal-main border-radius-main pr spacing-mb" :class="item.status_info.type === 2 ? 'opacity' : ''" :data-index="index" :data-value="item.url+propRealstoreDetailQuery" @tap="realstore_item_event">
<view class="base oh flex-row">
<!-- 基础内容 -->
<image :src="item.logo" mode="widthFix" class="logo circle br"></image>
<view class="base-right flex-1 flex-width">
<view class="title fw-b text-size single-text tl">
<text v-if="(item.alias || null) != null" class="va-m title-icon border-radius-sm br-main cr-main text-size-xs padding-horizontal-xs margin-right-xs">{{ item.alias }}</text>
<text class="va-m">{{ item.name }}</text>
</view>
<view class="margin-top-sm padding-top-xs text-size-xs cr-grey">
<view v-if="(item.status_info.time || null) != null" class="flex-row align-c">
<iconfont name="icon-time pr top-xs cr-grey-9"></iconfont>
<view :class="'status-icon text-size-xs divider-r padding-left-xs padding-right-sm margin-right-sm ' + (item.status_info.status == 1 ? 'cr-green' : item.status_info.type == 1 ? 'cr-red' : 'cr-grey-c')">
{{ item.status_info.msg }}
</view>
{{ item.status_info.time }}
</view>
</view>
</view>
</view>
<view class="flex-row jc-sb align-c br-t-dashed margin-top-main padding-top-sm">
<!-- 地址 -->
<view class="address-content single-text cr-base margin-left-xs dis-inline-block text-size-xs oh cp tl" :data-value="item.province_name + item.city_name + item.county_name + item.address" @tap.stop="text_copy_event">
<view class="dis-inline-block va-m cr-grey-9 margin-top-sm">
<iconfont name="icon-map-address"></iconfont>
</view>
<text class="va-m margin-left-xs">{{ item.province_name }}{{ item.city_name }}{{ item.county_name }}{{ item.address }}</text>
</view>
<view v-if="(item.distance || null) != null" class="text-size-xs cr-grey-c pa address-distance">{{$t('extraction-address.extraction-address.42v8tv')}}{{ item.distance }}</view>
</view>
<!-- 右侧操作 -->
<view class="icon-list pa">
<view v-if="(item.service_data || null) != null && (item.service_data.service_tel || null) != null" class="icon-item dis-inline-block tc cp" :data-value="item.service_data.service_tel" @tap.stop="tel_event">
<iconfont name="icon-tel" size="30rpx"></iconfont>
</view>
<!-- #ifndef MP-KUAISHOU -->
<view v-if="item.lat != 0 && item.lng != 0" class="icon-item dis-inline-block tc cp" :data-index="index" @tap.stop="address_map_event">
<iconfont name="icon-send" size="30rpx"></iconfont>
</view>
<!-- #endif -->
</view>
</view>
</block>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
data_list: [],
favor_user: [],
};
},
components: {},
props: {
propIsFavor: {
type: Boolean,
default: true,
},
propIsChoice: {
type: Boolean,
default: false,
},
propIsChoiceBackType: {
type: String,
default: '',
},
propIsOpenRealstoreRedirect: {
type: Boolean,
default: true,
},
propData: {
type: Object,
default: () => {
return {};
},
},
propFavorUser: {
type: Array,
default: () => [],
},
propRealstoreDetailQuery: {
type: String,
default: '',
},
},
// 属性值改变监听
watch: {
// 数据
propData(value, old_value) {
this.init();
}
},
// 页面被展示
created: function (e) {
this.init();
},
methods: {
// 初始化
init() {
var data_list = ((this.propData || null) == null || (this.propData.data || null) == null || this.propData.data.length == 0) ? [] : this.propData.data;
this.setData({
data_list: data_list,
favor_user: this.propFavorUser,
});
this.data_list_handle();
},
// 数据列表处理
data_list_handle() {
var temp_data_list = this.data_list;
for (var i in temp_data_list) {
temp_data_list[i]["is_favor"] = this.favor_user.indexOf(temp_data_list[i]["id"]) == -1 ? 0 : 1;
}
this.setData({
data_list: temp_data_list,
});
},
// 收藏事件
favor_event(e) {
if (!app.globalData.is_single_page_check()) {
return false;
}
var user = app.globalData.get_user_info(this, "favor_event");
if (user != false) {
var index = e.currentTarget.dataset.index;
var info = this.data_list[index];
uni.showLoading({
title: this.$t('common.processing_in_text'),
});
uni.request({
url: app.globalData.get_request_url("reversal", "favor", "realstore"),
method: "POST",
data: {
id: info.id,
},
dataType: "json",
success: (res) => {
uni.hideLoading();
if (res.data.code == 0) {
var temp_data = this.data_list;
var temp_favor = this.favor_user;
temp_data[index]["is_favor"] = res.data.data.status;
if (res.data.data.status == 1) {
if (temp_favor.indexOf(info.id) == -1) {
temp_favor.push(info.id);
}
} else {
if (temp_favor.indexOf(info.id) != -1) {
temp_favor.splice(index, 1);
}
}
this.setData({
data_list: temp_data,
favor_user: temp_favor,
});
app.globalData.showToast(res.data.msg, "success");
} else {
if (app.globalData.is_login_check(res.data, this, "favor_event")) {
app.globalData.showToast(res.data.msg);
}
}
},
fail: () => {
uni.hideLoading();
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
}
},
// 电话
tel_event(e) {
app.globalData.call_tel(e);
},
// 剪切板
text_copy_event(e) {
app.globalData.text_copy_event(e);
},
// 地图查看
address_map_event(e) {
var info = this.data_list[e.currentTarget.dataset.index] || {};
if (info.lat == 0 || info.lng == 0) {
app.globalData.showToast(this.$t('user-order-detail.user-order-detail.i876o3'));
return false;
}
var address = (info.province_name || "") + (info.city_name || "") + (info.county_name || "") + (info.address || "");
app.globalData.open_location(info.lng, info.lat, info.name, address);
},
// 门店事件
realstore_item_event(e) {
// 是否选择模式
if(this.propIsChoice) {
// 存储门店缓存
var data = this.data_list[e.currentTarget.dataset.index];
uni.setStorageSync(app.globalData.data.cache_realstore_detail_choice_key, {
data: data,
status: 1
});
// 回调事件
this.$emit('onChoiceEvent', data);
// 选择回调类型
switch(this.propIsChoiceBackType) {
// 返回上一个页面
case 'back' :
app.globalData.page_back_prev_event();
break;
// 进入门店详情页面
case 'realstore-detail' :
app.globalData.url_open(data.url, this.propIsOpenRealstoreRedirect);
break;
}
} else {
app.globalData.url_event(e);
}
}
}
};
</script>
<style></style>

View File

@@ -0,0 +1,234 @@
<template>
<view :class="theme_view">
<component-popup :propShow="propShow" propPosition="bottom" @onclose="popup_close_event">
<view class="flex-row jc-sb align-c padding-main">
<text class="cr-grey" @tap="popup_close_event">{{$t('common.cancel')}}</text>
<text class="cr-blue" @tap="sub_ragion_event">{{$t('common.confirm')}}</text>
</view>
<view class="g-dp-ctt-wrapper">
<picker-view class="picker-view" :indicator-style="indicatorStyle" :value="columns_index[0]" data-column="0" @change="changeHandler">
<picker-view-column>
<view class="g-dp-ctt-wp-item" v-for="(item, a) in columns[0]" :key="item.id">{{ item.name }}</view>
</picker-view-column>
</picker-view>
<picker-view class="picker-view" :indicator-style="indicatorStyle" :value="columns_index[1]" data-column="1" @change="changeHandler">
<picker-view-column>
<view class="g-dp-ctt-wp-item" v-for="(item, b) in columns[1]" :key="item.id">{{ item.name }}</view>
</picker-view-column>
</picker-view>
<picker-view class="picker-view" :indicator-style="indicatorStyle" :value="columns_index[2]" data-column="2" @change="changeHandler">
<picker-view-column>
<view class="g-dp-ctt-wp-item" v-for="(item, c) in columns[2]" :key="item.id">{{ item.name }}</view>
</picker-view-column>
</picker-view>
</view>
</component-popup>
</view>
</template>
<script>
const app = getApp();
import componentPopup from "@/components/popup/popup";
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
columns: [[0], [0], [0]],
// 下标
columns_index: [[0], [0], [0]],
indicatorStyle: `height: ${uni.upx2px(88)}px;`,
};
},
components: {
componentPopup,
},
props: {
propShow: {
type: Boolean,
default: false,
},
propProvinceId: {
type: [String,Number],
default: "",
},
propCityId: {
type: [String,Number],
default: "",
},
propCountyId: {
type: [String,Number],
default: "",
},
},
watch: {
propShow(new_val, old_val) {
if (new_val) {
this.getProvince();
}
},
},
methods: {
// 地区初始化匹配索引
get_region_value(index, id) {
var data = this.columns[index];
var data_id = id;
var list_index = [];
data.forEach((d, i) => {
if (d.id == data_id) {
list_index = [i];
return false;
}
});
this.$set(this.columns_index, index, list_index);
},
// picker 滚动change事件
changeHandler(e) {
const { dataset, value } = e.target;
if (dataset.column == 0) {
if (this.columns[0][value[0]].id) {
this.$set(this.columns_index, dataset.column, value);
this.$set(this.columns_index, 1, [0]);
this.$set(this.columns_index, 2, [0]);
this.getCity(this.columns[0][value[0]].id, true);
}
} else if (dataset.column == 1) {
if (this.columns[1][value[0]].id) {
this.$set(this.columns_index, dataset.column, value);
this.$set(this.columns_index, 2, [0]);
this.getArea(this.columns[1][value[0]].id, true);
}
} else if (dataset.column == 2) {
this.$set(this.columns_index, dataset.column, value);
}
},
// 获取省
getProvince() {
uni.request({
url: app.globalData.get_request_url("index", "region"),
method: "POST",
data: {},
dataType: "json",
success: (res) => {
if (res.data.code == 0) {
var data = res.data.data;
this.$set(this.columns, 0, data);
this.getCity(this.propProvinceId ? this.propProvinceId : data[0].id);
if (this.propProvinceId) {
this.get_region_value(0, this.propProvinceId);
}
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
app.globalData.showToast(this.$t('extraction-apply.extraction-apply.fo7y6c'));
},
});
},
// 获取市
getCity(province_id, init = false) {
if (province_id) {
uni.request({
url: app.globalData.get_request_url("index", "region"),
method: "POST",
data: {
pid: province_id,
},
dataType: "json",
success: (res) => {
if (res.data.code == 0) {
var data = res.data.data;
this.$set(this.columns, 1, data);
if (init) {
this.getArea(data[0].id);
} else {
this.getArea(this.propCityId ? this.propCityId : data[0].id);
if (this.propCityId) {
this.get_region_value(1, this.propCityId);
}
}
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
app.globalData.showToast(this.$t('extraction-apply.extraction-apply.b6qg7b'));
},
});
}
},
// 获取区
getArea(city_id, init = false) {
if (city_id) {
// 加载loding
uni.request({
url: app.globalData.get_request_url("index", "region"),
method: "POST",
data: {
pid: city_id,
},
dataType: "json",
success: (res) => {
if (res.data.code == 0) {
var data = res.data.data;
this.$set(this.columns, 2, data);
if (!init) {
if (this.propCountyId) {
this.get_region_value(2, this.propCountyId);
}
}
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
app.globalData.showToast(this.$t('extraction-apply.extraction-apply.5s5734'));
},
});
}
},
// 关闭按钮
popup_close_event(e) {
this.$emit("onclose", false);
},
//提交按钮
sub_ragion_event(e) {
let province = this.columns[0][this.columns_index[0]];
let city = this.columns[1][this.columns_index[1]];
let areal = this.columns[2][this.columns_index[2]];
this.popup_close_event();
this.$emit("call-back", province, city, areal);
},
},
};
</script>
<style scoped>
::v-deep .popup-bottom {
border-radius: 0;
}
.picker-view {
width: 32%; height: 480rpx
}
.picker-view-column {
height: 480rpx !important;
}
.g-dp-ctt-wrapper {
height: 480upx;
width: 100%;
background-color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.g-dp-ctt-wp-item {
width: 100%;
height: 88upx;
line-height: 88upx;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30upx;
}
</style>

View File

@@ -0,0 +1,262 @@
<template>
<view :class="theme_view">
<view :class="'search-content pr round '+propSize" :style="'background:' + propBgColor + ';' + ((propBrColor || null) != null ? 'border:1px solid ' + propBrColor + ';' : '')">
<view class="search-icon dis-inline-block pa" :style="'padding:' + propPadding" @tap="search_icon_event">
<iconfont :name="propIcon" :color="propIconColor" size="24rpx"></iconfont>
</view>
<input
type="text"
confirm-type="search"
:class="'input round wh-auto dis-block '+propClass"
:placeholder="(propPlaceholder || propPlaceholderValue || this.$t('search.search.660us5'))"
:placeholder-class="propPlaceholderClass"
:value="propDefaultValue"
:focus="propFocus"
@input="search_input_value_event"
@confirm="search_submit_confirm_event"
@focus="search_input_focus_event"
@blur="search_input_blur_event"
:style="'color:' + propTextColor + ';'"
/>
<button v-if="propIsBtn" class="search-btn pa bg-main" size="mini" type="default" @tap="search_submit_confirm_event">{{$t('common.search')}}</button>
</view>
</view>
</template>
<script>
const app = getApp();
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
input_value: '',
};
},
components: {},
props: {
propUrl: {
type: String,
default: '/pages/goods-search/goods-search',
},
propFormName: {
type: String,
default: 'keywords',
},
propDefaultValue: {
type: String,
default: '',
},
propPlaceholder: {
type: String,
default: '',
},
propPlaceholderValue: {
type: String,
default: '',
},
propPlaceholderClass: {
type: String,
default: 'cr-grey-c',
},
propClass: {
type: String,
default: '',
},
propTextColor: {
type: String,
default: '#666',
},
propBgColor: {
type: String,
default: '#fff',
},
propBrColor: {
type: String,
default: '',
},
propIsRequired: {
type: Boolean,
default: true,
},
propIsOnEvent: {
type: Boolean,
default: false,
},
propIsOnFocusEvent: {
type: Boolean,
default: false,
},
propIsOnBlurEvent: {
type: Boolean,
default: false,
},
propIsOnInputEvent: {
type: Boolean,
default: false,
},
propIcon: {
type: String,
default: 'icon-search-max',
},
propIconColor: {
type: String,
default: '#ccc',
},
propIsIconOnEvent: {
type: Boolean,
default: false,
},
propIsBtn: {
type: Boolean,
default: false,
},
propSize: {
type: String,
default: '',
},
propFocus: {
type: Boolean,
default: false,
},
propPadding: {
type: String,
default: '16rpx 20rpx 0 20rpx;',
},
},
// 属性值改变监听
watch: {
// 默认值
propDefaultValue(value, old_value) {
this.setData({
input_value: value,
});
}
},
// 页面被展示
created: function () {
this.setData({
input_value: this.propDefaultValue
});
},
methods: {
// 搜索输入事件
search_input_value_event(e) {
this.setData({
input_value: e.detail.value,
});
// 是否回调事件
if (this.propIsOnInputEvent) {
this.$emit('oninput', e.detail.value);
}
},
// 搜索失去焦点事件
search_input_blur_event(e) {
this.setData({
input_value: e.detail.value,
});
// 是否回调事件
if (this.propIsOnBlurEvent) {
this.$emit('onblur', e.detail.value);
}
},
// 搜索获取焦点事件
search_input_focus_event(e) {
this.setData({
input_value: e.detail.value,
});
// 是否回调事件
if (this.propIsOnFocusEvent) {
this.$emit('onfocus', e.detail.value);
}
},
// 搜索确认事件
search_submit_confirm_event() {
// 是否验证必须要传值
var value = this.input_value || this.propPlaceholderValue;
if (this.propIsRequired && value === '') {
app.globalData.showToast(this.$t('search.search.ic9b89'));
return false;
}
// 是否回调事件
if (this.propIsOnEvent) {
this.$emit('onsearch', value);
} else {
// 进入搜索页面
app.globalData.url_open(this.propUrl + '?' + this.propFormName + '=' + value);
}
},
// 搜索确认(外部调用直接跳转搜索)
search_submit_confirm(value = '') {
app.globalData.url_open(this.propUrl + '?' + this.propFormName + '=' + value);
},
// icon事件
search_icon_event() {
// 是否回调事件
if (this.propIsIconOnEvent) {
this.$emit('onicon', {});
}
},
},
};
</script>
<style scoped>
.search-content .search-icon {
z-index: 1;
left: 0;
top: 0;
line-height: 28rpx;
height: 42rpx;
}
.search-content .input {
box-sizing: border-box;
font-size: 24rpx;
padding: 0 32rpx 0 64rpx;
height: 56rpx;
line-height: 56rpx;
background: transparent;
}
.search-content .search-btn {
width: 106rpx;
height: 46rpx;
line-height: 46rpx;
font-size: 28rpx;
border-radius: 30rpx;
padding: 0;
color: #fff;
right: 6rpx;
top: 50%;
transform: translateY(-50%);
z-index: 2;
}
.search-content.sm .search-icon {
padding-top: 18rpx;
}
.search-content.sm .input {
height: 60rpx;
line-height: 60rpx;
font-size: 26rpx;
}
.search-content.sm .search-btn {
height: 50rpx;
line-height: 50rpx;
}
.search-content.md .search-icon {
padding-top: 22rpx;
}
.search-content.md .input {
height: 66rpx;
line-height: 66rpx;
font-size: 28rpx;
}
.search-content.md .search-btn {
height: 56rpx;
line-height: 56rpx;
}
</style>

View File

@@ -0,0 +1,299 @@
<template>
<view :class="theme_view">
<component-popup :propShow="popup_status" propPosition="bottom" @onclose="popup_close_event">
<view class="share-popup bg-white">
<view class="close fr oh">
<view class="fr" @tap.stop="popup_close_event">
<iconfont name="icon-close-o" size="28rpx" color="#999"></iconfont>
</view>
</view>
<view class="share-popup-content">
<!-- #ifdef MP-ALIPAY -->
<view class="share-items oh cp" @tap="share_base_event">
<image class="image" :src="common_static_url + 'share-user-icon.png'" mode="scaleToFill"></image>
<text class="cr-grey text-size-xs single-text">{{ $t('share-popup.share-popup.h04xiy') }}</text>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO || MP-KUAISHOU -->
<view class="share-items oh cp">
<button class="btn dis-block br-0 ht-auto" type="default" size="mini" open-type="share" hover-class="none" @tap="popup_close_event">
<image class="image" :src="common_static_url + 'share-user-icon.png'" mode="scaleToFill"></image>
<text class="cr-grey text-size-xs single-text">{{ $t('share-popup.share-popup.h04xiy') }}</text>
</button>
</view>
<!-- #endif -->
<!-- #ifdef APP -->
<block v-if="is_app_weixin">
<view class="share-items oh cp" data-scene="WXSceneSession" data-provider="weixin" @tap="share_app_event">
<image class="image" :src="common_static_url + 'share-user-icon.png'" mode="scaleToFill"></image>
<text class="cr-grey text-size-xs single-text">{{ $t('share-popup.share-popup.rhs2c5') }}</text>
</view>
<view class="share-items oh cp" data-scene="WXSceneTimeline" data-provider="weixin" @tap="share_app_event">
<image class="image" :src="common_static_url + 'share-friend-icon.png'" mode="scaleToFill"></image>
<text class="cr-grey text-size-xs single-text">{{ $t('share-popup.share-popup.mv9l10') }}</text>
</view>
<view class="share-items oh cp" data-scene="WXSceneFavorite" data-provider="weixin" @tap="share_app_event">
<image class="image" :src="common_static_url + 'share-favor-icon.png'" mode="scaleToFill"></image>
<text class="cr-grey text-size-xs single-text">{{ $t('share-popup.share-popup.f08y38') }}</text>
</view>
</block>
<block v-if="is_app_qq">
<view class="share-items oh cp" data-provider="qq" @tap="share_app_event">
<image class="image":src="common_static_url + 'share-qq-icon.png'" mode="scaleToFill"></image>
<text class="cr-grey text-size-xs single-text">{{ $t('share-popup.share-popup.1242w9') }}</text>
</view>
</block>
<!-- #endif -->
<!-- #ifdef H5 || APP -->
<view class="share-items oh cp" @tap="share_url_copy_event">
<image class="image" :src="common_static_url + 'share-url-icon.png'" mode="scaleToFill"></image>
<text class="cr-grey text-size-xs single-text">{{ $t('share-popup.share-popup.1oh013') }}</text>
</view>
<!-- #endif -->
<view v-if="is_goods_poster == 1 && (goods_id || 0) != 0" class="share-items oh cp" @tap="poster_event">
<image class="image" :src="common_static_url + 'share-poster-icon.png'" mode="scaleToFill"></image>
<text class="cr-grey text-size-xs single-text">{{ $t('share-popup.share-popup.dcp2qu') }}</text>
</view>
</view>
</view>
</component-popup>
<!-- 用户基础 -->
<component-user-base ref="user_base"></component-user-base>
</view>
</template>
<script>
const app = getApp();
var common_static_url = app.globalData.get_static_url('common');
import componentPopup from '@/components/popup/popup';
import componentUserBase from '@/components/user-base/user-base';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
common_static_url: common_static_url,
popup_status: false,
type: null,
is_goods_poster: 0,
goods_id: 0,
url: null,
images: null,
title: null,
summary: null,
is_app_weixin: true,
is_app_qq: true,
share_info: {},
};
},
components: {
componentPopup,
componentUserBase,
},
created: function () {},
methods: {
// 初始配置
init(config = {}) {
if (!app.globalData.is_single_page_check()) {
return false;
}
this.setData({
popup_status: config.status == undefined ? true : config.status,
type: config.type == undefined ? null : config.type,
is_goods_poster: config.is_goods_poster || 0,
goods_id: config.goods_id || 0,
goods_id: config.goods_id || 0,
url: config.url || null,
images: config.images || null,
title: config.title || null,
summary: config.summary || null,
share_info: config.share_info || {},
});
// 用户头像和昵称设置提示
if ((this.$refs.user_base || null) != null) {
this.$refs.user_base.init('share');
}
// #ifdef APP
// app分享通道隔离
uni.getProvider({
service: 'share',
success: (result) => {
var provider = result.provider || [];
this.setData({
is_app_weixin: provider.indexOf('weixin') != -1,
is_app_qq: provider.indexOf('qq') != -1,
});
},
fail: (error) => {},
});
// #endif
},
// 弹层关闭
popup_close_event(e) {
this.setData({
popup_status: false,
});
},
// url链接地址复制分享
share_url_copy_event() {
var url = app.globalData.get_page_url();
// 增加分享标识
if(url.indexOf('referrer') == -1) {
var uid = app.globalData.get_user_cache_info('id') || null;
if(uid != null) {
var join = url.indexOf('?') == -1 ? '?' : '&';
url += join+'referrer='+uid;
}
}
app.globalData.text_copy_event(url);
},
// 基础分享事件
share_base_event() {
this.setData({
popup_status: false,
});
uni.pageScrollTo({
scrollTop: 0,
duration: 300,
complete: (res) => {
setTimeout(function () {
uni.showShareMenu();
}, 500);
},
});
},
// 商品海报分享
poster_event() {
var user = app.globalData.get_user_info(this, 'poster_event');
if (user != false) {
uni.showLoading({
title: this.$t('detail.detail.6xvl35'),
});
uni.request({
url: app.globalData.get_request_url('goodsposter', 'distribution', 'distribution'),
method: 'POST',
data: { goods_id: this.goods_id },
dataType: 'json',
success: (res) => {
uni.hideLoading();
if (res.data.code == 0) {
uni.previewImage({
current: res.data.data,
urls: [res.data.data],
});
} else {
if (app.globalData.is_login_check(res.data, this, 'poster_event')) {
app.globalData.showToast(res.data.msg);
}
}
},
fail: () => {
uni.hideLoading();
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
}
},
// app分享
share_app_event(e) {
// 分享参数
var provider = e.currentTarget.dataset.provider;
var scene = e.currentTarget.dataset.scene || null;
// 分享基础数据
var share = app.globalData.share_content_handle(this.share_info || {});
var img = this.images || share.img;
var url = this.url || share.url;
var title = this.title || share.title;
var summary = this.summary || share.desc;
var type = this.type === null ? ((img || null) == null ? 1 : 0) : this.type;
var miniProgram = {};
// #ifdef APP
// 分享到好友是否走微信小程序则获取微信小程序原始id
if (scene == 'WXSceneSession') {
var weixin_original_id = app.globalData.get_config('config.common_app_mini_weixin_share_original_id') || null;
if (weixin_original_id != null) {
type = 5;
miniProgram = {
id: weixin_original_id,
path: url.split('#')[1],
type: 0,
webUrl: url,
};
}
}
// #endif
// 关闭分享弹窗
this.setData({
popup_status: false,
});
// 调用分享组件
uni.share({
provider: provider,
scene: scene,
type: type,
href: url,
title: title,
summary: summary,
imageUrl: img,
miniProgram: miniProgram,
success: function (res) {},
fail: function (err) {},
});
},
},
};
</script>
<style>
.share-popup {
padding: 20rpx 10rpx 0 10rpx;
position: relative;
}
.share-popup .close {
position: absolute;
top: 0;
right: 0;
z-index: 2;
padding: 20rpx;
}
.share-popup-content {
padding: 0 20rpx;
text-align: left;
}
.share-popup-content .share-items {
padding: 30rpx 0;
min-height: 85rpx;
display: flex;
}
.share-popup-content .share-items:not(:first-child) {
border-top: 1px solid #f0f0f0;
}
.share-popup-content .share-items .btn {
background: transparent;
padding: 0;
width: 100%;
text-align: left;
margin: 0;
}
.share-popup-content .share-items .image {
width: 80rpx;
height: 80rpx;
vertical-align: middle;
margin-right: 20rpx;
}
.share-popup-content .share-items .single-text {
width: calc(100% - 100rpx);
line-height: 85rpx;
}
</style>

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