11 Commits
Author SHA1 Message Date
xuan dc6e2ece54 chore(manifest): 更新应用版本号至1.1.4
- 将 versionName 从1.1.3更新为1.1.4
- 将 versionCode 从103更新为104
2026-01-04 14:16:57 +08:00
xuan e0b00cc62b feat(app): 添加自动检查版本更新功能
- 引入 app_upgrade 模块用于版本检测
- 实现 autoCheckUpgrade 方法自动检查版本更新
- 在 APP-PLUS 平台的 onLoad 生命周期调用自动更新检查
- 调用后端接口获取升级信息并判断是否需要更新
- 支持强制检查新版本,跳过时间戳限制
- 优化日志打印,便于排查版本升级过程中的问题
2026-01-04 14:16:00 +08:00
xuan 9272ed0e23 feat(scanner): 多厂商PDA扫码器兼容方案及扫码功能重构
- 采用策略模式及配置表驱动方式支持多品牌扫码器广播注册
- 实现扫码广播的统一注册和注销管理,支持并发多个扫码广播接收器
- 重构扫码处理逻辑,统一处理扫码结果,支持条码和二维码识别
- 移除原有单一品牌扫码注册,整合商米、新大陆等扫码头支持
- 调整扫码相关页面代码,集成scanMixin,实现扫码广播注册和注销自动化
- 优化扫码结果处理,支持删除模式及整缸扫描状态区分
- 修正多处界面文本格式与代码风格,提高代码一致性和可维护性
- 升级manifest版本号至1.1.3,标记此功能更新
2026-01-04 13:57:28 +08:00
xuan 9e4be6db92 refactor(http): 优化升级检查请求及新增完整URL PUT请求方法
- 将升级检查接口调用改为使用统一的 putJson 方法,简化请求逻辑
- 新增 vm.$u.putJsonFullUrl 方法,支持传入完整URL且带默认JSON请求头
- putJsonFullUrl 方法封装了请求的统一header和响应处理逻辑
- manifest版本号升级至1.1.2 (112)
- sys user setting页面增加升级检查返回日志打印,方便调试
2025-12-30 18:16:08 +08:00
xuan 03024c395a fix(http): 更新接口请求方式及环境配置
- 修改.env文件,切换为zhisheng预发布环境变量
- 将outFpmArrangeOrder接口请求改为使用putJson,支持json格式请求体
- 在http.interceptor.js中新增putJson方法,封装带json请求头的PUT请求接口
- 统一接口请求方式,确保请求头为application/json,提高接口兼容性和稳定性
2025-12-30 17:30:47 +08:00
xuan 7043a49206 feat(login): 优化登录逻辑及环境配置展示
- 修改环境变量配置,新增 VUE_APP_ENV_NAME 用于区分环境名称显示
- 调整登录接口请求,pdaLogin与pdaLogout改为相对路径调用
- 登录页面新增环境名称显示,区分不同环境使用状态
- 登录流程支持开发环境动态选择服务器地址,支持自定义地址输入及存储
- 增加请求和响应拦截器日志输出,方便调试接口请求问题
- 优化登录表单输入清除按钮显示与密码显示切换交互体验
- 修复和改善登录相关本地存储,自动保存账号密码和服务器地址
- 调整manifest包配置,禁用app-plus子包
- 格式化和规范登录页面样式调整,提升页面结构与样式一致性
2025-12-26 15:11:37 +08:00
xuan 8a9bb09c18 fix(http): 修正 PDA 登录登出接口的请求地址
- 将 PDA 登录接口的请求地址修改为使用生产环境 API URL 前缀
- 将 PDA 登出接口的请求地址修改为使用生产环境 API URL 前缀
- 保持接口调用方式不变,确保请求路径正确指向后端服务
2025-12-25 11:39:00 +08:00
xuan f63d202327 build(deps): update dependencies and manifest version
更新了项目依赖,包括:
- `@dcloudio` 相关依赖
- `@icon-park/vue`
- `@vue/shared`
- `core-js`
- `flyio`
- `vue-i18n`
- `vuex`
- `@dcloudio/types`
- `babel-plugin-import`

同时,更新了 `manifest.json` 中的 `versionName` 和 `versionCode`。

这些更新旨在提升项目性能、修复已知问题并引入新功能。
2025-08-18 11:43:03 +08:00
xuan aa186a3f82 feat(config): add environment-specific build and dev scripts with dotenv support
- 添加了针对不同环境(mes、zhisheng)的构建和开发脚本
- 集成 dotenv 支持,通过 .env 文件管理环境变量
- 更新 manifest.json 中的图标和启动页路径,修正版本号
- 重构配置文件以支持动态环境变量注入
- 修改 HTTP API 调用地址为可配置化,便于多环境部署切换
2025-08-16 18:09:06 +08:00
xuan 216d7c6ab4 feat(config): add pack.json for project configuration
- 添加项目打包配置文件,支持 Android 和 iOS 平台参数设置
- 包含证书、渠道包、广告开关等配置项
- 支持自定义基座和安心打包模式
2025-08-16 16:15:17 +08:00
xuan cfc3a24e6e refactor(uview-ui): remove unused components and libraries
Removed multiple unused uview-ui components and utility files to reduce bundle size and improve maintainability. This includes deletion of action sheet, alert tips, avatar cropper, badge, button, calendar, card, checkbox, collapse, count down, divider, dropdown, form, grid, icon, image, index list, input, keyboard, lazy load, line, link, loading, mask, modal, navbar, notice bar, number box, parse, picker, popup, radio, rate, read more, row, search, section, select, skeleton, slider, steps, sticky, subsection, swipe action, swiper, switch, tabbar, table, tabs, tag, time line, toast, top tips, upload, verification code, waterfall components and associated utilities.

Also removed redundant CSS and JS files related to uview-ui styling and functionality.
2025-08-16 16:15:02 +08:00
194 changed files with 2925 additions and 31725 deletions
+5
View File
@@ -0,0 +1,5 @@
NODE_ENV = development
VUE_APP_ENV_NAME = zhisheng环境
VUE_APP_UPGRADE_NAME = erp_pda_pre
VUE_APP_PRODUCTION_API_URL = https://hcscmpre.zzfzyc.com/hcscm/pda/v1
VUE_APP_DEV_API_URL = https://hcscmpre.zzfzyc.com/hcscm/pda/v1
+5
View File
@@ -0,0 +1,5 @@
NODE_ENV = development
VUE_APP_ENV_NAME = mes环境
VUE_APP_UPGRADE_NAME = erp_pda
VUE_APP_PRODUCTION_API_URL = https://www.haotopai.com/hcscm/mes/server/pda/v1
VUE_APP_DEV_API_URL = https://www.haotopai.com/hcscm/mes/server/pda/v1
+5
View File
@@ -0,0 +1,5 @@
NODE_ENV = development
VUE_APP_ENV_NAME = zhisheng环境
VUE_APP_UPGRADE_NAME = erp_pda_pre
VUE_APP_PRODUCTION_API_URL = https://hcscmpre.zzfzyc.com/hcscm/pda/v1
VUE_APP_DEV_API_URL = https://hcscmpre.zzfzyc.com/hcscm/pda/v1
+69
View File
@@ -1,3 +1,5 @@
# 不使用 pda项目,以后的新需求或者修改 使用 本项目
# pda-cli # pda-cli
## Project setup ## Project setup
@@ -17,3 +19,70 @@ yarn build
### Customize configuration ### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/). See [Configuration Reference](https://cli.vuejs.org/config/).
### 本地电脑window 使用命令行云打包:
cli命令需要配置环境变量:[cli配置环境变量](https://hx.dcloud.net.cn/cli/README)
cli云打包使用:
```
cli pack --config D:/work_xuan/pda-cli/pack.json
```
pack.json内容如下:详情可访问 [CLI uni-app发行 - Android/iOS云打包](https://hx.dcloud.net.cn/cli/pack) 了解更多
以下的证书信息是我本地电脑的本地证书:生成证书可参考 [证书生成](https://ask.dcloud.net.cn/article/35777)
本地使用Android Studio 离线打包教程如下: [Android 打包发行](https://nativesupport.dcloud.net.cn/AppDocs/package/android.html)
```
{
//项目名字或项目绝对路径
"project": "pda",
//打包平台 默认值android 值有"android","ios" 如果要打多个逗号隔开打包平台
"platform":"android",
//是否使用自定义基座 默认值false true自定义基座 false自定义证书
"iscustom": false,
//打包方式是否为安心打包默认值false,true安心打包,false传统打包
"safemode": false,
//android打包参数
"android": {
//安卓包名
"packagename":"uni.UNI_F79F300",
//安卓打包类型 默认值0 0 使用自有证书 1 使用公共证书 2 使用老版证书 3 使用云端证书
"androidpacktype":"0",
//安卓使用自有证书自有打包证书参数
//安卓打包证书别名,自有证书打包填写的参数
"certalias":"testalias",
//安卓打包证书文件路径,自有证书打包填写的参数
"certfile":"C:\\Users\\13597\\test.keystore",
//安卓打包证书密码,自有证书打包填写的参数
"certpassword":"123456",
//安卓打包证书库密码(HBuilderx4.41支持),自有证书打包填写的参数
"storePassword": "123456",
//安卓平台要打的渠道包 取值有"google","yyb","360","huawei","xiaomi","oppo","vivo",如果要打多个逗号隔开
"channels":""
},
//ios打包参数
"ios": {
//ios appid
"bundle":"com.test.ios",
//ios打包支持的设备类型 默认值iPhone 值有"iPhone","iPad" 如果要打多个逗号隔开打包平台
"supporteddevice":"iPhone,iPad",
//iOS使用自定义证书打包的profile文件路径
"profile":"",
//iOS使用自定义证书打包的p12文件路径
"certfile":"",
//iOS使用自定义证书打包的证书密码
"certpassword":"123"
},
//是否混淆 true混淆 false关闭
"isconfusion":false,
//开屏广告 true打开 false关闭
"splashads":false,
//悬浮红包广告true打开 false关闭
"rpads":false,
//push广告 true打开 false关闭
"pushads":false,
//加入换量联盟 true加入 false不加入
"exchange":false
}
```
### 云服务器走CICD 打包:
需要下载 [Linux HBuilderX Cli](https://ask.dcloud.net.cn/article/41469)
+51
View File
@@ -0,0 +1,51 @@
{
//项目名字或项目绝对路径
"project": "pda",
//打包平台 默认值android 值有"android","ios" 如果要打多个逗号隔开打包平台
"platform":"android",
//是否使用自定义基座 默认值false true自定义基座 false自定义证书
"iscustom": false,
//打包方式是否为安心打包默认值false,true安心打包,false传统打包
"safemode": false,
//android打包参数
"android": {
//安卓包名
"packagename":"uni.UNI_F79F300",
//安卓打包类型 默认值0 0 使用自有证书 1 使用公共证书 2 使用老版证书 3 使用云端证书
"androidpacktype":"0",
//安卓使用自有证书自有打包证书参数
//安卓打包证书别名,自有证书打包填写的参数
"certalias":"testalias",
//安卓打包证书文件路径,自有证书打包填写的参数
"certfile":"C:\\Users\\13597\\test.keystore",
//安卓打包证书密码,自有证书打包填写的参数
"certpassword":"123456",
//安卓打包证书库密码(HBuilderx4.41支持),自有证书打包填写的参数
"storePassword": "123456",
//安卓平台要打的渠道包 取值有"google","yyb","360","huawei","xiaomi","oppo","vivo",如果要打多个逗号隔开
"channels":""
},
//ios打包参数
"ios": {
//ios appid
"bundle":"com.test.ios",
//ios打包支持的设备类型 默认值iPhone 值有"iPhone","iPad" 如果要打多个逗号隔开打包平台
"supporteddevice":"iPhone,iPad",
//iOS使用自定义证书打包的profile文件路径
"profile":"",
//iOS使用自定义证书打包的p12文件路径
"certfile":"",
//iOS使用自定义证书打包的证书密码
"certpassword":"123"
},
//是否混淆 true混淆 false关闭
"isconfusion":false,
//开屏广告 true打开 false关闭
"splashads":false,
//悬浮红包广告true打开 false关闭
"rpads":false,
//push广告 true打开 false关闭
"pushads":false,
//加入换量联盟 true加入 false不加入
"exchange":false
}
+53 -45
View File
@@ -7,6 +7,8 @@
"serve": "npm run dev:h5", "serve": "npm run dev:h5",
"build": "npm run build:h5", "build": "npm run build:h5",
"build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build", "build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build",
"build:app-plus:zhisheng": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build --mode zhisheng",
"build:app-plus:mes": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build --mode mes",
"build:custom": "cross-env NODE_ENV=production uniapp-cli custom", "build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
"build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build", "build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build",
"build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build", "build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build",
@@ -25,6 +27,8 @@
"build:quickapp-webview-huawei": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build", "build:quickapp-webview-huawei": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build",
"build:quickapp-webview-union": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build", "build:quickapp-webview-union": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build",
"dev:app-plus": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch", "dev:app-plus": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch",
"dev:app-plus:zhisheng": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch --mode zhisheng",
"dev:app-plus:mes": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch --mode mes",
"dev:custom": "cross-env NODE_ENV=development uniapp-cli custom", "dev:custom": "cross-env NODE_ENV=development uniapp-cli custom",
"dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve", "dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve",
"dev:mp-360": "cross-env NODE_ENV=development UNI_PLATFORM=mp-360 vue-cli-service uni-build --watch", "dev:mp-360": "cross-env NODE_ENV=development UNI_PLATFORM=mp-360 vue-cli-service uni-build --watch",
@@ -55,55 +59,59 @@
"浩拓移动端" "浩拓移动端"
], ],
"dependencies": { "dependencies": {
"@dcloudio/uni-app": "^2.0.2-4070520250711001", "@dcloudio/uni-app": "2.0.2-4070520250711001",
"@dcloudio/uni-app-plus": "^2.0.2-4070520250711001", "@dcloudio/uni-app-plus": "2.0.2-4070520250711001",
"@dcloudio/uni-h5": "^2.0.2-4070520250711001", "@dcloudio/uni-h5": "2.0.2-4070520250711001",
"@dcloudio/uni-i18n": "^2.0.2-4070520250711001", "@dcloudio/uni-i18n": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-360": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-360": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-alipay": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-alipay": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-baidu": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-baidu": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-harmony": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-harmony": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-jd": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-jd": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-kuaishou": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-kuaishou": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-lark": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-lark": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-qq": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-qq": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-toutiao": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-toutiao": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-vue": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-vue": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-weixin": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-weixin": "2.0.2-4070520250711001",
"@dcloudio/uni-mp-xhs": "^2.0.2-4070520250711001", "@dcloudio/uni-mp-xhs": "2.0.2-4070520250711001",
"@dcloudio/uni-quickapp-native": "^2.0.2-4070520250711001", "@dcloudio/uni-quickapp-native": "2.0.2-4070520250711001",
"@dcloudio/uni-quickapp-webview": "^2.0.2-4070520250711001", "@dcloudio/uni-quickapp-webview": "2.0.2-4070520250711001",
"@dcloudio/uni-stacktracey": "^2.0.2-4070520250711001", "@dcloudio/uni-stacktracey": "2.0.2-4070520250711001",
"@dcloudio/uni-stat": "^2.0.2-4070520250711001", "@dcloudio/uni-stat": "2.0.2-4070520250711001",
"@vue/shared": "^3.0.0", "@icon-park/vue": "^1.4.2",
"core-js": "^3.8.3", "@vue/shared": "^3.5.18",
"flyio": "^0.6.2", "core-js": "^3.45.0",
"vue": ">= 2.6.14 < 2.7",
"vuex": "^3.2.0",
"@icon-park/vue": "^1.3.5",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"vue-i18n": "^8.20.0" "dotenv": "^17.2.1",
"flyio": "^0.6.14",
"uview-ui": "1.8.8",
"vue": "^2.6.14",
"vue-i18n": "^8.28.2",
"vuex": "^3.6.2"
}, },
"devDependencies": { "devDependencies": {
"@dcloudio/types": "^3.3.2", "@dcloudio/types": "^3.4.19",
"@dcloudio/uni-automator": "^2.0.2-4070520250711001", "@dcloudio/uni-automator": "2.0.2-4070520250711001",
"@dcloudio/uni-cli-i18n": "^2.0.2-4070520250711001", "@dcloudio/uni-cli-i18n": "2.0.2-4070520250711001",
"@dcloudio/uni-cli-shared": "^2.0.2-4070520250711001", "@dcloudio/uni-cli-shared": "2.0.2-4070520250711001",
"@dcloudio/uni-helper-json": "*", "@dcloudio/uni-helper-json": "^1.0.13",
"@dcloudio/uni-migration": "^2.0.2-4070520250711001", "@dcloudio/uni-migration": "2.0.2-4070520250711001",
"@dcloudio/uni-template-compiler": "^2.0.2-4070520250711001", "@dcloudio/uni-template-compiler": "2.0.2-4070520250711001",
"@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.2-4070520250711001", "@dcloudio/vue-cli-plugin-hbuilderx": "2.0.2-4070520250711001",
"@dcloudio/vue-cli-plugin-uni": "^2.0.2-4070520250711001", "@dcloudio/vue-cli-plugin-uni": "2.0.2-4070520250711001",
"@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.2-4070520250711001", "@dcloudio/vue-cli-plugin-uni-optimize": "2.0.2-4070520250711001",
"@dcloudio/webpack-uni-mp-loader": "^2.0.2-4070520250711001", "@dcloudio/webpack-uni-mp-loader": "2.0.2-4070520250711001",
"@dcloudio/webpack-uni-pages-loader": "^2.0.2-4070520250711001", "@dcloudio/webpack-uni-pages-loader": "2.0.2-4070520250711001",
"@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-service": "~5.0.0", "@vue/cli-service": "~5.0.8",
"babel-plugin-import": "^1.11.0", "babel-plugin-import": "^1.13.8",
"cross-env": "^7.0.2", "cross-env": "^7.0.3",
"jest": "^25.4.0", "jest": "^25.5.4",
"postcss-comment": "^2.0.0", "postcss-comment": "^2.0.0",
"vue-template-compiler": ">= 2.6.14 < 2.7" "sass": "1.26.2",
"sass-loader": "8.0.2",
"vue-template-compiler": "^2.6.14"
}, },
"browserslist": [ "browserslist": [
"Android >= 4.4", "Android >= 4.4",
+1 -1
View File
@@ -2,7 +2,7 @@
<view> <view>
<topDropdown :isSxShow="true" :dropdown1="dropdown1" dropdown2="上架中" :options1="options1" :options2="isSjList" :optionsSx="sxList" searchUrl="./searchAdmin" @dropDownFun1="dropDownFun1" @dropDownFun2="dropDownFun2" @optionSxFun="optionSxFun"> <topDropdown :isSxShow="true" :dropdown1="dropdown1" dropdown2="上架中" :options1="options1" :options2="isSjList" :optionsSx="sxList" searchUrl="./searchAdmin" @dropDownFun1="dropDownFun1" @dropDownFun2="dropDownFun2" @optionSxFun="optionSxFun">
</topDropdown> </topDropdown>
<dataNull v-if="list.length == 0" src="/static/img/chahua/dataNullXz.png" title="暂无相关产品" <dataNull v-if="list.length == 0" src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品"
title1="请添加或者更换查询条件"></dataNull> title1="请添加或者更换查询条件"></dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"
+1 -1
View File
@@ -1,6 +1,6 @@
<template> <template>
<view> <view>
<dataNull v-if="list.length == 0" src="/static/img/chahua/dataNullXz.png" title="暂无相关分类" <dataNull v-if="list.length == 0" src="/src/static/img/chahua/dataNullXz.png" title="暂无相关分类"
title1="点击添加按钮添加相关分类"></dataNull> title1="点击添加按钮添加相关分类"></dataNull>
<view v-else class="content"> <view v-else class="content">
<view class="row" v-for="(row, index) in list" :key="index"> <view class="row" v-for="(row, index) in list" :key="index">
+1 -1
View File
@@ -1,7 +1,7 @@
<template> <template>
<view> <view>
<searchView placeholderStr="请输入产品名称" @searchViewClickFun="searchEventsFun($event)"></searchView> <searchView placeholderStr="请输入产品名称" @searchViewClickFun="searchEventsFun($event)"></searchView>
<dataNull v-if="list.length == 0" src="/static/img/chahua/dataNullXz.png" title="暂无相关产品" <dataNull v-if="list.length == 0" src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品"
title1="请更换查询条件"></dataNull> title1="请更换查询条件"></dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"
+1 -1
View File
@@ -1,7 +1,7 @@
<template> <template>
<view> <view>
<searchView placeholderStr="请输入产品名称" @searchViewClickFun="searchEventsFun($event)"></searchView> <searchView placeholderStr="请输入产品名称" @searchViewClickFun="searchEventsFun($event)"></searchView>
<dataNull v-if="list.length == 0" src="/static/img/chahua/dataNullXz.png" title="暂无相关产品" <dataNull v-if="list.length == 0" src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品"
title1="请添加或者更换查询条件"></dataNull> title1="请添加或者更换查询条件"></dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"
+2 -2
View File
@@ -81,12 +81,12 @@
<getMore :isMore="tabBars[tabCurrentIndex].isMore" style="margin-bottom: 16px;"></getMore> <getMore :isMore="tabBars[tabCurrentIndex].isMore" style="margin-bottom: 16px;"></getMore>
<view style="height: 36rpx;"></view> <view style="height: 36rpx;"></view>
</view> </view>
<dataNull v-else src="/static/img/chahua/dataNullXz.png" title="暂无相关产品" title1="请先添加"> <dataNull v-else src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品" title1="请先添加">
</dataNull> </dataNull>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<dataNull v-else src="/static/img/chahua/dataNullXz.png" title="暂无相关产品"> <dataNull v-else src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品">
</dataNull> </dataNull>
</view> </view>
</view> </view>
+3 -9
View File
@@ -6,7 +6,7 @@
export const isDevelopment = process.env.NODE_ENV === 'development' export const isDevelopment = process.env.NODE_ENV === 'development'
const config = { const config = {
upgradeName: 'erp_pda_pre', upgradeName: process.env.VUE_APP_UPGRADE_NAME || 'erp_pda',
// 产品名称 // 产品名称
productName: '浩拓技术', productName: '浩拓技术',
@@ -17,22 +17,16 @@ const config = {
// 版本检查标识 // 版本检查标识
appCode: 'android', appCode: 'android',
// 内部版本号码
appVersion: 1,
// 管理基础路径
adminPath: 'https://hcscmpre.zzfzyc.com/hcscm/pda/v1',
// 环境判断 // 环境判断
isDevelopment: isDevelopment, isDevelopment: isDevelopment,
// 生产环境API URL // 生产环境API URL
productionApiUrl: 'https://hcscmpre.zzfzyc.com/hcscm/pda/v1', productionApiUrl: process.env.VUE_APP_PRODUCTION_API_URL || 'https://www.haotopai.com/hcscm/mes/server/pda/v1',
// 开发环境API URL选项 // 开发环境API URL选项
devApiUrlOptions: [ devApiUrlOptions: [
{ label: '测试环境', value: 'https://hcscmtest.zzfzyc.com/hcscm/pda/v1' }, { label: '测试环境', value: 'https://hcscmtest.zzfzyc.com/hcscm/pda/v1' },
{ label: '正式环境', value: 'https://hcscmpre.zzfzyc.com/hcscm/pda/v1' }, { label: '正式环境', value: process.env.VUE_APP_DEV_API_URL || 'https://www.haotopai.com/hcscm/mes/server/pda/v1' },
{ label: '自定义', value: 'custom' } { label: '自定义', value: 'custom' }
] ]
+25 -27
View File
@@ -12,9 +12,9 @@ const install = (Vue, vm) => {
// 基础服务:登录登出、身份信息、菜单授权、切换系统、字典数据等 // 基础服务:登录登出、身份信息、菜单授权、切换系统、字典数据等
lang: (params = {}) => vm.$u.get('/lang/'+params.lang), lang: (params = {}) => vm.$u.get('/lang/'+params.lang),
index: (params = {}) => vm.$u.get(config.adminPath+'/mobile/index', params), index: (params = {}) => vm.$u.get(config.productionApiUrl+'/mobile/index', params),
getUserInfo: (params = {}) => vm.$u.get(config.adminPath+'/mobile/user/getUserInfo', params), getUserInfo: (params = {}) => vm.$u.get(config.productionApiUrl+'/mobile/user/getUserInfo', params),
login: (params = {}) => vm.$u.post(config.adminPath+'/mobile/login/loginByPassword', params), login: (params = {}) => vm.$u.post(config.productionApiUrl+'/mobile/login/loginByPassword', params),
// PDA登录接口 // PDA登录接口
pdaLogin: (params = {}) => vm.$u.postJson('/login', params), pdaLogin: (params = {}) => vm.$u.postJson('/login', params),
pdaLogout: (params = {}) => vm.$u.postJson('/logout', params), pdaLogout: (params = {}) => vm.$u.postJson('/logout', params),
@@ -23,19 +23,19 @@ const install = (Vue, vm) => {
// 获取成品配布单详情 // 获取成品配布单详情
getFpmArrangeOrder: (params = {}) => vm.$u.get('/product/fpmArrangeOrder/getFpmArrangeOrder', params), getFpmArrangeOrder: (params = {}) => vm.$u.get('/product/fpmArrangeOrder/getFpmArrangeOrder', params),
// 生成出仓单 // 生成出仓单
outFpmArrangeOrder: (params = {}) => vm.$u.put('/product/fpmArrangeOrder/outFpmArrangeOrder', params), outFpmArrangeOrder: (params = {}) => vm.$u.putJson('/product/fpmArrangeOrder/outFpmArrangeOrder', params),
sendCode: (params = {}) => vm.$u.post(config.adminPath+'/mobile/login/sendCode', params), sendCode: (params = {}) => vm.$u.post(config.productionApiUrl+'/mobile/login/sendCode', params),
registerUser: (params = {}) => vm.$u.post(config.adminPath+'/mobile/user/registerUser', params), registerUser: (params = {}) => vm.$u.post(config.productionApiUrl+'/mobile/user/registerUser', params),
//首页相关api //首页相关api
getIndexCardInfo: (params = {}) => vm.$u.get(config.adminPath+'/mobile/index/getIndexCardInfo', params), getIndexCardInfo: (params = {}) => vm.$u.get(config.productionApiUrl+'/mobile/index/getIndexCardInfo', params),
getM2mOrderFlowList: (params = {}) => vm.$u.get(config.adminPath+'/mobile/index/getM2mOrderFlowList', params), getM2mOrderFlowList: (params = {}) => vm.$u.get(config.productionApiUrl+'/mobile/index/getM2mOrderFlowList', params),
//获取卡可购买套餐包 //获取卡可购买套餐包
getM2mOrderPackageList: (params = {}) => vm.$u.get(config.adminPath+'/mobile/index/getM2mOrderPackageList', params), getM2mOrderPackageList: (params = {}) => vm.$u.get(config.productionApiUrl+'/mobile/index/getM2mOrderPackageList', params),
logout: (params = {}) => vm.$u.get(config.adminPath+'/mobile/login/logout', params), logout: (params = {}) => vm.$u.get(config.productionApiUrl+'/mobile/login/logout', params),
authInfo: (params = {}) => vm.$u.get(config.adminPath+'/authInfo', params), authInfo: (params = {}) => vm.$u.get(config.productionApiUrl+'/authInfo', params),
menuTree: (params = {}) => vm.$u.get(config.adminPath+'/menuTree', params), menuTree: (params = {}) => vm.$u.get(config.productionApiUrl+'/menuTree', params),
switchSys: (params = {}) => vm.$u.get(config.adminPath+'/switch/'+params.sysCode), switchSys: (params = {}) => vm.$u.get(config.productionApiUrl+'/switch/'+params.sysCode),
dictData: (params = {}) => vm.$u.get(config.a取dminPath+'/system/dict/data/type/'+params.dictType), dictData: (params = {}) => vm.$u.get(config.a取dminPath+'/system/dict/data/type/'+params.dictType),
// 账号服务:验证码接口、忘记密码接口、注册账号接口等 // 账号服务:验证码接口、忘记密码接口、注册账号接口等
@@ -46,36 +46,34 @@ const install = (Vue, vm) => {
saveRegByValidCode: (params = {}) => vm.$u.post('/account/saveRegByValidCode', params), saveRegByValidCode: (params = {}) => vm.$u.post('/account/saveRegByValidCode', params),
// APP公共服务 // APP公共服务
upgradeCheck: (params = {}) => vm.$u.put(vm.$u.http.config.baseUrl.replace('/pda/v1', '') + '/version/check', params, { upgradeCheck: (params = {}) => vm.$u.putJson('/version/check', params),
'content-type': 'application/json'
}),
commentSave: (params = {}) => vm.$u.post('/app/comment/save', params), commentSave: (params = {}) => vm.$u.post('/app/comment/save', params),
// 个人信息修改 // 个人信息修改
user: { user: {
saveUserInfo: (params = {}) => vm.$u.post(config.adminPath+'/mobile/user/saveUserInfo', params), saveUserInfo: (params = {}) => vm.$u.post(config.productionApiUrl+'/mobile/user/saveUserInfo', params),
infoSavePwd: (params = {}) => vm.$u.put(config.adminPath+'/system/user/profile/updatePwd', params), infoSavePwd: (params = {}) => vm.$u.put(config.productionApiUrl+'/system/user/profile/updatePwd', params),
infoSavePqa: (params = {}) => vm.$u.post(config.adminPath+'/sys/user/infoSavePqa', params), infoSavePqa: (params = {}) => vm.$u.post(config.productionApiUrl+'/sys/user/infoSavePqa', params),
}, },
// 员工用户查询 // 员工用户查询
empUser: { empUser: {
listData: (params = {}) => vm.$u.get(config.adminPath+'/sys/empUser/listData', params), listData: (params = {}) => vm.$u.get(config.productionApiUrl+'/sys/empUser/listData', params),
}, },
// 组织机构查询 // 组织机构查询
office: { office: {
treeData: (params = {}) => vm.$u.get(config.adminPath+'/sys/office/treeData', params), treeData: (params = {}) => vm.$u.get(config.productionApiUrl+'/sys/office/treeData', params),
}, },
// 增删改查例子 // 增删改查例子
testData: { testData: {
form: (params = {}) => vm.$u.post(config.adminPath+'/test/testData/form', params), form: (params = {}) => vm.$u.post(config.productionApiUrl+'/test/testData/form', params),
list: (params = {}) => vm.$u.post(config.adminPath+'/test/testData/listData', params), list: (params = {}) => vm.$u.post(config.productionApiUrl+'/test/testData/listData', params),
save: (params = {}) => vm.$u.postJson(config.adminPath+'/test/testData/save', params), save: (params = {}) => vm.$u.postJson(config.productionApiUrl+'/test/testData/save', params),
disable: (params = {}) => vm.$u.post(config.adminPath+'/test/testData/disable', params), disable: (params = {}) => vm.$u.post(config.productionApiUrl+'/test/testData/disable', params),
enable: (params = {}) => vm.$u.post(config.adminPath+'/test/testData/enable', params), enable: (params = {}) => vm.$u.post(config.productionApiUrl+'/test/testData/enable', params),
delete: (params = {}) => vm.$u.post(config.adminPath+'/test/testData/delete', params), delete: (params = {}) => vm.$u.post(config.productionApiUrl+'/test/testData/delete', params),
}, },
}; };
+47 -2
View File
@@ -22,6 +22,9 @@ const install = (Vue, vm) => {
// 请求拦截,配置Token等参数 // 请求拦截,配置Token等参数
Vue.prototype.$u.http.interceptor.request = (req) => { Vue.prototype.$u.http.interceptor.request = (req) => {
console.log('request', req);
console.log('baseUrl', vm.$store.state.apiurl);
console.log('完整URL', vm.$store.state.apiurl + req.url);
if (!req.header){ if (!req.header){
req.header = []; req.header = [];
} }
@@ -42,13 +45,15 @@ const install = (Vue, vm) => {
req.header[rememberMeHeader] = vm.vuex_remember; req.header[rememberMeHeader] = vm.vuex_remember;
req.remember = false; req.remember = false;
} }
console.log('request', req);
return req; return req;
} }
// 响应拦截,判断状态码是否通过 // 响应拦截,判断状态码是否通过
Vue.prototype.$u.http.interceptor.response = async (res, req) => { Vue.prototype.$u.http.interceptor.response = async (res, req) => {
console.log('response', res); console.log('response', res);
console.log('response statusCode', res?.statusCode);
console.log('response data', res?.data);
console.log('response errMsg', res?.errMsg);
// 处理401未授权状态码 // 处理401未授权状态码
if (res.statusCode === 401) { if (res.statusCode === 401) {
@@ -63,6 +68,7 @@ const install = (Vue, vm) => {
}); });
return false; return false;
} }
console.log('res',res)
if (!(res.data)){ if (!(res.data)){
vm.$u.toast('未连接到服务器') vm.$u.toast('未连接到服务器')
return Promise.reject(new Error('未连接到服务器')); return Promise.reject(new Error('未连接到服务器'));
@@ -125,7 +131,7 @@ const install = (Vue, vm) => {
// 封装 post json 请求 // 封装 post json 请求
vm.$u.postJson = (url, data = {}, header = {}) => { vm.$u.postJson = (url, data = {}, header = {}) => {
console.log('header', header, url, vm.$u) console.log('header', header, url, vm.$store.state.apiurl)
header['content-type'] = 'application/json'; header['content-type'] = 'application/json';
return vm.$u.http.request({ return vm.$u.http.request({
url, url,
@@ -134,7 +140,46 @@ const install = (Vue, vm) => {
data data
}) })
} }
// 封装 post json 请求
vm.$u.putJson = (url, data = {}, header = {}) => {
console.log('header', header, url, vm.$u)
header['content-type'] = 'application/json';
return vm.$u.http.request({
url,
method: 'PUT',
header,
data
})
}
// 封装不使用 baseUrl 的请求(用于完整URL)
vm.$u.putJsonFullUrl = (url, data = {}, header = {}) => {
header['content-type'] = 'application/json';
return new Promise((resolve, reject) => {
uni.request({
url,
method: 'PUT',
header: {
...header,
'Platform': 2,
'x-requested-with': 'XMLHttpRequest',
'Authorization': vm.vuex_token || ''
},
data,
success: (res) => {
console.log('res',res)
if (res.data && res.data.code === 0) {
resolve(res.data.data);
} else {
reject(res.data);
}
},
fail: (err) => {
reject(err);
}
});
});
}
} }
export default { export default {
+177 -133
View File
@@ -1,143 +1,175 @@
// common/scanMixin.js // common/scanMixin.js
// 多厂商PDA扫码器兼容方案 - 策略模式 + 配置表驱动
export default { export default {
data() { data() {
return { return {
scanReceiver: null, scanReceivers: [], // 支持多个接收器
isPageActive: false isPageActive: false,
registeredBrands: [] // 已注册的品牌
} }
}, },
methods: { methods: {
// 注册扫码广播(兼容多种设备) // 扫码器配置表 - 方便扩展新厂商
registerScanBroadcast(scanCallback) { getScannerConfigs() {
try { return [
// 先尝试注册商米扫码头 {
this.registerSunmiBroadcast(scanCallback) brand: 'newland', // 东集
.then(() => { name: '东集',
console.log('商米扫码头注册成功'); action: 'com.android.server.scannerservice.broadcast',
}) dataKey: 'scannerdata',
.catch((error) => { needSetup: true,
console.log('商米扫码头注册失败,尝试新大陆扫码头:', error); setupAction: 'com.android.scanner.service_settings',
// 如果商米扫码头注册失败,尝试新大陆扫码头 setupParams: {
this.registerNewlandBroadcast(scanCallback); action_barcode_broadcast: 'com.android.server.scannerservice.broadcast',
}); key_barcode_broadcast: 'scannerdata',
} catch (error) { endchar: 'ENTER'
console.error("注册扫码广播失败:", error); }
return false; },
} {
brand: 'sunmi', // 商米
name: '商米',
action: 'com.sunmi.scanner.ACTION_DATA_CODE_RECEIVED',
dataKey: 'data',
needSetup: false
},
// {
// brand: 'honeywell', // 霍尼韦尔
// name: '霍尼韦尔',
// action: 'com.honeywell.decode.intent.action.EDIT_DATA',
// dataKey: 'data',
// needSetup: false
// },
// {
// brand: 'zebra', // 斑马
// name: '斑马',
// action: 'com.symbol.datawedge.api.RESULT_ACTION',
// dataKey: 'com.symbol.datawedge.data_string',
// needSetup: false
// },
// {
// brand: 'idata', // 盈达聚力
// name: '盈达聚力',
// action: 'android.intent.action.SCANRESULT',
// dataKey: 'value',
// needSetup: false
// },
// {
// brand: 'urovo', // 优博讯
// name: '优博讯',
// action: 'android.intent.ACTION_DECODE_DATA',
// dataKey: 'barcode_string',
// needSetup: false
// }
];
}, },
// 注册商米扫码头广播接收器 // 注册所有扫码广播(推荐方式:同时注册所有厂商)
registerSunmiBroadcast(scanCallback) { registerScanBroadcast(scanCallback) {
return new Promise((resolve, reject) => { // #ifdef APP-PLUS
const configs = this.getScannerConfigs();
configs.forEach(config => {
try { try {
const main = plus.android.runtimeMainActivity(); this.registerSingleBroadcast(config, scanCallback);
const IntentFilter = plus.android.importClass("android.content.IntentFilter"); this.registeredBrands.push(config.brand);
console.log(`${config.name}扫码广播注册成功`);
const filter = new IntentFilter();
filter.addAction("com.sunmi.scanner.ACTION_DATA_CODE_RECEIVED");
const self = this;
const receiver = plus.android.implements(
"io.dcloud.feature.internal.reflect.BroadcastReceiver",
{
onReceive: (context, intent) => {
console.log('商米扫码广播接收,isPageActive:', self.isPageActive);
// 只有当页面活动时才处理广播
if (!self.isPageActive) return;
try {
// 导入 Intent 类以使用其方法
const Intent = plus.android.importClass("android.content.Intent");
const scanResult = intent.getStringExtra("data");
const sourceBytes = intent.getByteArrayExtra("source_byte");
console.log('商米扫码结果:', scanResult, sourceBytes);
if (scanResult) {
self.handleScanResult(scanResult, scanCallback);
}
} catch (error) {
console.error('处理商米广播数据时出错:', error);
}
}
}
);
// 注册广播接收器
main.registerReceiver(receiver, filter);
this.scanReceiver = receiver;
console.log('商米扫码广播接收器注册成功');
resolve();
} catch (error) { } catch (error) {
console.error('注册商米广播接收器失败:', error); console.log(`${config.name}扫码广播注册失败:`, error);
reject(error);
} }
}); });
if (this.registeredBrands.length > 0) {
console.log('扫码广播注册完成,已注册厂商:', this.registeredBrands.join(', '));
return true;
}
console.error('所有扫码广播注册均失败');
return false;
// #endif
}, },
// 注册新大陆扫码头广播接收器 // 注册指定厂商的扫码广播
registerNewlandBroadcast(scanCallback) { registerScanBroadcastByBrand(brand, scanCallback) {
try { // #ifdef APP-PLUS
const main = plus.android.runtimeMainActivity(); const configs = this.getScannerConfigs();
const config = configs.find(c => c.brand === brand);
// 先配置扫码枪广播设置
try { if (!config) {
const Intent = plus.android.importClass("android.content.Intent"); console.error(`未找到厂商配置: ${brand}`);
const intent = new Intent("com.android.scanner.service_settings"); return false;
intent.putExtra(
"action_barcode_broadcast",
"com.android.server.scannerservice.broadcast"
);
intent.putExtra("key_barcode_broadcast", "scannerdata");
main.sendBroadcast(intent);
console.log('新大陆扫码枪广播配置完成');
} catch (error) {
console.error("配置新大陆扫码枪广播失败:", error);
}
// 注册广播接收器
const IntentFilter = plus.android.importClass(
"android.content.IntentFilter"
);
const filter = new IntentFilter();
filter.addAction("com.android.server.scannerservice.broadcast");
console.log("添加新大陆广播action完成");
const self = this;
const receiver = plus.android.implements(
"io.dcloud.feature.internal.reflect.BroadcastReceiver",
{
onReceive: (context, intent) => {
console.log('新大陆扫码广播接收,isPageActive:', self.isPageActive);
// 只有当页面活动时才处理广播
if (!self.isPageActive) return;
try {
// 导入 Intent 类以使用其方法
const Intent = plus.android.importClass("android.content.Intent");
const scanResult = intent.getStringExtra("scannerdata");
console.log("新大陆扫码结果:", scanResult);
if (scanResult) {
self.handleScanResult(scanResult, scanCallback);
}
} catch (error) {
console.error("处理新大陆广播数据时出错:", error);
}
},
}
);
// 注册广播接收器
main.registerReceiver(receiver, filter);
this.scanReceiver = receiver;
console.log("新大陆扫码广播注册成功,等待扫码...");
} catch (error) {
console.error("注册新大陆扫码广播失败:", error);
throw error;
} }
try {
this.registerSingleBroadcast(config, scanCallback);
this.registeredBrands.push(config.brand);
console.log(`${config.name}扫码广播注册成功`);
return true;
} catch (error) {
console.error(`${config.name}扫码广播注册失败:`, error);
return false;
}
// #endif
},
// 单个广播注册的核心方法
registerSingleBroadcast(config, scanCallback) {
const main = plus.android.runtimeMainActivity();
// 如果需要预配置(如东集)
if (config.needSetup && config.setupAction) {
try {
const Intent = plus.android.importClass('android.content.Intent');
const setupIntent = new Intent(config.setupAction);
Object.keys(config.setupParams || {}).forEach(key => {
setupIntent.putExtra(key, config.setupParams[key]);
});
main.sendBroadcast(setupIntent);
console.log(`${config.name}扫码配置完成`);
} catch (error) {
console.error(`${config.name}扫码配置失败:`, error);
}
}
// 注册广播接收器
const IntentFilter = plus.android.importClass('android.content.IntentFilter');
const filter = new IntentFilter();
filter.addAction(config.action);
const self = this;
const receiver = plus.android.implements(
'io.dcloud.feature.internal.reflect.BroadcastReceiver',
{
onReceive: (context, intent) => {
console.log(`${config.name}扫码广播接收,isPageActive:`, self.isPageActive);
if (!self.isPageActive) return;
try {
plus.android.importClass('android.content.Intent');
const scanResult = intent.getStringExtra(config.dataKey);
console.log(`${config.name}扫码结果:`, scanResult);
if (scanResult) {
self.handleScanResult(scanResult, scanCallback);
}
} catch (error) {
console.error(`处理${config.name}广播数据出错:`, error);
}
}
}
);
main.registerReceiver(receiver, filter);
// 保存接收器引用,用于后续注销
this.scanReceivers.push({
brand: config.brand,
receiver: receiver
});
}, },
// 处理扫码结果的统一方法 // 处理扫码结果的统一方法
@@ -156,18 +188,30 @@ export default {
} }
}, },
// 取消扫码广播监听 // 取消所有扫码广播监听
unregisterScanBroadcast() { unregisterScanBroadcast() {
if (this.scanReceiver) { // #ifdef APP-PLUS
try { if (this.scanReceivers.length === 0) return;
const main = plus.android.runtimeMainActivity();
main.unregisterReceiver(this.scanReceiver); try {
this.scanReceiver = null; const main = plus.android.runtimeMainActivity();
console.log("扫码广播注销成功");
} catch (err) { this.scanReceivers.forEach(item => {
console.error("注销扫码广播失败:", err); try {
} main.unregisterReceiver(item.receiver);
console.log(`${item.brand}扫码广播注销成功`);
} catch (err) {
console.error(`${item.brand}扫码广播注销失败:`, err);
}
});
this.scanReceivers = [];
this.registeredBrands = [];
console.log('所有扫码广播注销完成');
} catch (err) {
console.error('注销扫码广播失败:', err);
} }
// #endif
}, },
// 通用的商品扫描处理方法 // 通用的商品扫描处理方法
+1 -1
View File
@@ -11,7 +11,7 @@
props: { props: {
src: { src: {
type: String, type: String,
default: '/static/img/dataNull.png' default: '/src/static/img/dataNull.png'
}, },
title: { title: {
type: String, type: String,
+11 -10
View File
@@ -2,12 +2,12 @@
"name" : "浩拓技术", "name" : "浩拓技术",
"appid" : "__UNI__F79F300", "appid" : "__UNI__F79F300",
"description" : "浩拓纺织平台", "description" : "浩拓纺织平台",
"versionName" : "1.1.1", "versionName" : "1.1.4",
"versionCode" : 101, "versionCode" : 104,
"transformPx" : false, "transformPx" : false,
"app-plus" : { "app-plus" : {
"optimization" : { "optimization" : {
"subPackages" : true "subPackages" : false
}, },
"safearea" : { "safearea" : {
"bottom" : { "bottom" : {
@@ -40,6 +40,7 @@
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>", "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>", "<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
@@ -75,10 +76,10 @@
}, },
"icons" : { "icons" : {
"android" : { "android" : {
"hdpi" : "static/haotop/image/logo/72.png", "hdpi" : "src/static/haotop/image/logo/72.png",
"xhdpi" : "static/haotop/image/logo/96.png", "xhdpi" : "src/static/haotop/image/logo/96.png",
"xxhdpi" : "static/haotop/image/logo/144.png", "xxhdpi" : "src/static/haotop/image/logo/144.png",
"xxxhdpi" : "static/haotop/image/logo/192.png" "xxxhdpi" : "src/static/haotop/image/logo/192.png"
}, },
"ios" : { "ios" : {
"appstore" : "", "appstore" : "",
@@ -108,9 +109,9 @@
"splashscreen" : { "splashscreen" : {
"androidStyle" : "default", "androidStyle" : "default",
"android" : { "android" : {
"hdpi" : "static/haotop/image/logo/Start.png", "hdpi" : "src/static/haotop/image/logo/Start.png",
"xhdpi" : "static/haotop/image/logo/Start.png", "xhdpi" : "src/static/haotop/image/logo/Start.png",
"xxhdpi" : "static/haotop/image/logo/Start.png" "xxhdpi" : "src/static/haotop/image/logo/Start.png"
} }
} }
} }
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"easycom": { "easycom": {
"^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue" "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
}, },
"pages": [ "pages": [
{ {
@@ -58,7 +58,7 @@
} }
}, },
{ {
"path": "uview-ui/components/u-avatar-cropper/u-avatar-cropper", "path": "../node_modules/uview-ui/components/u-avatar-cropper/u-avatar-cropper",
"style": { "style": {
"navigationBarTitleText": "头像裁剪", "navigationBarTitleText": "头像裁剪",
"navigationBarBackgroundColor": "#000000" "navigationBarBackgroundColor": "#000000"
+1 -1
View File
@@ -2,7 +2,7 @@
<view> <view>
<topDropdown :isSxShow="true" :dropdown1="dropdown1" dropdown2="上架中" :options1="options1" :options2="isSjList" :optionsSx="sxList" searchUrl="./searchAdmin" @dropDownFun1="dropDownFun1" @dropDownFun2="dropDownFun2" @optionSxFun="optionSxFun"> <topDropdown :isSxShow="true" :dropdown1="dropdown1" dropdown2="上架中" :options1="options1" :options2="isSjList" :optionsSx="sxList" searchUrl="./searchAdmin" @dropDownFun1="dropDownFun1" @dropDownFun2="dropDownFun2" @optionSxFun="optionSxFun">
</topDropdown> </topDropdown>
<dataNull v-if="list.length == 0" src="/static/img/chahua/dataNullXz.png" title="暂无相关产品" <dataNull v-if="list.length == 0" src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品"
title1="请添加或者更换查询条件"></dataNull> title1="请添加或者更换查询条件"></dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"
+1 -1
View File
@@ -1,6 +1,6 @@
<template> <template>
<view> <view>
<dataNull v-if="list.length == 0" src="/static/img/chahua/dataNullXz.png" title="暂无相关分类" <dataNull v-if="list.length == 0" src="/src/static/img/chahua/dataNullXz.png" title="暂无相关分类"
title1="点击添加按钮添加相关分类"></dataNull> title1="点击添加按钮添加相关分类"></dataNull>
<view v-else class="content"> <view v-else class="content">
<view class="row" v-for="(row, index) in list" :key="index"> <view class="row" v-for="(row, index) in list" :key="index">
+2 -2
View File
@@ -44,13 +44,13 @@
</view> </view>
</view> </view>
</view> </view>
<dataNull v-else src="/static/img/chahua/dataNullXz.png" title="暂无产品参数说明"></dataNull> <dataNull v-else src="/src/static/img/chahua/dataNullXz.png" title="暂无产品参数说明"></dataNull>
<view class="xqTitle">产品详情图</view> <view class="xqTitle">产品详情图</view>
<view class="imgXQ" v-if="detail.cpXqtList && detail.cpXqtList.length > 0"> <view class="imgXQ" v-if="detail.cpXqtList && detail.cpXqtList.length > 0">
<u-image width="100%" mode="widthFix" v-for="(item, index) in detail.cpXqtList" :key="index" :src="item.url" <u-image width="100%" mode="widthFix" v-for="(item, index) in detail.cpXqtList" :key="index" :src="item.url"
:lazy-load="true" @click="xqtClickFun(item.url, detail.cpXqtList)"></u-image> :lazy-load="true" @click="xqtClickFun(item.url, detail.cpXqtList)"></u-image>
</view> </view>
<dataNull v-else src="/static/img/chahua/dataNullXz.png" title="暂无产品详情图"></dataNull> <dataNull v-else src="/src/static/img/chahua/dataNullXz.png" title="暂无产品详情图"></dataNull>
<!--提交按钮--> <!--提交按钮-->
<view v-if="!pageType" class="submitView"> <view v-if="!pageType" class="submitView">
<u-button type="primary" class="submitBtn" :ripple="true" ripple-bg-color="#909399" @click="updateCpFun">修改产品</u-button> <u-button type="primary" class="submitBtn" :ripple="true" ripple-bg-color="#909399" @click="updateCpFun">修改产品</u-button>
+1 -1
View File
@@ -1,7 +1,7 @@
<template> <template>
<view> <view>
<searchView placeholderStr="请输入产品名称" @searchViewClickFun="searchEventsFun($event)"></searchView> <searchView placeholderStr="请输入产品名称" @searchViewClickFun="searchEventsFun($event)"></searchView>
<dataNull v-if="list.length == 0" src="/static/img/chahua/dataNullXz.png" title="暂无相关产品" <dataNull v-if="list.length == 0" src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品"
title1="请更换查询条件"></dataNull> title1="请更换查询条件"></dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"
+1 -1
View File
@@ -1,7 +1,7 @@
<template> <template>
<view> <view>
<searchView placeholderStr="请输入产品名称" @searchViewClickFun="searchEventsFun($event)"></searchView> <searchView placeholderStr="请输入产品名称" @searchViewClickFun="searchEventsFun($event)"></searchView>
<dataNull v-if="list.length == 0" src="/static/img/chahua/dataNullXz.png" title="暂无相关产品" <dataNull v-if="list.length == 0" src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品"
title1="请添加或者更换查询条件"></dataNull> title1="请添加或者更换查询条件"></dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="getChanPinFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"
+2 -2
View File
@@ -57,12 +57,12 @@
<getMore :isMore="tabBars[tabCurrentIndex].isMore" style="margin-bottom: 16px;"></getMore> <getMore :isMore="tabBars[tabCurrentIndex].isMore" style="margin-bottom: 16px;"></getMore>
<view style="height: 36rpx;"></view> <view style="height: 36rpx;"></view>
</view> </view>
<dataNull v-else src="/static/img/chahua/dataNullXz.png" title="暂无相关产品" title1="请先添加"> <dataNull v-else src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品" title1="请先添加">
</dataNull> </dataNull>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<dataNull v-else src="/static/img/chahua/dataNullXz.png" title="暂无相关产品"> <dataNull v-else src="/src/static/img/chahua/dataNullXz.png" title="暂无相关产品">
</dataNull> </dataNull>
</view> </view>
</view> </view>
+1 -1
View File
@@ -16,7 +16,7 @@
<getMore :isMore="isMore" nullMsg="已加载全部~"></getMore> <getMore :isMore="isMore" nullMsg="已加载全部~"></getMore>
<view class="h200"></view> <view class="h200"></view>
</view> </view>
<dataNull v-else src="/static/img/dataNull.png" title="暂无相关客户哦~"></dataNull> <dataNull v-else src="/src/static/img/dataNull.png" title="暂无相关客户哦~"></dataNull>
</scroll-view> </scroll-view>
<u-action-sheet :list="sheetList" v-model="moreShow"></u-action-sheet> <u-action-sheet :list="sheetList" v-model="moreShow"></u-action-sheet>
</view> </view>
+1 -1
View File
@@ -11,7 +11,7 @@
<getMore :isMore="isMore" nullMsg="已加载全部~"></getMore> <getMore :isMore="isMore" nullMsg="已加载全部~"></getMore>
<view class="h200"></view> <view class="h200"></view>
</view> </view>
<dataNull v-else src="/static/img/dataNull.png" title="暂无相关客户哦~"></dataNull> <dataNull v-else src="/src/static/img/dataNull.png" title="暂无相关客户哦~"></dataNull>
</scroll-view> </scroll-view>
<u-action-sheet :list="sheetList" v-model="moreShow"></u-action-sheet> <u-action-sheet :list="sheetList" v-model="moreShow"></u-action-sheet>
</view> </view>
@@ -16,7 +16,7 @@
<getMore :isMore="isMore" nullMsg="已加载全部~"></getMore> <getMore :isMore="isMore" nullMsg="已加载全部~"></getMore>
<view class="h200"></view> <view class="h200"></view>
</view> </view>
<dataNull v-else src="/static/img/dataNull.png" title="暂无相关营销部门哦~"></dataNull> <dataNull v-else src="/src/static/img/dataNull.png" title="暂无相关营销部门哦~"></dataNull>
</scroll-view> </scroll-view>
<u-action-sheet :list="sheetList" v-model="moreShow"></u-action-sheet> <u-action-sheet :list="sheetList" v-model="moreShow"></u-action-sheet>
</view> </view>
@@ -472,4 +472,4 @@
border-radius: 10rpx; border-radius: 10rpx;
margin: 6rpx 26rpx 20rpx 0; margin: 6rpx 26rpx 20rpx 0;
} }
</style> </style>
+1 -1
View File
@@ -14,7 +14,7 @@
</view> </view>
</view> </view>
<dataNull v-if="list.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关排产单" title1="请添加或者更换搜索添加"> <dataNull v-if="list.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关排产单" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="selectDataFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="selectDataFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"
+1 -1
View File
@@ -1,7 +1,7 @@
<template> <template>
<view> <view>
<topDropdown :isSxShow="true" :isPaiXu="true" :dropdown1="dropdown1" :options1="options1" :optionsSx="sxList" :optionsPx="optionsPx" :isDuoXuan="true" searchLabel1="报价名称" searchPh1="请输入报价单名称" @searchBoxEmitFun="searchBoxEmitFun" @dropDownFun1="dropDownFun1" @optionSxFun="optionSxFun" @optionPxFun="optionPxFun" :isKhShow="true"></topDropdown> <topDropdown :isSxShow="true" :isPaiXu="true" :dropdown1="dropdown1" :options1="options1" :optionsSx="sxList" :optionsPx="optionsPx" :isDuoXuan="true" searchLabel1="报价名称" searchPh1="请输入报价单名称" @searchBoxEmitFun="searchBoxEmitFun" @dropDownFun1="dropDownFun1" @optionSxFun="optionSxFun" @optionPxFun="optionPxFun" :isKhShow="true"></topDropdown>
<dataNull v-if="list.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关合同" title1="请添加或者更换搜索添加"></dataNull> <dataNull v-if="list.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关合同" title1="请添加或者更换搜索添加"></dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="selectDataFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="selectDataFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" @refresherrefresh="onRefresh" @refresherrestore="onRestore"> refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" @refresherrefresh="onRefresh" @refresherrestore="onRestore">
<view v-for="(item, index) in list" :key="index" @click="cardClickFun(item, index)"> <view v-for="(item, index) in list" :key="index" @click="cardClickFun(item, index)">
+1 -1
View File
@@ -16,7 +16,7 @@
:current="activeTabIndex" @change="onTabChange"></u-tabs> :current="activeTabIndex" @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="ItemDataList.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关销售付运资料" title1="请添加或者更换搜索添加"> <dataNull v-if="ItemDataList.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关销售付运资料" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"> refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray">
+1 -1
View File
@@ -10,7 +10,7 @@
:current="activeTabIndex" @change="onTabChange"></u-tabs> :current="activeTabIndex" @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="list.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关销售预约单" title1="请添加或者更换搜索添加"> <dataNull v-if="list.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关销售预约单" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="selectDataFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="selectDataFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"
+1 -1
View File
@@ -37,7 +37,7 @@
<!-- 空状态展示 --> <!-- 空状态展示 -->
<view v-if="showEmpty" class="empty-state"> <view v-if="showEmpty" class="empty-state">
<dataNull <dataNull
src="/static/img/chahua/gjNull.png" src="/src/static/img/chahua/gjNull.png"
title="暂无相关配布单" title="暂无相关配布单"
title1="请添加或者更换搜索添加" title1="请添加或者更换搜索添加"
></dataNull> ></dataNull>
+113 -296
View File
@@ -6,31 +6,17 @@
<text class="title" style="width: 200px">日期:{{ BillDate }}</text> <text class="title" style="width: 200px">日期:{{ BillDate }}</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width: 200px" <text class="title" style="width: 200px">客户名称:{{ CustomerName }}</text>
>客户名称:{{ CustomerName }}</text <text class="title" style="width: 200px">销 售 员:{{ SaleUserName }}</text>
>
<text class="title" style="width: 200px"
>销 售 员:{{ SaleUserName }}</text
>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width: 200px" <text class="title" style="width: 200px">仓库名称:{{ StoreName }}{{ ToStoreName }}</text>
>仓库名称:{{ StoreName }}{{ ToStoreName }}</text
>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width: 200px" <text class="title" style="width: 200px">备注内容:{{ BillRemark }}</text>
>备注内容:{{ BillRemark }}</text
>
</u-form-item> </u-form-item>
<u-form-item label-width="130" label="条码资料:"> <u-form-item label-width="130" label="条码资料:">
<input <input type="text" v-model="QRBarCode" maxlength="-1" style="width: 170px" @confirm="SalePickBillDetailScan" />
type="text"
v-model="QRBarCode"
maxlength="-1"
style="width: 170px"
@confirm="SalePickBillDetailScan"
/>
<checkbox-group @change="handleAllCrockNoChange"> <checkbox-group @change="handleAllCrockNoChange">
<checkbox :checked="AllCrockNoScanStatus">整缸</checkbox> <checkbox :checked="AllCrockNoScanStatus">整缸</checkbox>
</checkbox-group> </checkbox-group>
@@ -41,58 +27,38 @@
</u-form> </u-form>
<u-form ref="uForm"> <u-form ref="uForm">
<u-form-item> <u-form-item>
<text class="title" style="width: 400px" <text class="title" style="width: 400px">成品名称:{{ FabricGoodsNo }}{{ FabricGoodsName }}</text>
>成品名称:{{ FabricGoodsNo }}{{ FabricGoodsName }}</text
>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width: 400px" <text class="title" style="width: 400px">色号颜色:{{ GoodsCodeNo }}{{ GoodsCodeName }}</text>
>色号颜色:{{ GoodsCodeNo }}{{ GoodsCodeName }}</text
>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width: 200px">成品缸号:{{ CrockNo }}</text> <text class="title" style="width: 200px">成品缸号:{{ CrockNo }}</text>
<text class="title" style="width: 200px" <text class="title" style="width: 200px">成品卷号:{{ GoodsBillNo }}</text>
>成品卷号:{{ GoodsBillNo }}</text
>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title">{{ BillDataMessage }}</text> <text class="title">{{ BillDataMessage }}</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width: 130px" <text class="title" style="width: 130px">配布条数:{{ BillSumRoll }}</text>
>配布条数:{{ BillSumRoll }}</text
>
<text class="title" style="width: 130px">数量:{{ BillSumQty }}</text> <text class="title" style="width: 130px">数量:{{ BillSumQty }}</text>
<text class="title" style="width: 120px">米数:{{ BillSumMQty }}</text> <text class="title" style="width: 120px">米数:{{ BillSumMQty }}</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width: 130px" <text class="title" style="width: 130px">已配条数:{{ BillScanRoll }}</text>
>已配条数:{{ BillScanRoll }}</text
>
<text class="title" style="width: 130px">数量:{{ BillScanQty }}</text> <text class="title" style="width: 130px">数量:{{ BillScanQty }}</text>
<text class="title" style="width: 120px">米数:{{ BillScanMQty }}</text> <text class="title" style="width: 120px">米数:{{ BillScanMQty }}</text>
</u-form-item> </u-form-item>
<view class="submitView"> <view class="submitView">
<u-button <u-button type="primary" class="submitBtn" :ripple="true" :loading="submitLoading" ripple-bg-color="#909399"
type="primary" @click="submitBtnFun">
class="submitBtn"
:ripple="true"
:loading="submitLoading"
ripple-bg-color="#909399"
@click="submitBtnFun"
>
{{ pageType ? "保存" : "提交" }} {{ pageType ? "保存" : "提交" }}
</u-button> </u-button>
</view> </view>
</u-form> </u-form>
<view class="u-demo-area"> <view class="u-demo-area">
<u-toast ref="uToast"></u-toast> <u-toast ref="uToast"></u-toast>
<wyb-table <wyb-table ref="table" :headers="headersMaster" :contents="GoodsDetailList" />
ref="table"
:headers="headersMaster"
:contents="GoodsDetailList"
/>
</view> </view>
</view> </view>
</template> </template>
@@ -105,8 +71,10 @@ import util, {
playErrorAudio, playErrorAudio,
} from "../../common/util"; } from "../../common/util";
import wybTable from "@/components/wyb-table/wyb-table.vue"; import wybTable from "@/components/wyb-table/wyb-table.vue";
import scanMixin from "@/common/scanMixin.js";
let that = ""; let that = "";
export default { export default {
mixins: [scanMixin],
data() { data() {
return { return {
SaleBillNo: "", // 单号 SaleBillNo: "", // 单号
@@ -115,7 +83,7 @@ export default {
align: "center", align: "center",
index: 0, index: 0,
pageType: "", pageType: "",
submitLoading: false, submitLoading: false,
actionSheetShow: false, actionSheetShow: false,
QRBarCode: "", QRBarCode: "",
BillNo: "", BillNo: "",
@@ -192,8 +160,7 @@ export default {
], ],
scanningInput: "", // 用于累积扫码输入 scanningInput: "", // 用于累积扫码输入
lastKeyTime: 0, // 用于判断扫码速度 lastKeyTime: 0, // 用于判断扫码速度
scanReceiver: null, // scanReceiver 和 isPageActive 由 scanMixin 提供
isPageActive: false, // 添加页面活动状态标志
}; };
}, },
@@ -214,14 +181,13 @@ export default {
}, },
onUnload() { onUnload() {
// #ifdef APP-PLUS
this.isPageActive = false; this.isPageActive = false;
// #ifdef APP-PLUS
this.unregisterScanBroadcast(); this.unregisterScanBroadcast();
// #endif // #endif
}, },
onHide() { onHide() {
// 页面隐藏时
this.isPageActive = false; this.isPageActive = false;
// #ifdef APP-PLUS // #ifdef APP-PLUS
this.unregisterScanBroadcast(); this.unregisterScanBroadcast();
@@ -229,10 +195,15 @@ export default {
}, },
onShow() { onShow() {
// 页面显示时
this.isPageActive = true; this.isPageActive = true;
// #ifdef APP-PLUS // #ifdef APP-PLUS
this.registerScanBroadcast(); this.registerScanBroadcast((scanResult) => {
console.log("配布单详情-扫码结果:", scanResult);
this.QRBarCode = scanResult;
this.$nextTick(() => {
this.handleScans();
});
});
// #endif // #endif
}, },
@@ -300,7 +271,7 @@ export default {
} }
this.GoodsDetailList = aResultDataList; this.GoodsDetailList = aResultDataList;
}).catch(error => { }).catch(error => {
this.triggered = false; this.triggered = false;
this.showError(error.msg); this.showError(error.msg);
@@ -323,247 +294,93 @@ export default {
icon: 'success' icon: 'success'
}); });
}, },
handleScans() { handleScans() {
// 删除模式判断 // 删除模式判断
let aBarCodeDelStatus = 1; // 1=正常扫码,3=删除模式 let aBarCodeDelStatus = 1; // 1=正常扫码,3=删除模式
if (this.BarCodeDelStatus) { if (this.BarCodeDelStatus) {
aBarCodeDelStatus = 3; aBarCodeDelStatus = 3;
}
// 整缸扫描判断
let aAllCrockNoScanStatus = 2; // 0=普通扫描,1=整缸扫描
if (this.AllCrockNoScanStatus) {
aAllCrockNoScanStatus = "1";
}
// 数据清理:去除空格、换行符等
let cleanCode = this.QRBarCode.replace(/\s+/g, '').replace(/[\r\n]/g, '');
console.log("this.QRBarCode ---->>" + cleanCode);
// 初始化条码和二维码变量
let aQRBarCode = "";
let aBarCode = "";
if (cleanCode.startsWith("66^") || cleanCode.startsWith("99^") || /[\u4E00-\u9FA5]/.test(cleanCode)) {
aQRBarCode = cleanCode;
} else {
aBarCode = cleanCode;
}
if (aQRBarCode == "" && aBarCode == "") {
this.QRBarCode = "";
this.showError('请扫描二维码或者条码');
return;
}
console.log('请求参数', aBarCodeDelStatus);
console.log('请求参数', aBarCode);
console.log('请求参数', aQRBarCode);
console.log('请求参数', this.BillMasterID);
console.log('请求参数 SaleBillNo', this.SaleBillNo);
console.log('请求参数 token', uni.getStorageSync("userToken").Token);
// 发送请求
util.request({
url: "/product/fpmArrangeOrder/updateFpmArrangeOrder",
method: "PUT",
header: {
Platform: 2,
Authorization: uni.getStorageSync("userToken").Token,
},
data: {
arrange_type: aBarCodeDelStatus, // 1=正常扫码,3=删除模式
bar_code: aBarCode, // 条码(如果是条形码就使用这个)
qr_code: aQRBarCode, // 二维码(如果是二维码就使用这个)
id: parseInt(this.BillMasterID), // 订单ID
// order_no: this.SaleBillNo, // 单号
},
success: (res) => {
console.log('API响应:', res);
if (res.data.code == 0 && res.data.msg == "success") {
var aResultData = res.data.data;
this.playSuccess();
this.FabricGoodsNo = aResultData.product_code;
this.FabricGoodsName = aResultData.product_name;
this.GoodsCodeNo = aResultData.product_color_code;
this.GoodsCodeName = aResultData.product_color_name;
this.CrockNo = aResultData.dyelot_number;
this.GoodsBillNo = aResultData.volume_number;
this.QRBarCode = "";
if (aBarCodeDelStatus == 1) {
this.showSuccess("扫描成功!");
} else {
this.showSuccess("删除成功!");
}
this.SalePickDetail();
} else {
this.showError("扫描出错," + res.data.msg);
this.QRBarCode = "";
}
},
fail: (error) => {
console.error('API请求失败:', error);
this.QRBarCode = "";
this.showError("连接服务器出错,请检查后台服务是否启动!");
},
});
},
// 注册扫码广播接收器(支持商米和新大陆扫码头)
registerScanBroadcast() {
try {
// 先尝试注册商米扫码头
this.registerSunmiBroadcast()
.then(() => {
console.log('商米扫码头注册成功');
})
.catch((error) => {
console.log('商米扫码头注册失败,尝试新大陆扫码头:', error);
// 如果商米扫码头注册失败,尝试新大陆扫码头
this.registerNewlandBroadcast();
});
} catch (error) {
console.error("注册扫码广播失败:", error);
} }
},
// 注册商米扫码头广播接收器 // 整缸扫描判断
registerSunmiBroadcast() { let aAllCrockNoScanStatus = 2; // 0=普通扫描,1=整缸扫描
return new Promise((resolve, reject) => { if (this.AllCrockNoScanStatus) {
try { aAllCrockNoScanStatus = "1";
const main = plus.android.runtimeMainActivity(); }
const IntentFilter = plus.android.importClass("android.content.IntentFilter");
const filter = new IntentFilter();
filter.addAction("com.sunmi.scanner.ACTION_DATA_CODE_RECEIVED");
const receiver = plus.android.implements( // 数据清理:去除空格、换行符等
"io.dcloud.feature.internal.reflect.BroadcastReceiver", let cleanCode = this.QRBarCode.replace(/\s+/g, '').replace(/[\r\n]/g, '');
{
onReceive: (context, intent) => {
console.log('商米扫码广播接收,isPageActive:', this.isPageActive);
// 只有当页面活动时才处理广播
if (!this.isPageActive) return;
try { console.log("this.QRBarCode ---->>" + cleanCode);
const scanResult = intent.getStringExtra("data");
const sourceBytes = intent.getByteArrayExtra("source_byte"); // 初始化条码和二维码变量
let aQRBarCode = "";
console.log('商米扫码结果:', scanResult, sourceBytes); let aBarCode = "";
if (scanResult) { if (cleanCode.startsWith("66^") || cleanCode.startsWith("99^") || /[\u4E00-\u9FA5]/.test(cleanCode)) {
this.handleScanResult(scanResult); aQRBarCode = cleanCode;
} } else {
} catch (error) { aBarCode = cleanCode;
console.error('处理商米广播数据时出错:', error); }
}
} if (aQRBarCode == "" && aBarCode == "") {
this.QRBarCode = "";
this.showError('请扫描二维码或者条码');
return;
}
console.log('请求参数', aBarCodeDelStatus);
console.log('请求参数', aBarCode);
console.log('请求参数', aQRBarCode);
console.log('请求参数', this.BillMasterID);
console.log('请求参数 SaleBillNo', this.SaleBillNo);
console.log('请求参数 token', uni.getStorageSync("userToken").Token);
// 发送请求
util.request({
url: "/product/fpmArrangeOrder/updateFpmArrangeOrder",
method: "PUT",
header: {
Platform: 2,
Authorization: uni.getStorageSync("userToken").Token,
},
data: {
arrange_type: aBarCodeDelStatus, // 1=正常扫码,3=删除模式
bar_code: aBarCode, // 条码(如果是条形码就使用这个)
qr_code: aQRBarCode, // 二维码(如果是二维码就使用这个)
id: parseInt(this.BillMasterID), // 订单ID
// order_no: this.SaleBillNo, // 单号
},
success: (res) => {
console.log('API响应:', res);
if (res.data.code == 0 && res.data.msg == "success") {
var aResultData = res.data.data;
this.playSuccess();
this.FabricGoodsNo = aResultData.product_code;
this.FabricGoodsName = aResultData.product_name;
this.GoodsCodeNo = aResultData.product_color_code;
this.GoodsCodeName = aResultData.product_color_name;
this.CrockNo = aResultData.dyelot_number;
this.GoodsBillNo = aResultData.volume_number;
this.QRBarCode = "";
if (aBarCodeDelStatus == 1) {
this.showSuccess("扫描成功!");
} else {
this.showSuccess("删除成功!");
} }
);
// 注册广播接收器 this.SalePickDetail();
main.registerReceiver(receiver, filter); } else {
this.scanReceiver = receiver; this.showError("扫描出错," + res.data.msg);
console.log('商米扫码广播接收器注册成功'); this.QRBarCode = "";
resolve();
} catch (error) {
console.error('注册商米广播接收器失败:', error);
reject(error);
}
});
},
// 注册新大陆扫码头广播接收器
registerNewlandBroadcast() {
try {
const main = plus.android.runtimeMainActivity();
// 先配置扫码枪广播设置
try {
const Intent = plus.android.importClass("android.content.Intent");
const intent = new Intent("com.android.scanner.service_settings");
intent.putExtra(
"action_barcode_broadcast",
"com.android.server.scannerservice.broadcast"
);
intent.putExtra("key_barcode_broadcast", "scannerdata");
main.sendBroadcast(intent);
console.log('新大陆扫码枪广播配置完成');
} catch (error) {
console.error("配置新大陆扫码枪广播失败:", error);
}
// 注册广播接收器
const IntentFilter = plus.android.importClass(
"android.content.IntentFilter"
);
const filter = new IntentFilter();
filter.addAction("com.android.server.scannerservice.broadcast");
console.log("添加新大陆广播action完成");
const receiver = plus.android.implements(
"io.dcloud.feature.internal.reflect.BroadcastReceiver",
{
onReceive: (context, intent) => {
console.log('新大陆扫码广播接收,isPageActive:', this.isPageActive);
// 只有当页面活动时才处理广播
if (!this.isPageActive) return;
try {
const scanResult = intent.getStringExtra("scannerdata");
console.log("新大陆扫码结果:", scanResult);
if (scanResult) {
this.handleScanResult(scanResult);
}
} catch (error) {
console.error("处理新大陆广播数据时出错:", error);
}
},
} }
); },
fail: (error) => {
// 注册广播接收器 console.error('API请求失败:', error);
main.registerReceiver(receiver, filter); this.QRBarCode = "";
this.scanReceiver = receiver; this.showError("连接服务器出错,请检查后台服务是否启动!");
console.log("新大陆扫码广播注册成功,等待扫码..."); },
} catch (error) { });
console.error("注册新大陆扫码广播失败:", error);
throw error;
}
},
// 处理扫码结果的统一方法
handleScanResult(scanResult) {
try {
// 数据清理:去除空格、换行符等
let cleanCode = scanResult.replace(/\s+/g, '').replace(/[\r\n]/g, '');
console.log("配布单详情-扫码结果:", cleanCode);
this.QRBarCode = cleanCode;
this.$nextTick(() => {
this.handleScans();
});
} catch (error) {
console.error("处理扫码结果时出错:", error);
}
},
// 注销扫码广播接收器
unregisterScanBroadcast() {
if (this.scanReceiver) {
try {
const main = plus.android.runtimeMainActivity();
main.unregisterReceiver(this.scanReceiver);
this.scanReceiver = null;
console.log("扫码广播注销成功");
} catch (error) {
console.error("注销扫码广播失败:", error);
}
}
}, },
SalePickBillDetailScan() { SalePickBillDetailScan() {
@@ -601,13 +418,13 @@ export default {
} }
}, },
// 新增提交订单的方法 // 新增提交订单的方法
submitOrder: function() { submitOrder: function () {
this.submitLoading = true this.submitLoading = true
this.$u.api.outFpmArrangeOrder({ this.$u.api.outFpmArrangeOrder({
id: parseInt(this.BillMasterID) id: parseInt(this.BillMasterID)
}).then(res => { }).then(res => {
this.submitLoading = false this.submitLoading = false
console.log('outFpmArrangeOrder res',res) console.log('outFpmArrangeOrder res', res)
if (res) { if (res) {
this.BillDataMessage = "成功生成出仓单!"; this.BillDataMessage = "成功生成出仓单!";
this.playSuccess(); this.playSuccess();
@@ -617,7 +434,7 @@ export default {
return; return;
} }
}).catch(error => { }).catch(error => {
this.submitLoading = false this.submitLoading = false
this.playError(); this.playError();
this.BillDataMessage = "连接服务器出错,请检查后台服务是否启动!"; this.BillDataMessage = "连接服务器出错,请检查后台服务是否启动!";
}); });
@@ -676,7 +493,7 @@ page {
margin-right: 12rpx; margin-right: 12rpx;
} }
.cpInput > input { .cpInput>input {
box-sizing: border-box; box-sizing: border-box;
border: 1rpx solid #dddddd; border: 1rpx solid #dddddd;
width: 100%; width: 100%;
@@ -690,7 +507,7 @@ page {
margin-right: 12rpx; margin-right: 12rpx;
} }
.cpInput1 > input { .cpInput1>input {
box-sizing: border-box; box-sizing: border-box;
border: 1rpx solid #dddddd; border: 1rpx solid #dddddd;
width: 100%; width: 100%;
@@ -16,7 +16,7 @@
:current="activeTabIndex" @change="onTabChange"></u-tabs> :current="activeTabIndex" @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="ItemDataList.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加"> <dataNull v-if="ItemDataList.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"> refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray">
+1 -1
View File
@@ -6,7 +6,7 @@
:current="activeTabIndex" @change="onTabChange"></u-tabs> :current="activeTabIndex" @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="list.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关胚布进仓单" title1="请添加或者更换搜索添加"> <dataNull v-if="list.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关胚布进仓单" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered"
+2 -2
View File
@@ -15,7 +15,7 @@
@change="onTabChange"></u-tabs> @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="ItemDataList.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关出仓资料" <dataNull v-if="ItemDataList.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关出仓资料"
title1="请添加或者更换搜索添加"> title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" refresher-enabled :refresher-threshold="200" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" refresher-enabled :refresher-threshold="200"
@@ -228,4 +228,4 @@
margin: 20upx; margin: 20upx;
font-size: 28upx; font-size: 28upx;
} }
</style> </style>
@@ -15,7 +15,7 @@
@change="onTabChange"></u-tabs> @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="list.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加"> <dataNull v-if="list.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<!-- <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <!-- <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"> refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray">
@@ -2,12 +2,12 @@
<view class="wrap"> <view class="wrap">
<u-form ref="uForm"> <u-form ref="uForm">
<u-form-item> <u-form-item>
<text class="title" style="width:200px;">单号:{{GoodsCheckBillNo}}</text> <text class="title" style="width:200px;">单号:{{ GoodsCheckBillNo }}</text>
<text class="title" style="width:200px;">日期:{{GoodsCheckBillDate}}</text> <text class="title" style="width:200px;">日期:{{ GoodsCheckBillDate }}</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width:200px;">仓库名称:{{StoreName}}</text> <text class="title" style="width:200px;">仓库名称:{{ StoreName }}</text>
<text class="title" style="width:200px;">仓位名称:{{StoreStationName}}</text> <text class="title" style="width:200px;">仓位名称:{{ StoreStationName }}</text>
</u-form-item> </u-form-item>
<u-form-item label-width="150" label="条码资料:"> <u-form-item label-width="150" label="条码资料:">
<input type="text" v-model="QRBarCode" maxlength="-1" style="width:200px;" <input type="text" v-model="QRBarCode" maxlength="-1" style="width:200px;"
@@ -17,32 +17,32 @@
</checkbox-group> </checkbox-group>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title">{{BillDataMessage}}</text> <text class="title">{{ BillDataMessage }}</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width:200px;">成品编号:{{FabricGoodsNo}}</text> <text class="title" style="width:200px;">成品编号:{{ FabricGoodsNo }}</text>
<text class="title" style="width:200px;">成品重量:{{GoodsQty}}KG</text> <text class="title" style="width:200px;">成品重量:{{ GoodsQty }}KG</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width:200px;">成品色号:{{GoodsCodeNo}}</text> <text class="title" style="width:200px;">成品色号:{{ GoodsCodeNo }}</text>
<text class="title" style="width:200px;">成品颜色:{{GoodsCodeName}}</text> <text class="title" style="width:200px;">成品颜色:{{ GoodsCodeName }}</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width:200px;">成品缸号:{{CrockNo}}</text> <text class="title" style="width:200px;">成品缸号:{{ CrockNo }}</text>
<text class="title" style="width:200px;">缸号卷号:{{GoodsBillNo}}</text> <text class="title" style="width:200px;">缸号卷号:{{ GoodsBillNo }}</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width:200px;">本架盘前:{{BillSumOldRoll}}条</text> <text class="title" style="width:200px;">本架盘前:{{ BillSumOldRoll }}条</text>
<text class="title" style="width:200px;">本架实盘:{{BillSumNewRoll}}条</text> <text class="title" style="width:200px;">本架实盘:{{ BillSumNewRoll }}条</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width:200px;">本色盘前:{{GoodsCodeNoSumOldRoll}}条</text> <text class="title" style="width:200px;">本色盘前:{{ GoodsCodeNoSumOldRoll }}条</text>
<text class="title" style="width:200px;">本色实盘:{{GoodsCodeNoSumNewRoll}}条</text> <text class="title" style="width:200px;">本色实盘:{{ GoodsCodeNoSumNewRoll }}条</text>
</u-form-item> </u-form-item>
<u-form-item> <u-form-item>
<text class="title" style="width:200px;">本缸盘前:{{CrockNoSumOldRoll}}条</text> <text class="title" style="width:200px;">本缸盘前:{{ CrockNoSumOldRoll }}条</text>
<text class="title" style="width:200px;">本缸实盘:{{CrockNoSumNewRoll}}条</text> <text class="title" style="width:200px;">本缸实盘:{{ CrockNoSumNewRoll }}条</text>
</u-form-item> </u-form-item>
</u-form> </u-form>
<view class="u-demo-area"> <view class="u-demo-area">
@@ -55,417 +55,425 @@
<view class="submitView"> <view class="submitView">
<u-button type="primary" style="width:100px;" class="commitBtn" :ripple="true" ripple-bg-color="#909399" <u-button type="primary" style="width:100px;" class="commitBtn" :ripple="true" ripple-bg-color="#909399"
@click="commitBtnFun"> @click="commitBtnFun">
{{CommitType ? '消审' : '审核'}} {{ CommitType ? '消审' : '审核' }}
</u-button> </u-button>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
import util, { import util, {
parFabricGoodsBarCode2D, parFabricGoodsBarCode2D,
parYarnGoodsBarCode2D, parYarnGoodsBarCode2D,
playSuccessAudio, playSuccessAudio,
playErrorAudio playErrorAudio
} from '../../common/util'; } from '../../common/util';
import scanMixin from '@/common/scanMixin.js'; import scanMixin from '@/common/scanMixin.js';
import wybTable from '@/components/wyb-table/wyb-table.vue'; import wybTable from '@/components/wyb-table/wyb-table.vue';
//let that = ''; //let that = '';
export default { export default {
mixins: [scanMixin], mixins: [scanMixin],
data() { data() {
return { return {
borderColor: '#e4e7ed', borderColor: '#e4e7ed',
align: 'center', align: 'center',
index: 0, index: 0,
CommitType: '', CommitType: '',
CommitProcName: '', CommitProcName: '',
actionSheetShow: false, actionSheetShow: false,
QRBarCode: '', QRBarCode: '',
GoodsCheckBillID: 0, GoodsCheckBillID: 0,
GoodsCheckBillNo: '', GoodsCheckBillNo: '',
GoodsCheckBillDate: '', GoodsCheckBillDate: '',
GoodsCheckBillMasterNo: '', GoodsCheckBillMasterNo: '',
StoreNameID: '', StoreNameID: '',
StoreName: '', StoreName: '',
StoreStationID: '', StoreStationID: '',
StoreStationNo: '', StoreStationNo: '',
StoreStationName: '', StoreStationName: '',
CustomerName: '', CustomerName: '',
FabricGoodsNo: '', FabricGoodsNo: '',
FabricGoodsName: '', FabricGoodsName: '',
GoodsCodeNo: '', GoodsCodeNo: '',
GoodsCodeName: '', GoodsCodeName: '',
CrockNo: '', CrockNo: '',
GoodsBillNo: '', GoodsBillNo: '',
GoodsQty: 0, GoodsQty: 0,
StoreStationNameFocus: false, StoreStationNameFocus: false,
BillSumOldRoll: 0, BillSumOldRoll: 0,
BillSumNewRoll: 0, BillSumNewRoll: 0,
GoodsCodeNoSumOldRoll: 0, GoodsCodeNoSumOldRoll: 0,
GoodsCodeNoSumNewRoll: 0, GoodsCodeNoSumNewRoll: 0,
CrockNoSumOldRoll: 0, CrockNoSumOldRoll: 0,
CrockNoSumNewRoll: 0, CrockNoSumNewRoll: 0,
BarCodeDelStatus: false, BarCodeDelStatus: false,
GoodsDetailList: [], GoodsDetailList: [],
BillDataMessage: '', BillDataMessage: '',
headersMaster: [{ headersMaster: [{
label: '成品编号', label: '成品编号',
key: 'product_code' key: 'product_code'
}, { }, {
label: '成品名称', label: '成品名称',
key: 'product_name' key: 'product_name'
}, { }, {
label: '成品色号', label: '成品色号',
key: 'product_color_code' key: 'product_color_code'
}, { }, {
label: '成品颜色', label: '成品颜色',
key: 'product_color_name' key: 'product_color_name'
}, { }, {
label: '成品缸号', label: '成品缸号',
key: 'dyelot_number' key: 'dyelot_number'
}, { }, {
label: '盘前条数', label: '盘前条数',
key: 'roll' key: 'roll'
}, { }, {
label: '实盘条数', label: '实盘条数',
key: 'check_roll' key: 'check_roll'
}, { }, {
label: '盈亏条数', label: '盈亏条数',
key: 'different_roll' key: 'different_roll'
}], }],
// 移除了 scanReceiver 和 isPageActive,因为它们现在由 scanMixin 提供 // 移除了 scanReceiver 和 isPageActive,因为它们现在由 scanMixin 提供
} }
}, },
onLoad(e) { onLoad(e) {
//that = this; //that = this;
if (e.billid) { if (e.billid) {
this.GoodsCheckBillID = e.billid; this.GoodsCheckBillID = e.billid;
this.GoodsCheckBillDetailData(); this.GoodsCheckBillDetailData();
} }
}, },
onShow() { onShow() {
// 调用 mixin 中的 onShow 方法 // 设置页面激活状态
if (this.$options.mixins && this.$options.mixins[0] && this.$options.mixins[0].onShow) { this.isPageActive = true;
this.$options.mixins[0].onShow.call(this);
} // #ifdef APP-PLUS
this.registerScanBroadcast((scanResult) => {
// #ifdef APP-PLUS console.log("扫码结果:", scanResult);
this.registerScanBroadcast((scanResult) => {
console.log("扫码结果:", scanResult); // 处理扫描结果
this.QRBarCode = scanResult;
// 处理扫描结果,按照原有逻辑 this.$nextTick(() => {
if(!this.QRBarCode){ this.GoodsCheckBillDetailScan();
console.log("请先扫描资料!", scanResult); });
this.QRBarCode = scanResult; });
this.$nextTick(() => { // #endif
this.GoodsCheckBillDetailScan(); },
});
return; onHide() {
} this.isPageActive = false;
// #ifdef APP-PLUS
this.unregisterScanBroadcast();
// #endif
},
onUnload() {
this.isPageActive = false;
// #ifdef APP-PLUS
this.unregisterScanBroadcast();
// #endif
},
methods: {
// 添加通用错误提示方法
showError(message) {
this.playError();
uni.showModal({
title: '提示',
content: message,
showCancel: false
}); });
// #endif
}, },
methods: {
// 添加通用错误提示方法
showError(message) {
this.playError();
uni.showModal({
title: '提示',
content: message,
showCancel: false
});
},
playSuccess() {
util.playSuccessAudio();
},
playError() {
util.playErrorAudio();
},
BarCodeDelChange: function() { playSuccess() {
this.BarCodeDelStatus = !this.BarCodeDelStatus; util.playSuccessAudio();
}, },
playError() {
util.playErrorAudio();
},
GoodsCheckBillDetailScan() { BarCodeDelChange: function () {
if (this.StoreNameID == 0 && this.GoodsCheckBillNo == '') { this.BarCodeDelStatus = !this.BarCodeDelStatus;
this.QRBarCode = ''; },
this.showError('请先新增单据');
return; GoodsCheckBillDetailScan() {
} if (this.StoreNameID == 0 && this.GoodsCheckBillNo == '') {
this.QRBarCode = '';
// 数据清理:去除空格、换行符等 this.showError('请先新增单据');
let cleanCode = this.QRBarCode.replace(/\s+/g, '').replace(/[\r\n]/g, ''); return;
}
console.log("this.QRBarCode ---->>" + cleanCode);
// 数据清理:去除空格、换行符等
var aQRBarCode = ''; let cleanCode = this.QRBarCode.replace(/\s+/g, '').replace(/[\r\n]/g, '');
var aBarCode = '';
if (cleanCode.startsWith('66^')>0 || cleanCode.startsWith('99^')>0 || /[\u4E00-\u9FA5]/.test(cleanCode)){ console.log("this.QRBarCode ---->>" + cleanCode);
aQRBarCode = cleanCode
}else { var aQRBarCode = '';
aBarCode = cleanCode; var aBarCode = '';
} if (cleanCode.startsWith('66^') > 0 || cleanCode.startsWith('99^') > 0 || /[\u4E00-\u9FA5]/.test(cleanCode)) {
aQRBarCode = cleanCode
if (aQRBarCode == '' && aBarCode == ''){ } else {
this.QRBarCode = ''; aBarCode = cleanCode;
this.aBarCode = '' }
this.showError('请扫描二维码或者条码');
return; if (aQRBarCode == '' && aBarCode == '') {
} this.QRBarCode = '';
this.aBarCode = ''
let aBarCodeDelStatus = 1 this.showError('请扫描二维码或者条码');
if (this.BarCodeDelStatus) { return;
aBarCodeDelStatus = 3; }
};
let aBarCodeDelStatus = 1
util.request({ if (this.BarCodeDelStatus) {
url: '/product/productCheckOrder/updateProductCheckOrder', aBarCodeDelStatus = 3;
method: 'put', };
data: {
'arrange_type': aBarCodeDelStatus, util.request({
'id': parseInt(this.GoodsCheckBillID), url: '/product/productCheckOrder/updateProductCheckOrder',
'bar_code': aBarCode, method: 'put',
'qr_code': aQRBarCode, data: {
}, 'arrange_type': aBarCodeDelStatus,
success: (res) => { 'id': parseInt(this.GoodsCheckBillID),
console.log("-->>--" + JSON.stringify(res.data)); 'bar_code': aBarCode,
if (res.data.code == '0'){ 'qr_code': aQRBarCode,
let aResultData = res.data.data; },
this.FabricGoodsNo = aResultData.product_code; success: (res) => {
this.FabricGoodsName = aResultData.product_name; console.log("-->>--" + JSON.stringify(res.data));
this.GoodsCodeNo = aResultData.product_color_code; if (res.data.code == '0') {
this.GoodsCodeName = aResultData.product_color_name; let aResultData = res.data.data;
this.CrockNo = aResultData.dyelot_number; this.FabricGoodsNo = aResultData.product_code;
this.GoodsBillNo = aResultData.volume_number; this.FabricGoodsName = aResultData.product_name;
this.GoodsQty = aResultData.weight / 10000; this.GoodsCodeNo = aResultData.product_color_code;
this.BillSumOldRoll = aResultData.warehouse_bin_check_before_roll/100; this.GoodsCodeName = aResultData.product_color_name;
this.BillSumNewRoll = aResultData.warehouse_bin_check_roll/100; this.CrockNo = aResultData.dyelot_number;
this.GoodsCodeNoSumOldRoll = aResultData.color_check_before_roll/100; this.GoodsBillNo = aResultData.volume_number;
this.GoodsCodeNoSumNewRoll = aResultData.color_check_roll/100; this.GoodsQty = aResultData.weight / 10000;
this.CrockNoSumOldRoll = aResultData.dye_check_before_roll/100; this.BillSumOldRoll = aResultData.warehouse_bin_check_before_roll / 100;
this.CrockNoSumNewRoll = aResultData.dye_check_roll/100; this.BillSumNewRoll = aResultData.warehouse_bin_check_roll / 100;
this.GoodsCodeNoSumOldRoll = aResultData.color_check_before_roll / 100;
this.GoodsCodeNoSumNewRoll = aResultData.color_check_roll / 100;
this.CrockNoSumOldRoll = aResultData.dye_check_before_roll / 100;
this.CrockNoSumNewRoll = aResultData.dye_check_roll / 100;
this.playSuccess();
this.GoodsCheckBillDetailData();
this.BillDataMessage = res.data.msg;
this.QRBarCode = '';
}
else {
this.showError(res.data.msg);
this.QRBarCode = '';
}
},
fail: (error) => {
this.showError('连接服务器出错,请检查后台服务是否启动!');
console.log('error', error)
},
})
},
GoodsCheckBillDetailData: function () {
console.log("--aaa->>" + this.GoodsCheckBillID);
console.log("--aaa->>" + this.GoodsCheckBillID);
util.request({
url: '/product/productCheckOrder/getProductCheckOrder',
method: 'get',
data: {
'id': parseInt(this.GoodsCheckBillID),
},
success: (res) => {
console.log("-->>--" + JSON.stringify(res.data.data));
this.GoodsDetailList = [];
this.GoodsCheckBillNo = res.data.data.order_no;
this.StoreName = res.data.data.warehouse_name
this.StoreStationName = res.data.data.warehouse_bin_name;
this.GoodsCheckBillDate = this.$u.timeFormat(res.data.data.check_time, 'yyyy-mm-dd')
/* this.BillSumOldRoll = res.data.data.item_data.roll;
this.BillSumNewRoll = res.data.data.item_data.check_roll; */
var aResultData = res.data.data.item_data;
this.BillSumOldRoll = 0;
this.BillSumNewRoll = 0;
for (var i = 0; i < aResultData.length; i++) {
aResultData[i].roll = aResultData[i].roll / 100;
aResultData[i].check_roll = aResultData[i].check_roll / 100;
aResultData[i].different_roll = aResultData[i].different_roll / 100;
this.BillSumOldRoll = this.BillSumOldRoll + aResultData[i].roll;
this.BillSumNewRoll = this.BillSumNewRoll + aResultData[i].check_roll;
};
this.GoodsDetailList = aResultData;
},
})
},
// 审核按钮方法
commitBtnFun: function () {
if (this.BillMasterID <= 0) {
this.showError('当前单据未提交,不能审核或消审!');
return;
}
var aCommitRecallName = '审核';
if (this.CommitType == '') {
aCommitRecallName = '审核';
} else {
aCommitRecallName = '消审';
}
// if (this.CommitProcName == '') {
// this.playError();
// this.BillDataMessage = '当前' + aCommitRecallName + '操作配置有误,不能审核!';
// return;
// }
console.log('GoodsCheckBillID', this.GoodsCheckBillID)
util.request({
url: '/product/productCheckOrder/updateProductCheckOrderAuditStatusPass',
method: 'put',
data: {
'id': parseInt(this.GoodsCheckBillID),
},
success: (res) => {
console.log('审核', res)
if (res.data.code == '0') {
var aResultData = JSON.parse(res.data.data);
console.log('aResultData', aResultData)
if (aResultData.code == '0' && aResultData.msg ==
'success') {
this.playSuccess(); this.playSuccess();
this.GoodsCheckBillDetailData(); this.BillDataMessage = aCommitRecallName + "成功!";
this.BillDataMessage = res.data.msg;
this.QRBarCode = '';
}
else {
this.showError(res.data.msg);
this.QRBarCode = '';
}
},
fail: (error) => {
this.showError('连接服务器出错,请检查后台服务是否启动!');
console.log('error',error)
},
})
},
GoodsCheckBillDetailData: function() { if (aCommitRecallName == '审核') {
console.log("--aaa->>" + this.GoodsCheckBillID); this.CommitType = '已审核'
console.log("--aaa->>" + this.GoodsCheckBillID);
util.request({
url: '/product/productCheckOrder/getProductCheckOrder',
method: 'get',
data: {
'id': parseInt(this.GoodsCheckBillID),
},
success: (res) => {
console.log("-->>--" + JSON.stringify(res.data.data));
this.GoodsDetailList = [];
this.GoodsCheckBillNo = res.data.data.order_no;
this.StoreName = res.data.data.warehouse_name
this.StoreStationName = res.data.data.warehouse_bin_name;
this.GoodsCheckBillDate = this.$u.timeFormat(res.data.data.check_time, 'yyyy-mm-dd')
/* this.BillSumOldRoll = res.data.data.item_data.roll;
this.BillSumNewRoll = res.data.data.item_data.check_roll; */
var aResultData = res.data.data.item_data;
this.BillSumOldRoll = 0;
this.BillSumNewRoll = 0;
for (var i = 0; i < aResultData.length; i++) {
aResultData[i].roll = aResultData[i].roll / 100;
aResultData[i].check_roll = aResultData[i].check_roll / 100;
aResultData[i].different_roll = aResultData[i].different_roll / 100;
this.BillSumOldRoll = this.BillSumOldRoll + aResultData[i].roll;
this.BillSumNewRoll = this.BillSumNewRoll + aResultData[i].check_roll;
};
this.GoodsDetailList = aResultData;
},
})
},
// 审核按钮方法
commitBtnFun: function() {
if (this.BillMasterID <= 0) {
this.showError('当前单据未提交,不能审核或消审!');
return;
}
var aCommitRecallName = '审核';
if (this.CommitType == '') {
aCommitRecallName = '审核';
} else {
aCommitRecallName = '消审';
}
// if (this.CommitProcName == '') {
// this.playError();
// this.BillDataMessage = '当前' + aCommitRecallName + '操作配置有误,不能审核!';
// return;
// }
console.log('GoodsCheckBillID',this.GoodsCheckBillID)
util.request({
url: '/product/productCheckOrder/updateProductCheckOrderAuditStatusPass',
method: 'put',
data: {
'id': parseInt(this.GoodsCheckBillID),
},
success: (res) => {
console.log('审核',res)
if (res.data.code == '0') {
var aResultData = JSON.parse(res.data.data);
console.log('aResultData',aResultData)
if (aResultData.code == '0' && aResultData.msg ==
'success') {
this.playSuccess();
this.BillDataMessage = aCommitRecallName + "成功!";
if (aCommitRecallName == '审核') {
this.CommitType = '已审核'
} else {
this.CommitType = ''
}
} else { } else {
this.showError(aCommitRecallName + '出错!' + aResultData.BillDataMessage); this.CommitType = ''
return;
} }
} else { } else {
this.showError(aCommitRecallName + '出错,' + res.data.msg); this.showError(aCommitRecallName + '出错!' + aResultData.BillDataMessage);
return; return;
} }
}, } else {
fail: (error) => { this.showError(aCommitRecallName + '出错,' + res.data.msg);
this.showError('连接服务器出错,请检查后台服务是否启动!'); return;
}, }
}) },
}, fail: (error) => {
this.showError('连接服务器出错,请检查后台服务是否启动!');
},
})
},
}
} }
}
</script> </script>
<style> <style>
page { page {
background-color: #F8F8F8; background-color: #F8F8F8;
padding-bottom: 260rpx; padding-bottom: 260rpx;
} }
.u-radio { .u-radio {
width: 200rpx !important; width: 200rpx !important;
} }
.submitView { .submitView {
width: 100%; width: 100%;
padding: 16rpx 0 26rpx; padding: 16rpx 0 26rpx;
background-color: #FFFFFF; background-color: #FFFFFF;
position: fixed; position: fixed;
bottom: 0; bottom: 0;
left: 0; left: 0;
border-top: 1rpx solid #f1f1f1; border-top: 1rpx solid #f1f1f1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 100; z-index: 100;
} }
.submitBtn { .submitBtn {
width: 666rpx; width: 666rpx;
} }
.productBox { .productBox {
background-color: #FFFFFF; background-color: #FFFFFF;
margin-top: 32rpx; margin-top: 32rpx;
padding: 26rpx 0; padding: 26rpx 0;
} }
.tjcpName { .tjcpName {
width: 686rpx; width: 686rpx;
height: 40rpx; height: 40rpx;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
border-left: 6rpx solid #007AFF; border-left: 6rpx solid #007AFF;
padding-left: 12rpx; padding-left: 12rpx;
margin-left: 26rpx; margin-left: 26rpx;
margin-top: 26rpx; margin-top: 26rpx;
} }
.cpInput { .cpInput {
width: 150rpx !important; width: 150rpx !important;
margin-right: 12rpx; margin-right: 12rpx;
} }
.cpInput>input { .cpInput>input {
box-sizing: border-box; box-sizing: border-box;
border: 1rpx solid #DDDDDD; border: 1rpx solid #DDDDDD;
width: 100%; width: 100%;
height: 60rpx; height: 60rpx;
border-radius: 10rpx; border-radius: 10rpx;
padding: 0 10rpx; padding: 0 10rpx;
} }
.cpInput1 { .cpInput1 {
width: 200rpx !important; width: 200rpx !important;
margin-right: 12rpx; margin-right: 12rpx;
} }
.cpInput1>input { .cpInput1>input {
box-sizing: border-box; box-sizing: border-box;
border: 1rpx solid #DDDDDD; border: 1rpx solid #DDDDDD;
width: 100%; width: 100%;
height: 60rpx; height: 60rpx;
border-radius: 10rpx; border-radius: 10rpx;
padding: 0 10rpx; padding: 0 10rpx;
} }
.clearIcon { .clearIcon {
position: absolute; position: absolute;
right: 6rpx; right: 6rpx;
top: 6rpx; top: 6rpx;
} }
.greenPrice { .greenPrice {
font-size: 16px; font-size: 16px;
color: #19BE6B !important; color: #19BE6B !important;
font-weight: bold; font-weight: bold;
} }
.disFlex { .disFlex {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 8rpx; margin-bottom: 8rpx;
} }
.inputName { .inputName {
color: #ADADAD; color: #ADADAD;
font-size: 16px; font-size: 16px;
} }
.addHKQS { .addHKQS {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 16rpx 26rpx; padding: 16rpx 26rpx;
font-size: 15px; font-size: 15px;
font-weight: bold; font-weight: bold;
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
</style> </style>
@@ -18,7 +18,7 @@
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="ItemDataList.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加"> <dataNull v-if="ItemDataList.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
@@ -16,7 +16,7 @@
:current="activeTabIndex" @change="onTabChange"></u-tabs> :current="activeTabIndex" @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="ItemDataList.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加"> <dataNull v-if="ItemDataList.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"> refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray">
@@ -16,7 +16,7 @@
:current="activeTabIndex" @change="onTabChange"></u-tabs> :current="activeTabIndex" @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="ItemDataList.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关移仓资料" title1="请添加或者更换搜索添加"> <dataNull v-if="ItemDataList.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关移仓资料" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"> refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray">
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -14,7 +14,7 @@
</view> </view>
</view> </view>
<dataNull v-if="list.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关排产单" title1="请添加或者更换搜索添加"> <dataNull v-if="list.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关排产单" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="selectDataFun" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" @scrolltolower="selectDataFun"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray" refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"
@@ -6,7 +6,7 @@
:current="activeTabIndex" @change="onTabChange"></u-tabs> :current="activeTabIndex" @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="ItemDataList.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加"> <dataNull v-if="ItemDataList.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"> refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray">
+1 -1
View File
@@ -6,7 +6,7 @@
:current="activeTabIndex" @change="onTabChange"></u-tabs> :current="activeTabIndex" @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="ItemDataList.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关进仓资料" title1="请添加或者更换搜索添加"> <dataNull v-if="ItemDataList.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关进仓资料" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"> refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray">
+1 -1
View File
@@ -6,7 +6,7 @@
:current="activeTabIndex" @change="onTabChange"></u-tabs> :current="activeTabIndex" @change="onTabChange"></u-tabs>
</div> </div>
</u-sticky> </u-sticky>
<dataNull v-if="ItemDataList.length == 0" src="/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加"> <dataNull v-if="ItemDataList.length == 0" src="/src/static/img/chahua/gjNull.png" title="暂无相关出仓资料" title1="请添加或者更换搜索添加">
</dataNull> </dataNull>
<scroll-view v-else scroll-y="true" :style="{height: scrollHeight}" <scroll-view v-else scroll-y="true" :style="{height: scrollHeight}"
refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray"> refresher-enabled :refresher-threshold="200" :refresher-triggered="triggered" refresher-background="gray">
File diff suppressed because it is too large Load Diff
+1
View File
@@ -47,6 +47,7 @@ export default {
app_name: config.upgradeName, app_name: config.upgradeName,
current_version: appInfo.version current_version: appInfo.version
}) })
console.log('res',res)
if(!res.download_url){ if(!res.download_url){
this.$u.toast('当前版本已是最新版'); this.$u.toast('当前版本已是最新版');
return return
+37 -128
View File
@@ -3,12 +3,7 @@
<common-navbar title="工作台" :showBack="false"> <common-navbar title="工作台" :showBack="false">
<template #right> <template #right>
<view class="scan_box"> <view class="scan_box">
<u-icon <u-icon name="scan" color="#333333" size="46" @click="handleScan"></u-icon>
name="scan"
color="#333333"
size="46"
@click="handleScan"
></u-icon>
</view> </view>
</template> </template>
</common-navbar> </common-navbar>
@@ -21,10 +16,7 @@
</view> </view>
<view class="grid-text">成品配布</view> <view class="grid-text">成品配布</view>
</u-grid-item> </u-grid-item>
<u-grid-item <u-grid-item :index="1" @click="navTo('/pages/storegoods/QRBarCodeReview')">
:index="1"
@click="navTo('/pages/storegoods/QRBarCodeReview')"
>
<view class="home-icon icon-color02"> <view class="home-icon icon-color02">
<i class="iconfont icon-mall-bag"></i> <i class="iconfont icon-mall-bag"></i>
</view> </view>
@@ -35,44 +27,31 @@
<view class="workbench-title">成品管理</view> <view class="workbench-title">成品管理</view>
<view class="toolbar"> <view class="toolbar">
<u-grid class="grid" :col="4" :border="false"> <u-grid class="grid" :col="4" :border="false">
<u-grid-item <u-grid-item :index="0" @click="navTo('/pages/storegoods/dyeworksDyeback')">
:index="0"
@click="navTo('/pages/storegoods/dyeworksDyeback')"
>
<view class="home-icon icon-color04"> <view class="home-icon icon-color04">
<i class="iconfont icon-finance"></i> <i class="iconfont icon-finance"></i>
</view> </view>
<view class="grid-text">染整进仓</view> <view class="grid-text">染整进仓</view>
</u-grid-item> </u-grid-item>
<u-grid-item <u-grid-item :index="1" @click="navTo('/pages/storegoods/storeGoodsBusinessIn')">
:index="1"
@click="navTo('/pages/storegoods/storeGoodsBusinessIn')"
>
<view class="home-icon icon-color03"> <view class="home-icon icon-color03">
<i class="iconfont icon-mall-bag"></i> <i class="iconfont icon-mall-bag"></i>
</view> </view>
<view class="grid-text">成品进仓</view> <view class="grid-text">成品进仓</view>
</u-grid-item> </u-grid-item>
<u-grid-item <u-grid-item :index="2" @click="navTo('/pages/storegoods/storeGoodsBusinessOut')">
:index="2"
@click="navTo('/pages/storegoods/storeGoodsBusinessOut')"
>
<view class="home-icon icon-color12"> <view class="home-icon icon-color12">
<i class="iconfont icon-baoxiaodan"></i> <i class="iconfont icon-baoxiaodan"></i>
</view> </view>
<view class="grid-text">成品出仓</view> <view class="grid-text">成品出仓</view>
</u-grid-item> </u-grid-item>
<u-grid-item <u-grid-item @click="navTo('/pages/storegoods/storeGoodsBusinessCheck')">
@click="navTo('/pages/storegoods/storeGoodsBusinessCheck')"
>
<view class="home-icon icon-color04"> <view class="home-icon icon-color04">
<i class="iconfont icon-shenpi"></i> <i class="iconfont icon-shenpi"></i>
</view> </view>
<view class="grid-text">成品盘点</view> <view class="grid-text">成品盘点</view>
</u-grid-item> </u-grid-item>
<u-grid-item <u-grid-item @click="navTo('/pages/storegoods/storeGoodsBusinessStationMoveOnly')">
@click="navTo('/pages/storegoods/storeGoodsBusinessStationMoveOnly')"
>
<view class="home-icon icon-color04"> <view class="home-icon icon-color04">
<i class="iconfont icon-shenpi"></i> <i class="iconfont icon-shenpi"></i>
</view> </view>
@@ -151,6 +130,7 @@
</template> </template>
<script> <script>
import CommonNavbar from "@/components/common-navbar/index"; import CommonNavbar from "@/components/common-navbar/index";
import scanMixin from '@/common/scanMixin.js';
/** /**
* Copyright (c) 2013-Now http://aidex.vip All rights reserved. * Copyright (c) 2013-Now http://aidex.vip All rights reserved.
*/ */
@@ -158,6 +138,7 @@ export default {
components: { components: {
CommonNavbar, CommonNavbar,
}, },
mixins: [scanMixin],
data() { data() {
return { return {
show: false, show: false,
@@ -168,34 +149,17 @@ export default {
//{image: '/static/aidex/banner/banner03.png'} //{image: '/static/aidex/banner/banner03.png'}
], ],
todoCount: 3, todoCount: 3,
scanReceiver: null, // scanReceiver 和 isPageActive 已由 scanMixin 提供
isPageActive: false,
}; };
}, },
onLoad() { onLoad() {
// #ifdef APP-PLUS // #ifdef APP-PLUS
this.isPageActive = true; // 使用 scanMixin 的方法注册扫码广播,传入自定义回调
this.registerScanBroadcast(); this.registerScanBroadcast(this.handleScanCode);
// #endif // #endif
}, },
// onUnload, onHide, onShow 已由 scanMixin 自动处理
onUnload() {
// #ifdef APP-PLUS
this.isPageActive = false;
this.unregisterBroadcast();
// #endif
},
onHide() {
// 页面隐藏时
this.isPageActive = false;
},
onShow() {
// 页面显示时
this.isPageActive = true;
},
computed: { computed: {
contentStyle() { contentStyle() {
@@ -206,6 +170,15 @@ export default {
}, },
}, },
methods: { methods: {
// 自定义扫码处理回调 - 处理本页面特有的扫码逻辑
handleScanCode(scanResult) {
console.log('工作台-扫码结果:', scanResult);
const cleanResult = scanResult.trim().replace(/[\r\n]/g, '');
uni.navigateTo({
url: '/pages/saleship/salepickscandetail?order_no=' + cleanResult
});
},
navTo(url) { navTo(url) {
uni.navigateTo({ uni.navigateTo({
url: url, url: url,
@@ -217,11 +190,11 @@ export default {
itemClick(index) { itemClick(index) {
console.log(index); console.log(index);
}, },
// 处理扫码 // 处理扫码(手动点击扫码按钮)
handleScan() { handleScan() {
// uni.navigateTo({ // uni.navigateTo({
// url: '/pages/saleship/salepickscandetail?order_no=FPD-PB-202412270196' // url: '/pages/saleship/salepickscandetail?order_no=FPD-PB-202412270196'
// }) // })
// return // return
// #ifdef APP-PLUS || MP-WEIXIN // #ifdef APP-PLUS || MP-WEIXIN
uni.scanCode({ uni.scanCode({
@@ -229,9 +202,9 @@ export default {
success: (res) => { success: (res) => {
console.log('扫码成功:', res); console.log('扫码成功:', res);
const cleanResult = res.result.trim().replace(/[\r\n]/g, ''); const cleanResult = res.result.trim().replace(/[\r\n]/g, '');
uni.navigateTo({ uni.navigateTo({
url: '/pages/saleship/salepickscandetail?order_no=' + cleanResult url: '/pages/saleship/salepickscandetail?order_no=' + cleanResult
}) })
}, },
fail: (err) => { fail: (err) => {
console.error('扫码失败:', err); console.error('扫码失败:', err);
@@ -275,99 +248,32 @@ export default {
}); });
} }
}, },
// 注册扫码广播 // registerScanBroadcast 和 unregisterScanBroadcast 已由 scanMixin 提供
registerScanBroadcast() {
try {
console.log('开始注册扫码广播');
const main = plus.android.runtimeMainActivity();
// 配置扫码枪广播
try {
const Intent = plus.android.importClass('android.content.Intent');
const intent = new Intent('com.android.scanner.service_settings');
intent.putExtra('action_barcode_broadcast', 'com.android.server.scannerservice.broadcast');
intent.putExtra('key_barcode_broadcast', 'scannerdata');
main.sendBroadcast(intent);
console.log('扫码枪广播配置已发送');
} catch (error) {
console.error('配置扫码枪广播失败:', error);
}
// 注册广播接收器
const IntentFilter = plus.android.importClass('android.content.IntentFilter');
const filter = new IntentFilter();
filter.addAction('com.android.server.scannerservice.broadcast');
const receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
onReceive: (context, intent) => {
// 只有当页面活动时才处理广播
if (!this.isPageActive) return;
try {
console.log("Workbench intent:", intent);
const scanResult = intent.getStringExtra('scannerdata');
console.log('工作台-扫码结果:', scanResult);
if (scanResult) {
this.$nextTick(() => {
const cleanResult = scanResult.trim().replace(/[\r\n]/g, '');
uni.navigateTo({
url: '/pages/saleship/salepickscandetail?order_no=' + cleanResult
});
});
}
} catch (error) {
console.error('处理广播数据时出错:', error, error.stack);
}
}
});
// 注册广播接收器
main.registerReceiver(receiver, filter);
this.scanReceiver = receiver;
console.log('扫码广播注册成功,等待扫码...');
} catch (error) {
console.error('注册扫码广播失败:', error);
console.error('错误详情:', error.message);
console.error('错误堆栈:', error.stack);
}
},
// 注销广播接收器
unregisterBroadcast() {
if (this.scanReceiver) {
try {
const main = plus.android.runtimeMainActivity();
main.unregisterReceiver(this.scanReceiver);
this.scanReceiver = null;
console.log('扫码广播注销成功');
} catch (error) {
console.error('注销扫码广播失败:', error);
}
}
},
}, },
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
@import "index.scss"; @import "index.scss";
.scan_box { .scan_box {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.scan_text { .scan_text {
font-size: 12px; font-size: 12px;
color: #2979ff; color: #2979ff;
} }
} }
.banner-box { .banner-box {
padding: 0 2%; padding: 0 2%;
width: 96%; width: 96%;
height: 170rpx; height: 170rpx;
margin: 30rpx 0 30rpx; margin: 30rpx 0 30rpx;
} }
.u-swiper-wrap { .u-swiper-wrap {
padding: 0 10px; padding: 0 10px;
} }
@@ -378,6 +284,7 @@ export default {
display: inline-block; display: inline-block;
margin: 0 1.5%; margin: 0 1.5%;
} }
.banner-pic image { .banner-pic image {
width: 100%; width: 100%;
height: 170rpx; height: 170rpx;
@@ -394,12 +301,14 @@ export default {
position: relative; position: relative;
top: -3px; top: -3px;
} }
.workbench-title { .workbench-title {
font-size: 32rpx; font-size: 32rpx;
font-weight: bold; font-weight: bold;
color: #333333; color: #333333;
padding: 15px 30rpx; padding: 15px 30rpx;
} }
.home-icon i.icon-tongzhi { .home-icon i.icon-tongzhi {
font-size: 22px; font-size: 22px;
} }
+850 -1
View File
@@ -4,7 +4,7 @@
* 使用的时候,请将下面的一行复制到您的uniapp项目根目录的uni.scss中即可 * 使用的时候,请将下面的一行复制到您的uniapp项目根目录的uni.scss中即可
* uView自定义的css类名和scss变量,均以"u-"开头,不会造成冲突,请放心使用 * uView自定义的css类名和scss变量,均以"u-"开头,不会造成冲突,请放心使用
*/ */
@import 'uview-ui/theme.scss';
$u-main-color: #303133; $u-main-color: #303133;
$u-content-color: #505256; $u-content-color: #505256;
$u-tips-color: #909399; $u-tips-color: #909399;
@@ -39,3 +39,852 @@ $u-type-info-light: #f4f4f5;
$u-form-item-height: 70rpx; $u-form-item-height: 70rpx;
$u-form-item-border-color: #dcdfe6; $u-form-item-border-color: #dcdfe6;
.uni-tabbar{
box-shadow: 0px 0px 10px rgba(0,0,0,0.15);
}
.uni-page-head{
background: #ffffff!important;
}
.form_right{
.u-form-item__message{
text-align: right;
}
.u-input{
textarea{
text-align: left!important;
}
}
.uni-input-input{
text-align: right;
}
}
.yjfk-form-right{
.uni-input-input{
text-align: right;
}
}
.input-left-form{
.input-placeholder{
text-align: left!important;
}
.uni-input-input{
text-align: left!important;
}
}
.pages-sys-user-index{
.uni-page-head{
background: #5b95ff!important;
color: #ffffff!important;
}
}
.pages-sys-login-index{
.uni-page-head{
display: none;
}
.uni-placeholder{
height: 0!important;
}
}
.home-icon {
width: 48px;
height: 48px;
align-items: center;
border-radius: 6px;
box-sizing: border-box;
display: flex;
justify-content: center;
color:#ffffff;
font-size: 26px;
}
.home-icon i{
font-size: 26px;
}
.icon-color01{
background: #ef8e35;
border:1rpx solid #ef8e35;
}
.icon-color02{
background-image: linear-gradient(0deg, #5a9aec, #84acf7);
border:1rpx solid #6893d5;
}
.icon-color03{
background-image: linear-gradient(0deg, #5fceb3, #61d2ba);
border:1rpx solid #58c1ab;
}
.icon-color03{
background-image: linear-gradient(0deg, #58ca93, #58ca93);
border:1rpx solid #58c1ab;
}
.icon-color04{
background-image: linear-gradient(0deg, #4f77ed, #4f77ed);
border:1rpx solid #4f77ed;
}
.icon-color05{
background-image: linear-gradient(0deg, #eb516b, #eb516c);
border:1rpx solid #cc4962;
}
.icon-color06{
background-image: linear-gradient(0deg, #F35643, #F35643);
border:1rpx solid #F35643;
}
.icon-color07{
background-image: linear-gradient(0deg, #4b86f6, #4b86f6);
border:1rpx solid #4e7fd8;
}
.icon-color08{
background-image: linear-gradient(0deg, #f2a953, #f2a953);
border:1rpx solid #d2954c;
}
.icon-color09{
background-image: linear-gradient(-30deg, #3b6eef, #3b6eef);
border:1rpx solid #3663d1;
}
.icon-color10{
background-image: linear-gradient(0deg, #59bef7, #58bcf6);
border:1rpx solid #4ea6da;
}
.icon-color11{
background:#ffffff;
border:1rpx solid #ddddde;
}
.icon-color12{
background-image: linear-gradient(0deg, #FFC60A, #FFC60A);
border:1rpx solid #FFC60A;
}
.icon-color13{
background:#f5f6f7;
border:1rpx dashed #e1e1e3;
}
.home-icon{
position: relative;
.u-badge{
top:-8px!important;
right:-8px!important;
padding:1px 4px!important;
}
}
/* .grid-height270 .u-collapse-body{
height: 270px!important;
} */
.u-grid-item-box{
padding: 0 0 12px!important;
}
.pages-sys-login-code .u-box{
border: 1px solid #dcdee1!important;
}
/* 首页样式开始 */
.home-head{
margin-left: 10px;
}
.home-head img, .home-head uni-image{
width: 32px!important;
height: 32px!important;
}
.wrap-home .u-slot-right{
margin-right: 20rpx!important;
}
.wrap-home .list .u-cell-item-box .u-cell_title{
width: auto!important;
min-width:auto!important ;
color: #202328;
}
.wrap-home .u-navbar:after{
border-bottom-width: 0px!important;
}
.side-personal-list .u-cell{
padding: 12px 16px;
}
.side-personal-list .u-cell .u-iconfont{
font-size: 20px!important;
}
.side-info .u-border-top:after, .side-info .u-border-bottom:after{
border-bottom-width: 0;
border-top-width: 0;
}
.side-personal-list .u-border-top:after{
border-bottom-width: 0;
border-top-width: 0;
}
.u-mask-zoom{
background-color: rgba(0, 0, 0, 0.5)!important;
}
.wrap-home .u-title{
font-weight: bold!important;
color: #000000!important;
}
.wrap-home .u-cell__value{
text-align: left!important;
}
/* 首页样式结束 */
/* 物联中心样式开始 */
.pages-sys-application-index .u-slot-right{
margin-right: 20rpx!important;
}
.pages-sys-application-index .u-title{
font-weight: bold!important;
color: #000000!important;
}
.pages-sys-application-index .u-navbar:after {
border-bottom-width: 0px!important;
}
.apply-topbox{
background: #ffffff;
padding: 10px 0;
}
.apply-box{
padding: 10px 0 10px;
position: relative;
.u-image{
position: absolute;
left: 10px;
}
.u-image .u-image__image{
border-radius: 50%!important;
border: 1px solid #ededed;
box-shadow:0 3px 3px rgba(#000000, 0.2);
}
.u-size-default{
padding:2px 5px!important;
margin: 0 10rpx!important;
}
.other-title{
color: #999999;
}
.u-row{
padding-left: 10px;
}
}
.Statistics-box{
padding: 15px;
background: #ffffff;
.title-text{
color: #000000;
font-size: 32rpx;
text-align: center;
border-bottom: 1px solid #ededed;
padding-bottom: 10px;
margin-bottom: 5px;
}
.other-text{
color: #666666;
font-size: 28rpx;
text-align: left;
padding: 5px 0;
}
.other-text02{
color: #9c9c9c;
font-size: 24rpx;
padding: 5px 0;
em{
font-style: normal;
color: #5473e8;
}
}
.u-active{
background-image: linear-gradient(90deg, #8271ff, #65cbff);
}
}
/* 物联中心样式结束 */
/* 套餐详情开始 */
.details-list-item {
margin:20rpx 0 0!important;
.u-card__body{
padding: 0!important;
}
.u-body-item{
padding: 15px!important;
.header-text{
color: #000000;
font-size: 30rpx;
}
.other-itemtext{
color: #999999;
font-size: 20rpx;
em{
font-style: normal;
color: #ff4400;
}
}
}
}
/* 套餐详情结束 */
/* 套餐列表开始 */
.order-list-item {
margin:20rpx 0 0!important;
uni-image{
width: 130rpx;
height: 130rpx;
margin-right: 10rpx;
}
.u-card__body{
padding: 0!important;
}
.u-body-item{
padding: 15px!important;
background: #ffffff;
.u-body-item-title{
color: #000000;
font-size: 30rpx;
}
.other-itemtext{
color: #999999;
font-size: 20rpx;
}
.money-itemtext{
color: #ff4400;
font-size: 20rpx;
em{
font-style: normal;
color: #ff4400;
font-size: 36rpx;
}
}
}
}
/* 套餐列表结束 */
/* 套餐购买详情 */
.buy-box{
padding:0 15px 10px;
background: #ffffff;
.remind-text{
font-size: 12px;
color: #ff4300;
}
}
.payment-box{
background: #ffffff;
.u-form-item--left .u-icon{
font-size: 32px;
margin-top: 8px;
}
.uicon-weixin-circle-fill{
color: #36c956;
}
.uicon-zhifubao-circle-fill{
color: #06b4fd;
}
.u-form-item{
padding:5px 10px!important;
}
}
.pay-box{
background: #ffffff;
padding:0 0 15px;
.u-form-item--left .u-icon{
font-size: 32px;
margin-top: 8px;
color: #2e2e2e;
}
.u-btn{
padding: 0px 20px!important;
}
.u-form-item{
padding:5px 10px!important;
}
.uni-input-input{
font-size: 32px;
color: #2e2e2e;
}
}
.bottom-box{
position: fixed;
z-index: 99;
background: #ffffff;
height: 49px;
bottom:0;
width: 100%;
border-top: 1px solid #ededed;
.number-text{
color: #333333;
font-size: 20rpx;
em{
font-style: normal;
color: #ff4400;
font-size: 36rpx;
}
}
}
/* 套餐购买详情 */
.amount-top-box{
padding: 10px 15px;
background: #ffffff;
.amount-text{
color: #333333;
font-size: 30rpx;
}
.number-text{
color: #333333;
font-size: 60rpx;
padding-top: 10rpx;
float: left;
em{
font-style: normal;
color: #333333;
font-size: 20rpx;
}
}
.u-size-mini{
margin: 16px 0 0 10px;
}
}
.text-spacing{
color: #333333;
font-size: 30rpx;
padding: 10rpx 15px;
}
.u-cell-title{
padding: 10px 16px;
color: #333333;
}
.pay-right-box{
text-align: right;
height: 48px;
.number{
font-size: 40rpx;
color: #333333;
padding: 5px 0;
}
.up{
color: #ff7000;
}
.other{
font-size: 24rpx;
color: #999999;
padding: 3px 0 0;
}
}
/* 联系客服开始 */
.service-banner{
padding: 15px 15px 0;
uni-image{
height: 155px;
width: 100%;
border-radius: 8px;
}
}
.service-box{
padding:0 15px;
.u-cell__left-icon-wrap .u-icon__icon{
font-size: 32px!important;
}
.u-cell_title{
border-left: 1px solid #d7d4d4;
padding-left: 10px;
}
.uicon-phone-fill{
color: #f3ae45;
}
.uicon-weixin-circle-fill{
color: #36c956;
}
.uicon-qq-circle-fill{
color: #23a0f0;
}
.u-cell{
padding:10px!important;
border-radius: 8px;
border: 1px solid #e4e7ed!important;
box-shadow: 0px 0px 10px rgba(0,0,0,0.15);
margin: 0 0 15px;
}
.u-border-bottom:after {
border-bottom-width: 0px;
}
.u-border-top:after {
border-top-width: 0px;
}
}
/* 联系客服结束 */
/* 个人中心开始 */
.user-banner{
padding: 0px 15px;
uni-image{
height: 100px;
width: 100%;
border-radius: 8px;
}
}
.personal-list .u-cell{
padding: 12px 16px;
}
.personal-list .u-cell-box .u-cell_title{
color: #202328;
}
.personal-list .u-cell .u-iconfont{
font-size: 20px!important;
}
/* 个人中心结束 */
.u-cell-title{
padding: 10px 16px!important;
color: #333333!important;
}
/* 全部订单列表开始 */
.order-list-item{
.u-card__head{
padding: 8px 15px!important;
}
.u-body-item-title{
width: 100%;
}
.u-card__head--left__title{
color: #9d9d9d!important;
font-size: 12px!important;
}
.u-card__head--right .u-card__head__title__text{
color: #ff7001!important;
font-size: 16px!important;
}
.u-card__foot{
padding: 8px 15px!important;
font-size: 12px!important;
}
.order-list-foot{
height: 25px;
line-height: 25px;
}
.order-list-foot-btn{
float: right;
.u-btn{
margin: 0 2px;
border: 1px solid #e6e6e6!important;
}
}
}
/* 全部订单列表结束 */
/* 订单详情开始 */
.pages-sys-application-order-detailed{
.title-text{
color: #333333;
font-size: 15px;
padding:10px 15px;
background: #ffffff;
}
.detailed-information{
background: #ffffff;
font-size: 12px;
color: #1f1f1f;
.u-col{
padding: 8px 15px!important;
}
}
}
/* 订单详情结束 */
/* 代理商样式开始 */
.agent-box{
padding:10px;
.u-col{
padding:0 3px!important;
}
.u-card{
margin:0px!important;
.u-card__head{
padding: 8px 10px 0!important;
.iconfont{
float: left;
margin-right: 5px;
}
}
.u-card__body{
padding:27px 5px!important;
.agent-text{
color: #ff7001;
font-size: 16px;
font-weight: 700;
}
.agent-other-text{
color: #999999;
font-size: 12px;
}
}
}
.agent-box-right{
.u-card__body {
padding: 5px 10px!important;
span{
margin: 0 5px;
color: #999999;
font-size: 12px;
em{
font-style: normal;
color: #ff7001;
font-size: 14px;
margin: 0 5px;
font-weight: 700;
}
}
}
}
.u-border-bottom:after {
border-bottom-width: 0px;
}
}
/* 代理商样式结束 */
/* 我的利益样式开始 */
.earning-list-item{
margin: 0!important;
.u-card__head{
padding: 8px 15px!important;
}
.u-card__body{
padding: 10px 15px 0!important;
}
.u-body-item-title{
width: 100%;
font-size: 12px;
color: #999;
.type-name{
font-size: 16px;
font-weight: 500;
color: #000;
line-height: 2;
}
.type-money{
font-size: 18px;
font-weight: 500;
color: #ff7001;
line-height: 2;
}
}
.u-card__head--left__title{
color: #000000!important;
font-size: 16px!important;
}
.u-card__head--right .u-card__head__title__text{
color: #999999!important;
font-size: 12px!important;
}
.u-card__foot{
padding: 5px 15px 10px!important;
font-size: 12px!important;
}
.earning-list-foot{
line-height: 20px;
}
.u-card__foot:after {
border-top-width: 0px!important;
}
.earning-headleft{
font-size: 16px;
color: #000;
line-height: 28px;
.iconfont{
margin: 5px 5px 0 0;
float: left;
}
}
.earning-headright{
font-size: 12px;
color: #999;
}
}
/* 我的利益样式结束 */
/* 地址管理样式开始 */
.address-item{
margin: 0!important;
.u-card__head{
padding: 10px 15px 0!important;
.u-flex{
justify-content: start;
}
}
.u-card__head:after {
border-bottom-width: 0px!important;
}
.u-card__body{
color: #999999!important;
}
.u-card__foot{
padding: 10px 15px!important;
}
.address-foot{
justify-content: space-between;
.u-radio{
.u-radio__label{
font-size: 12px!important;
}
}
.u-icon{
margin: 0 5px;
}
.u-icon__label{
font-size: 12px!important;
}
}
}
.pages-sys-user-edit-address .u-form-item{
padding:10px 15px!important;
}
/* 地址管理样式结束 */
.sign-out{
margin:0;
border-radius: 0;
border: 0!important;
color: #ff4141!important;
background: #fff;
}
/* 批量充值样式 */
.pages-sys-user-batch-recharge .u-form-item{
padding:10px 15px!important;
}
.batch-recharge-box{
background: #ffffff;
}
.batch-recharge-title{
padding: 10px 15px;
font-size: 30rpx;
background: #ffffff;
color: #333333;
em{
font-style: normal;
color: #ff4300;
}
}
.result-text{
em{
font-style: normal;
color: #ff4300;
}
}
.card-list-item{
background: #ffffff;
}
.card-list-item .u-form-item{
padding: 5px 15px!important;
}
.remind-text {
font-size: 12px;
color: #ff4300;
}
/* 提现样式记录 */
.pages-sys-application-cash-out-record .u-input__input{
min-height: 25px!important;
}
/* 消息列表页面样式 */
.msg-list-item{
.u-card__head--left__thumb{
width: 22px!important;
height:22px!important;
}
}
/* 消息列表页面样式 */
.u-divider{
background: transparent!important;
padding: 10px 0!important;
}
.pages-sys-book-index{
.u-index-bar__sidebar{
top:55%!important;
}
}
.pages-sys-book-personal-details{
.u-icon{
color: #999999;
}
.u-cell_title{
color: #999999;
}
.u-cell__value{
text-align: left!important;
color: #000000!important;
font-size: 30rpx!important;
}
.connect{
.u-cell__value{
color: #5b95ff!important;
}
}
}
.apply-form-field{
background: #ffffff;
.u-form-item{
padding:5px 10px!important;
}
.input-placeholder{
text-align: right!important;
}
.uni-input-input{
text-align: right!important;
}
.form-field-select{
.u-iconfont{
font-size: 0!important;
}
}
.add-user{
color: #bfbfbf;
font-size: 30px;
}
.apply-name{
color: #333333;
line-height: 20px;
span{
color: #999999;
display: block;
}
}
.apply-link{
.u-link{
color: #5ab149!important;
padding: 0 5px;
}
}
.u-close--top-right{
top:5px!important;
}
.popup-title{
color: #333333;
padding:5px 10px;
font-size: 30rpx;
}
.u-cell{
padding:8px 12px!important;
}
}
/* 申请详情页面 */
.datails-examine{
.u-cell{
padding: 15rpx 40rpx!important;
}
}
.pages-sys-msg-details{
.u-time-axis {
position: relative;
background: #fff;
padding: 15px 10px 10px 70px!important;
}
.u-time-axis-node{
left: -20px!important;
}
.u-time-axis::before {
left: 45px!important;
top:15px!important;
border-left: 2px solid #ddd!important;
}
.u-time-axis-item{
min-height: 40px;
}
}
.custom-header{
.u-title{
font-size: 16px!important;
color: #000!important;
font-weight:700!important;
}
}
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 www.uviewui.com
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.
-106
View File
@@ -1,106 +0,0 @@
<p align="center">
<img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;">
</p>
<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView</h3>
<h3 align="center">多平台快速开发的UI框架</h3>
## 说明
uView UI,是[uni-app](https://uniapp.dcloud.io/)生态优秀的UI框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水
## 特性
- 兼容安卓,iOS,微信小程序,H5,QQ小程序,百度小程序,支付宝小程序,头条小程序
- 60+精选组件,功能丰富,多端兼容,让您快速集成,开箱即用
- 众多贴心的JS利器,让您飞镖在手,召之即来,百步穿杨
- 众多的常用页面和布局,让您专注逻辑,事半功倍
- 详尽的文档支持,现代化的演示效果
- 按需引入,精简打包体积
## 安装
```bash
# npm方式安装
npm i uview-ui
```
## 快速上手
1. `main.js`引入uView库
```js
// main.js
import uView from 'uview-ui';
Vue.use(uView);
```
2. `App.vue`引入基础样式(注意style标签需声明scss属性支持)
```css
/* App.vue */
<style lang="scss">
@import "uview-ui/index.scss";
</style>
```
3. `uni.scss`引入全局scss变量文件
```css
/* uni.scss */
@import "uview-ui/theme.scss";
```
4. `pages.json`配置easycom规则(按需引入)
```js
// pages.json
{
"easycom": {
// npm安装的方式不需要前面的"@/",下载安装的方式需要"@/"
// npm安装方式
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
// 下载安装方式
// "^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
},
// 此为本身已有的内容
"pages": [
// ......
]
}
```
请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容
## 使用方法
配置easycom规则后,自动按需引入,无需`import`组件,直接引用即可。
```html
<template>
<u-button>按钮</u-button>
</template>
```
请通过[快速上手](https://uviewui.com/components/quickstart.html)了解更详细的内容
## 链接
- [官方文档](https://uviewui.com/)
- [更新日志](https://uviewui.com/components/changelog.html)
- [升级指南](https://uviewui.com/components/changelog.html)
- [关于我们](https://uviewui.com/cooperation/about.html)
## 预览
您可以通过**微信**扫码,查看最佳的演示效果。
<br>
<br>
<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" >
<!-- ## 捐赠uView的研发
uView文档和源码全部开源免费,如果您认为uView帮到了您的开发工作,您可以捐赠uView的研发工作,捐赠无门槛,哪怕是一杯可乐也好(相信这比打赏主播更有意义)。
<img src="https://uviewui.com/common/wechat.png" width="220" >
<img style="margin-left: 100px;" src="https://uviewui.com/common/alipay.png" width="220" >
-->
## 版权信息
uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uView应用到您的产品中。
@@ -1,190 +0,0 @@
<template>
<u-popup mode="bottom" :border-radius="borderRadius" :popup="false" v-model="value" :maskCloseAble="maskCloseAble"
length="auto" :safeAreaInsetBottom="safeAreaInsetBottom" @close="popupClose" :z-index="uZIndex">
<view class="u-tips u-border-bottom" v-if="tips.text" :style="[tipsStyle]">
{{tips.text}}
</view>
<block v-for="(item, index) in list" :key="index">
<view
@touchmove.stop.prevent
@tap="itemClick(index)"
:style="[itemStyle(index)]"
class="u-action-sheet-item u-line-1"
:class="[index < list.length - 1 ? 'u-border-bottom' : '']"
:hover-stay-time="150"
>
<text>{{item.text}}</text>
<text class="u-action-sheet-item__subtext u-line-1" v-if="item.subText">{{item.subText}}</text>
</view>
</block>
<view class="u-gab" v-if="cancelBtn">
</view>
<view @touchmove.stop.prevent class="u-actionsheet-cancel u-action-sheet-item" hover-class="u-hover-class"
:hover-stay-time="150" v-if="cancelBtn" @tap="close">{{cancelText}}</view>
</u-popup>
</template>
<script>
/**
* actionSheet 操作菜单
* @description 本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI,配置更加灵活,所有平台都表现一致。
* @tutorial https://www.uviewui.com/components/actionSheet.html
* @property {Array<Object>} list 按钮的文字数组,见官方文档示例
* @property {Object} tips 顶部的提示文字,见官方文档示例
* @property {String} cancel-text 取消按钮的提示文字
* @property {Boolean} cancel-btn 是否显示底部的取消按钮(默认true)
* @property {Number String} border-radius 弹出部分顶部左右的圆角值,单位rpx(默认0)
* @property {Boolean} mask-close-able 点击遮罩是否可以关闭(默认true)
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
* @property {Number String} z-index z-index值(默认1075)
* @property {String} cancel-text 取消按钮的提示文字
* @event {Function} click 点击ActionSheet列表项时触发
* @event {Function} close 点击取消按钮时触发
* @example <u-action-sheet :list="list" @click="click" v-model="show"></u-action-sheet>
*/
export default {
name: "u-action-sheet",
props: {
// 点击遮罩是否可以关闭actionsheet
maskCloseAble: {
type: Boolean,
default: true
},
// 按钮的文字数组,可以自定义颜色和字体大小,字体单位为rpx
list: {
type: Array,
default () {
// 如下
// return [{
// text: '确定',
// color: '',
// fontSize: ''
// }]
return [];
}
},
// 顶部的提示文字
tips: {
type: Object,
default () {
return {
text: '',
color: '',
fontSize: '26'
}
}
},
// 底部的取消按钮
cancelBtn: {
type: Boolean,
default: true
},
// 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距
safeAreaInsetBottom: {
type: Boolean,
default: false
},
// 通过双向绑定控制组件的弹出与收起
value: {
type: Boolean,
default: false
},
// 弹出的顶部圆角值
borderRadius: {
type: [String, Number],
default: 0
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
},
// 取消按钮的文字提示
cancelText: {
type: String,
default: '取消'
}
},
computed: {
// 顶部提示的样式
tipsStyle() {
let style = {};
if (this.tips.color) style.color = this.tips.color;
if (this.tips.fontSize) style.fontSize = this.tips.fontSize + 'rpx';
return style;
},
// 操作项目的样式
itemStyle() {
return (index) => {
let style = {};
if (this.list[index].color) style.color = this.list[index].color;
if (this.list[index].fontSize) style.fontSize = this.list[index].fontSize + 'rpx';
// 选项被禁用的样式
if (this.list[index].disabled) style.color = '#c0c4cc';
return style;
}
},
uZIndex() {
// 如果用户有传递z-index值,优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
}
},
methods: {
// 点击取消按钮
close() {
// 发送input事件,并不会作用于父组件,而是要设置组件内部通过props传递的value参数
// 这是一个vue发送事件的特殊用法
this.popupClose();
this.$emit('close');
},
// 弹窗关闭
popupClose() {
this.$emit('input', false);
},
// 点击某一个item
itemClick(index) {
// disabled的项禁止点击
if(this.list[index].disabled) return;
this.$emit('click', index);
this.$emit('input', false);
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-tips {
font-size: 26rpx;
text-align: center;
padding: 34rpx 0;
line-height: 1;
color: $u-tips-color;
}
.u-action-sheet-item {
@include vue-flex;;
line-height: 1;
justify-content: center;
align-items: center;
font-size: 32rpx;
padding: 34rpx 0;
flex-direction: column;
}
.u-action-sheet-item__subtext {
font-size: 24rpx;
color: $u-tips-color;
margin-top: 20rpx;
}
.u-gab {
height: 12rpx;
background-color: rgb(234, 234, 236);
}
.u-actionsheet-cancel {
color: $u-main-color;
}
</style>
@@ -1,256 +0,0 @@
<template>
<view class="u-alert-tips" v-if="show" :class="[
!show ? 'u-close-alert-tips': '',
type ? 'u-alert-tips--bg--' + type + '-light' : '',
type ? 'u-alert-tips--border--' + type + '-disabled' : '',
]" :style="{
backgroundColor: bgColor,
borderColor: borderColor
}">
<view class="u-icon-wrap">
<u-icon v-if="showIcon" :name="uIcon" :size="description ? 40 : 32" class="u-icon" :color="uIconType" :custom-style="iconStyle"></u-icon>
</view>
<view class="u-alert-content" @tap.stop="click">
<view class="u-alert-title" :style="[uTitleStyle]">
{{title}}
</view>
<view v-if="description" class="u-alert-desc" :style="[descStyle]">
{{description}}
</view>
</view>
<view class="u-icon-wrap">
<u-icon @click="close" v-if="closeAble && !closeText" hoverClass="u-type-error-hover-color" name="close" color="#c0c4cc"
:size="22" class="u-close-icon" :style="{
top: description ? '18rpx' : '24rpx'
}"></u-icon>
</view>
<text v-if="closeAble && closeText" class="u-close-text" :style="{
top: description ? '18rpx' : '24rpx'
}">{{closeText}}</text>
</view>
</template>
<script>
/**
* alertTips 警告提示
* @description 警告提示,展现需要关注的信息
* @tutorial https://uviewui.com/components/alertTips.html
* @property {String} title 显示的标题文字
* @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选
* @property {String} type 关闭按钮(默认为叉号icon图标)
* @property {String} icon 图标名称
* @property {Object} icon-style 图标的样式,对象形式
* @property {Object} title-style 标题的样式,对象形式
* @property {Object} desc-style 描述的样式,对象形式
* @property {String} close-able 用文字替代关闭图标,close-able为true时有效
* @property {Boolean} show-icon 是否显示左边的辅助图标
* @property {Boolean} show 显示或隐藏组件
* @event {Function} click 点击组件时触发
* @event {Function} close 点击关闭按钮时触发
*/
export default {
name: 'u-alert-tips',
props: {
// 显示文字
title: {
type: String,
default: ''
},
// 主题,success/warning/info/error
type: {
type: String,
default: 'warning'
},
// 辅助性文字
description: {
type: String,
default: ''
},
// 是否可关闭
closeAble: {
type: Boolean,
default: false
},
// 关闭按钮自定义文本
closeText: {
type: String,
default: ''
},
// 是否显示图标
showIcon: {
type: Boolean,
default: false
},
// 文字颜色,如果定义了color值,icon会失效
color: {
type: String,
default: ''
},
// 背景颜色
bgColor: {
type: String,
default: ''
},
// 边框颜色
borderColor: {
type: String,
default: ''
},
// 是否显示
show: {
type: Boolean,
default: true
},
// 左边显示的icon
icon: {
type: String,
default: ''
},
// icon的样式
iconStyle: {
type: Object,
default() {
return {}
}
},
// 标题的样式
titleStyle: {
type: Object,
default() {
return {}
}
},
// 描述文字的样式
descStyle: {
type: Object,
default() {
return {}
}
},
},
data() {
return {
}
},
computed: {
uTitleStyle() {
let style = {};
// 如果有描述文字的话,标题进行加粗
style.fontWeight = this.description ? 500 : 'normal';
// 将用户传入样式对象和style合并,传入的优先级比style高,同属性会被覆盖
return this.$u.deepMerge(style, this.titleStyle);
},
uIcon() {
// 如果有设置icon名称就使用,否则根据type主题,推定一个默认的图标
return this.icon ? this.icon : this.$u.type2icon(this.type);
},
uIconType() {
// 如果有设置图标的样式,优先使用,没有的话,则用type的样式
return Object.keys(this.iconStyle).length ? '' : this.type;
}
},
methods: {
// 点击内容
click() {
this.$emit('click');
},
// 点击关闭按钮
close() {
this.$emit('close');
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-alert-tips {
@include vue-flex;
align-items: center;
padding: 16rpx 30rpx;
border-radius: 8rpx;
position: relative;
transition: all 0.3s linear;
border: 1px solid #fff;
&--bg--primary-light {
background-color: $u-type-primary-light;
}
&--bg--info-light {
background-color: $u-type-info-light;
}
&--bg--success-light {
background-color: $u-type-success-light;
}
&--bg--warning-light {
background-color: $u-type-warning-light;
}
&--bg--error-light {
background-color: $u-type-error-light;
}
&--border--primary-disabled {
border-color: $u-type-primary-disabled;
}
&--border--success-disabled {
border-color: $u-type-success-disabled;
}
&--border--error-disabled {
border-color: $u-type-error-disabled;
}
&--border--warning-disabled {
border-color: $u-type-warning-disabled;
}
&--border--info-disabled {
border-color: $u-type-info-disabled;
}
}
.u-close-alert-tips {
opacity: 0;
visibility: hidden;
}
.u-icon {
margin-right: 16rpx;
}
.u-alert-title {
font-size: 28rpx;
color: $u-main-color;
}
.u-alert-desc {
font-size: 26rpx;
text-align: left;
color: $u-content-color;
}
.u-close-icon {
position: absolute;
top: 20rpx;
right: 20rpx;
}
.u-close-hover {
color: red;
}
.u-close-text {
font-size: 24rpx;
color: $u-tips-color;
position: absolute;
top: 20rpx;
right: 20rpx;
line-height: 1;
}
</style>
@@ -1,290 +0,0 @@
<template>
<view class="content">
<view class="cropper-wrapper" :style="{ height: cropperOpt.height + 'px' }">
<canvas
class="cropper"
:disable-scroll="true"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
:style="{ width: cropperOpt.width, height: cropperOpt.height, backgroundColor: 'rgba(0, 0, 0, 0.8)' }"
canvas-id="cropper"
id="cropper"
></canvas>
<canvas
class="cropper"
:disable-scroll="true"
:style="{
position: 'fixed',
top: `-${cropperOpt.width * cropperOpt.pixelRatio}px`,
left: `-${cropperOpt.height * cropperOpt.pixelRatio}px`,
width: `${cropperOpt.width * cropperOpt.pixelRatio}px`,
height: `${cropperOpt.height * cropperOpt.pixelRatio}`
}"
canvas-id="targetId"
id="targetId"
></canvas>
</view>
<view class="cropper-buttons safe-area-padding" :style="{ height: bottomNavHeight + 'px' }">
<!-- #ifdef H5 -->
<view class="upload" @tap="uploadTap">选择图片</view>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="upload" @tap="uploadTap">重新选择</view>
<!-- #endif -->
<view class="getCropperImage" @tap="getCropperImage(false)">确定</view>
</view>
</view>
</template>
<script>
import WeCropper from './weCropper.js';
export default {
props: {
// 裁剪矩形框的样式,其中可包含的属性为lineWidth-边框宽度(单位rpx),color: 边框颜色,
// mask-遮罩颜色,一般设置为一个rgba的透明度,如"rgba(0, 0, 0, 0.35)"
boundStyle: {
type: Object,
default() {
return {
lineWidth: 4,
borderColor: 'rgb(245, 245, 245)',
mask: 'rgba(0, 0, 0, 0.35)'
};
}
}
// // 裁剪框宽度,单位rpx
// rectWidth: {
// type: [String, Number],
// default: 400
// },
// // 裁剪框高度,单位rpx
// rectHeight: {
// type: [String, Number],
// default: 400
// },
// // 输出图片宽度,单位rpx
// destWidth: {
// type: [String, Number],
// default: 400
// },
// // 输出图片高度,单位rpx
// destHeight: {
// type: [String, Number],
// default: 400
// },
// // 输出的图片类型,如果发现裁剪的图片很大,可能是因为设置为了"png",改成"jpg"即可
// fileType: {
// type: String,
// default: 'jpg',
// },
// // 生成的图片质量
// // H5上无效,目前不考虑使用此参数
// quality: {
// type: [Number, String],
// default: 1
// }
},
data() {
return {
// 底部导航的高度
bottomNavHeight: 50,
originWidth: 200,
width: 0,
height: 0,
cropperOpt: {
id: 'cropper',
targetId: 'targetCropper',
pixelRatio: 1,
width: 0,
height: 0,
scale: 2.5,
zoom: 8,
cut: {
x: (this.width - this.originWidth) / 2,
y: (this.height - this.originWidth) / 2,
width: this.originWidth,
height: this.originWidth
},
boundStyle: {
lineWidth: uni.upx2px(this.boundStyle.lineWidth),
mask: this.boundStyle.mask,
color: this.boundStyle.borderColor
}
},
// 裁剪框和输出图片的尺寸,高度默认等于宽度
// 输出图片宽度,单位px
destWidth: 200,
// 裁剪框宽度,单位px
rectWidth: 200,
// 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可
fileType: 'jpg',
src: '', // 选择的图片路径,用于在点击确定时,判断是否选择了图片
};
},
onLoad(option) {
let rectInfo = uni.getSystemInfoSync();
this.width = rectInfo.windowWidth;
this.height = rectInfo.windowHeight - this.bottomNavHeight;
this.cropperOpt.width = this.width;
this.cropperOpt.height = this.height;
this.cropperOpt.pixelRatio = rectInfo.pixelRatio;
if (option.destWidth) this.destWidth = option.destWidth;
if (option.rectWidth) {
let rectWidth = Number(option.rectWidth);
this.cropperOpt.cut = {
x: (this.width - rectWidth) / 2,
y: (this.height - rectWidth) / 2,
width: rectWidth,
height: rectWidth
};
}
this.rectWidth = option.rectWidth;
if (option.fileType) this.fileType = option.fileType;
// 初始化
this.cropper = new WeCropper(this.cropperOpt)
.on('ready', ctx => {
// wecropper is ready for work!
})
.on('beforeImageLoad', ctx => {
// before picture loaded, i can do something
})
.on('imageLoad', ctx => {
// picture loaded
})
.on('beforeDraw', (ctx, instance) => {
// before canvas draw,i can do something
});
// 设置导航栏样式,以免用户在page.json中没有设置为黑色背景
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '#000000'
});
uni.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
this.src = res.tempFilePaths[0];
// 获取裁剪图片资源后,给data添加src属性及其值
this.cropper.pushOrign(this.src);
}
});
},
methods: {
touchStart(e) {
this.cropper.touchStart(e);
},
touchMove(e) {
this.cropper.touchMove(e);
},
touchEnd(e) {
this.cropper.touchEnd(e);
},
getCropperImage(isPre = false) {
if(!this.src) return this.$u.toast('请先选择图片再裁剪');
let cropper_opt = {
destHeight: Number(this.destWidth), // uni.canvasToTempFilePath要求这些参数为数值
destWidth: Number(this.destWidth),
fileType: this.fileType
};
this.cropper.getCropperImage(cropper_opt, (path, err) => {
if (err) {
uni.showModal({
title: '温馨提示',
content: err.message
});
} else {
if (isPre) {
uni.previewImage({
current: '', // 当前显示图片的 http 链接
urls: [path] // 需要预览的图片 http 链接列表
});
} else {
uni.$emit('uAvatarCropper', path);
this.$u.route({
type: 'back'
});
}
}
});
},
uploadTap() {
const self = this;
uni.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: (res) => {
self.src = res.tempFilePaths[0];
// 获取裁剪图片资源后,给data添加src属性及其值
self.cropper.pushOrign(this.src);
}
});
}
}
};
</script>
<style scoped lang="scss">
@import '../../libs/css/style.components.scss';
.content {
background: rgba(255, 255, 255, 1);
}
.cropper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 11;
}
.cropper-buttons {
background-color: #000000;
color: #eee;
}
.cropper-wrapper {
position: relative;
@include vue-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
background-color: #000;
}
.cropper-buttons {
width: 100vw;
@include vue-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 0;
left: 0;
font-size: 28rpx;
}
.cropper-buttons .upload,
.cropper-buttons .getCropperImage {
width: 50%;
text-align: center;
}
.cropper-buttons .upload {
text-align: left;
padding-left: 50rpx;
}
.cropper-buttons .getCropperImage {
text-align: right;
padding-right: 50rpx;
}
</style>
File diff suppressed because it is too large Load Diff
@@ -1,244 +0,0 @@
<template>
<view class="u-avatar" :style="[wrapStyle]" @tap="click">
<image
@error="loadError"
:style="[imgStyle]"
class="u-avatar__img"
v-if="!uText && avatar"
:src="avatar"
:mode="imgMode"
></image>
<text class="u-line-1" v-else-if="uText" :style="{
fontSize: '38rpx'
}">{{uText}}</text>
<slot v-else></slot>
<view class="u-avatar__sex" v-if="showSex" :class="['u-avatar__sex--' + sexIcon]" :style="[uSexStyle]">
<u-icon :name="sexIcon" size="20"></u-icon>
</view>
<view class="u-avatar__level" v-if="showLevel" :style="[uLevelStyle]">
<u-icon :name="levelIcon" size="20"></u-icon>
</view>
</view>
</template>
<script>
let base64Avatar = "data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z";
/**
* avatar 头像
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
* @tutorial https://www.uviewui.com/components/avatar.html
* @property {String} bg-color 背景颜色,一般显示文字时用(默认#ffffff)
* @property {String} src 头像路径,如加载失败,将会显示默认头像
* @property {String Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值,单位rpx(默认default)
* @property {String} mode 显示类型,见上方说明(默认circle)
* @property {String} sex-icon 性别图标,man-男,woman-女(默认man)
* @property {String} level-icon 等级图标(默认level)
* @property {String} sex-bg-color 性别图标背景颜色
* @property {String} level-bg-color 等级图标背景颜色
* @property {String} show-sex 是否显示性别图标(默认false)
* @property {String} show-level 是否显示等级图标(默认false)
* @property {String} img-mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值(默认aspectFill)
* @property {String} index 用户传递的标识符值,如果是列表循环,可穿v-for的index值
* @event {Function} click 头像被点击
* @example <u-avatar :src="src"></u-avatar>
*/
export default {
name: 'u-avatar',
props: {
// 背景颜色
bgColor: {
type: String,
default: 'transparent'
},
// 头像路径
src: {
type: String,
default: ''
},
// 尺寸,large-大,default-中等,mini-小,如果为数值,则单位为rpx
// 宽度等于高度
size: {
type: [String, Number],
default: 'default'
},
// 头像模型,square-带圆角方形,circle-圆形
mode: {
type: String,
default: 'circle'
},
// 文字内容
text: {
type: String,
default: ''
},
// 图片的裁剪模型
imgMode: {
type: String,
default: 'aspectFill'
},
// 标识符
index: {
type: [String, Number],
default: ''
},
// 右上角性别角标,man-男,woman-女
sexIcon: {
type: String,
default: 'man'
},
// 右下角的等级图标
levelIcon: {
type: String,
default: 'level'
},
// 右下角等级图标背景颜色
levelBgColor: {
type: String,
default: ''
},
// 右上角性别图标的背景颜色
sexBgColor: {
type: String,
default: ''
},
// 是否显示性别图标
showSex: {
type: Boolean,
default: false
},
// 是否显示等级图标
showLevel: {
type: Boolean,
default: false
}
},
data() {
return {
error: false,
// 头像的地址,因为如果加载错误,需要赋值为默认图片,props值无法修改,所以需要一个中间值
avatar: this.src ? this.src : base64Avatar,
}
},
watch: {
src(n) {
// 用户可能会在头像加载失败时,再次修改头像值,所以需要重新赋值
if(!n) {
// 如果传入null或者'',或者undefined,显示默认头像
this.avatar = base64Avatar;
this.error = true;
} else {
this.avatar = n;
this.error = false;
}
}
},
computed: {
wrapStyle() {
let style = {};
style.height = this.size == 'large' ? '120rpx' : this.size == 'default' ?
'90rpx' : this.size == 'mini' ? '70rpx' : this.size + 'rpx';
style.width = style.height;
style.flex = `0 0 ${style.height}`;
style.backgroundColor = this.bgColor;
style.borderRadius = this.mode == 'circle' ? '500px' : '5px';
if(this.text) style.padding = `0 6rpx`;
return style;
},
imgStyle() {
let style = {};
style.borderRadius = this.mode == 'circle' ? '500px' : '5px';
return style;
},
// 取字符串的第一个字符
uText() {
return String(this.text)[0];
},
// 性别图标的自定义样式
uSexStyle() {
let style = {};
if(this.sexBgColor) style.backgroundColor = this.sexBgColor;
return style;
},
// 等级图标的自定义样式
uLevelStyle() {
let style = {};
if(this.levelBgColor) style.backgroundColor = this.levelBgColor;
return style;
}
},
methods: {
// 图片加载错误时,显示默认头像
loadError() {
this.error = true;
this.avatar = base64Avatar;
},
click() {
this.$emit('click', this.index);
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-avatar {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
align-items: center;
justify-content: center;
font-size: 28rpx;
color: $u-content-color;
border-radius: 10px;
position: relative;
&__img {
width: 100%;
height: 100%;
}
&__sex {
position: absolute;
width: 32rpx;
color: #ffffff;
height: 32rpx;
@include vue-flex;
justify-content: center;
align-items: center;
border-radius: 100rpx;
top: 5%;
z-index: 1;
right: -7%;
border: 1px #ffffff solid;
&--man {
background-color: $u-type-primary;
}
&--woman {
background-color: $u-type-error;
}
&--none {
background-color: $u-type-warning;
}
}
&__level {
position: absolute;
width: 32rpx;
color: #ffffff;
height: 32rpx;
@include vue-flex;
justify-content: center;
align-items: center;
border-radius: 100rpx;
bottom: 5%;
z-index: 1;
right: -7%;
border: 1px #ffffff solid;
background-color: $u-type-warning;
}
}
</style>
@@ -1,153 +0,0 @@
<template>
<view @tap="backToTop" class="u-back-top" :class="['u-back-top--mode--' + mode]" :style="[{
bottom: bottom + 'rpx',
right: right + 'rpx',
borderRadius: mode == 'circle' ? '10000rpx' : '8rpx',
zIndex: uZIndex,
opacity: opacity
}, customStyle]">
<view class="u-back-top__content" v-if="!$slots.default && !$slots.$default">
<u-icon @click="backToTop" :name="icon" :custom-style="iconStyle"></u-icon>
<view class="u-back-top__content__tips">
{{tips}}
</view>
</view>
<slot v-else />
</view>
</template>
<script>
export default {
name: 'u-back-top',
props: {
// 返回顶部的形状,circle-圆形,square-方形
mode: {
type: String,
default: 'circle'
},
// 自定义图标
icon: {
type: String,
default: 'arrow-upward'
},
// 提示文字
tips: {
type: String,
default: ''
},
// 返回顶部滚动时间
duration: {
type: [Number, String],
default: 100
},
// 滚动距离
scrollTop: {
type: [Number, String],
default: 0
},
// 距离顶部多少距离显示,单位rpx
top: {
type: [Number, String],
default: 400
},
// 返回顶部按钮到底部的距离,单位rpx
bottom: {
type: [Number, String],
default: 200
},
// 返回顶部按钮到右边的距离,单位rpx
right: {
type: [Number, String],
default: 40
},
// 层级
zIndex: {
type: [Number, String],
default: '9'
},
// 图标的样式,对象形式
iconStyle: {
type: Object,
default() {
return {
color: '#909399',
fontSize: '38rpx'
}
}
},
// 整个组件的样式
customStyle: {
type: Object,
default() {
return {}
}
}
},
watch: {
showBackTop(nVal, oVal) {
// 当组件的显示与隐藏状态发生跳变时,修改组件的层级和不透明度
// 让组件有显示和消失的动画效果,如果用v-if控制组件状态,将无设置动画效果
if(nVal) {
this.uZIndex = this.zIndex;
this.opacity = 1;
} else {
this.uZIndex = -1;
this.opacity = 0;
}
}
},
computed: {
showBackTop() {
// 由于scrollTop为页面的滚动距离,默认为px单位,这里将用于传入的top(rpx)值
// 转为px用于比较,如果滚动条到顶的距离大于设定的距离,就显示返回顶部的按钮
return this.scrollTop > uni.upx2px(this.top);
},
},
data() {
return {
// 不透明度,为了让组件有一个显示和隐藏的过渡动画
opacity: 0,
// 组件的z-index值,隐藏时设置为-1,就会看不到
uZIndex: -1
}
},
methods: {
backToTop() {
uni.pageScrollTo({
scrollTop: 0,
duration: this.duration
});
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-back-top {
width: 80rpx;
height: 80rpx;
position: fixed;
z-index: 9;
@include vue-flex;
flex-direction: column;
justify-content: center;
background-color: #E1E1E1;
color: $u-content-color;
align-items: center;
transition: opacity 0.4s;
&__content {
@include vue-flex;
flex-direction: column;
align-items: center;
&__tips {
font-size: 24rpx;
transform: scale(0.8);
line-height: 1;
}
}
}
</style>
-216
View File
@@ -1,216 +0,0 @@
<template>
<view v-if="show" class="u-badge" :class="[
isDot ? 'u-badge-dot' : '',
size == 'mini' ? 'u-badge-mini' : '',
type ? 'u-badge--bg--' + type : ''
]" :style="[{
top: offset[0] + 'rpx',
right: offset[1] + 'rpx',
fontSize: fontSize + 'rpx',
position: absolute ? 'absolute' : 'static',
color: color,
backgroundColor: bgColor
}, boxStyle]"
>
{{showText}}
</view>
</template>
<script>
/**
* badge 角标
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
* @tutorial https://www.uviewui.com/components/badge.html
* @property {String Number} count 展示的数字,大于 overflowCount 时显示为 ${overflowCount}+,为0且show-zero为false时隐藏
* @property {Boolean} is-dot 不展示数字,只有一个小点(默认false)
* @property {Boolean} absolute 组件是否绝对定位,为true时,offset参数才有效(默认true)
* @property {String Number} overflow-count 展示封顶的数字值(默认99)
* @property {String} type 使用预设的背景颜色(默认error)
* @property {Boolean} show-zero 当数值为 0 时,是否展示 Badge(默认false)
* @property {String} size Badge的尺寸,设为mini会得到小一号的Badge(默认default)
* @property {Array} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,单位rpx。absolute为true时有效(默认[20, 20])
* @property {String} color 字体颜色(默认#ffffff)
* @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效
* @property {Boolean} is-center 组件中心点是否和父组件右上角重合,优先级比offset高,如设置,offset参数会失效(默认false)
* @example <u-badge type="error" count="7"></u-badge>
*/
export default {
name: 'u-badge',
props: {
// primary,warning,success,error,info
type: {
type: String,
default: 'error'
},
// default, mini
size: {
type: String,
default: 'default'
},
//是否是圆点
isDot: {
type: Boolean,
default: false
},
// 显示的数值内容
count: {
type: [Number, String],
},
// 展示封顶的数字值
overflowCount: {
type: Number,
default: 99
},
// 当数值为 0 时,是否展示 Badge
showZero: {
type: Boolean,
default: false
},
// 位置偏移
offset: {
type: Array,
default: () => {
return [20, 20]
}
},
// 是否开启绝对定位,开启了offset才会起作用
absolute: {
type: Boolean,
default: true
},
// 字体大小
fontSize: {
type: [String, Number],
default: '24'
},
// 字体演示
color: {
type: String,
default: '#ffffff'
},
// badge的背景颜色
bgColor: {
type: String,
default: ''
},
// 是否让badge组件的中心点和父组件右上角重合,配置的话,offset将会失效
isCenter: {
type: Boolean,
default: false
}
},
computed: {
// 是否将badge中心与父组件右上角重合
boxStyle() {
let style = {};
if(this.isCenter) {
style.top = 0;
style.right = 0;
// Y轴-50%,意味着badge向上移动了badge自身高度一半,X轴50%,意味着向右移动了自身宽度一半
style.transform = "translateY(-50%) translateX(50%)";
} else {
style.top = this.offset[0] + 'rpx';
style.right = this.offset[1] + 'rpx';
style.transform = "translateY(0) translateX(0)";
}
// 如果尺寸为mini,后接上scal()
if(this.size == 'mini') {
style.transform = style.transform + " scale(0.8)";
}
return style;
},
// isDot类型时,不显示文字
showText() {
if(this.isDot) return '';
else {
if(this.count > this.overflowCount) return `${this.overflowCount}+`;
else return this.count;
}
},
// 是否显示组件
show() {
// 如果count的值为0,并且showZero设置为false,不显示组件
if(this.count == 0 && this.showZero == false) return false;
else return true;
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-badge {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
justify-content: center;
align-items: center;
line-height: 24rpx;
padding: 4rpx 8rpx;
border-radius: 100rpx;
z-index: 9;
&--bg--primary {
background-color: $u-type-primary;
}
&--bg--error {
background-color: $u-type-error;
}
&--bg--success {
background-color: $u-type-success;
}
&--bg--info {
background-color: $u-type-info;
}
&--bg--warning {
background-color: $u-type-warning;
}
}
.u-badge-dot {
height: 16rpx;
width: 16rpx;
border-radius: 100rpx;
line-height: 1;
}
.u-badge-mini {
transform: scale(0.8);
transform-origin: center center;
}
// .u-primary {
// background: $u-type-primary;
// color: #fff;
// }
// .u-error {
// background: $u-type-error;
// color: #fff;
// }
// .u-warning {
// background: $u-type-warning;
// color: #fff;
// }
// .u-success {
// background: $u-type-success;
// color: #fff;
// }
// .u-black {
// background: #585858;
// color: #fff;
// }
.u-info {
background-color: $u-type-info;
color: #fff;
}
</style>
@@ -1,596 +0,0 @@
<template>
<button
id="u-wave-btn"
class="u-btn u-line-1 u-fix-ios-appearance"
:class="[
'u-size-' + size,
plain ? 'u-btn--' + type + '--plain' : '',
loading ? 'u-loading' : '',
shape == 'circle' ? 'u-round-circle' : '',
hairLine ? showHairLineBorder : 'u-btn--bold-border',
'u-btn--' + type,
disabled ? `u-btn--${type}--disabled` : '',
]"
:hover-start-time="Number(hoverStartTime)"
:hover-stay-time="Number(hoverStayTime)"
:disabled="disabled"
:form-type="formType"
:open-type="openType"
:app-parameter="appParameter"
:hover-stop-propagation="hoverStopPropagation"
:send-message-title="sendMessageTitle"
send-message-path="sendMessagePath"
:lang="lang"
:data-name="dataName"
:session-from="sessionFrom"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
@getphonenumber="getphonenumber"
@getuserinfo="getuserinfo"
@error="error"
@opensetting="opensetting"
@launchapp="launchapp"
:style="[customStyle, {
overflow: ripple ? 'hidden' : 'visible'
}]"
@tap.stop="click($event)"
:hover-class="getHoverClass"
:loading="loading"
>
<slot></slot>
<view
v-if="ripple"
class="u-wave-ripple"
:class="[waveActive ? 'u-wave-active' : '']"
:style="{
top: rippleTop + 'px',
left: rippleLeft + 'px',
width: fields.targetWidth + 'px',
height: fields.targetWidth + 'px',
'background-color': rippleBgColor || 'rgba(0, 0, 0, 0.15)'
}"
></view>
</button>
</template>
<script>
/**
* button 按钮
* @description Button 按钮
* @tutorial https://www.uviewui.com/components/button.html
* @property {String} size 按钮的大小
* @property {Boolean} ripple 是否开启点击水波纹效果
* @property {String} ripple-bg-color 水波纹的背景色,ripple为true时有效
* @property {String} type 按钮的样式类型
* @property {Boolean} plain 按钮是否镂空,背景色透明
* @property {Boolean} disabled 是否禁用
* @property {Boolean} hair-line 是否显示按钮的细边框(默认true)
* @property {Boolean} shape 按钮外观形状,见文档说明
* @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈)
* @property {String} form-type 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
* @property {String} open-type 开放能力
* @property {String} data-name 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
* @property {String} hover-class 指定按钮按下去的样式类。当 hover-class="none" 时,没有点击态效果(App-nvue 平台暂不支持)
* @property {Number} hover-start-time 按住后多久出现点击态,单位毫秒
* @property {Number} hover-stay-time 手指松开后点击态保留时间,单位毫秒
* @property {Object} custom-style 对按钮的自定义样式,对象形式,见文档说明
* @event {Function} click 按钮点击
* @event {Function} getphonenumber open-type="getPhoneNumber"时有效
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
* @event {Function} error 当使用开放能力时,发生错误的回调
* @event {Function} opensetting 在打开授权设置页并关闭后回调
* @event {Function} launchapp 打开 APP 成功的回调
* @example <u-button>月落</u-button>
*/
export default {
name: 'u-button',
props: {
// 是否细边框
hairLine: {
type: Boolean,
default: true
},
// 按钮的预置样式,default,primary,error,warning,success
type: {
type: String,
default: 'default'
},
// 按钮尺寸,default,medium,mini
size: {
type: String,
default: 'default'
},
// 按钮形状,circle(两边为半圆),square(带圆角)
shape: {
type: String,
default: 'square'
},
// 按钮是否镂空
plain: {
type: Boolean,
default: false
},
// 是否禁止状态
disabled: {
type: Boolean,
default: false
},
// 是否加载中
loading: {
type: Boolean,
default: false
},
// 开放能力,具体请看uniapp稳定关于button组件部分说明
// https://uniapp.dcloud.io/component/button
openType: {
type: String,
default: ''
},
// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
// 取值为submit(提交表单),reset(重置表单)
formType: {
type: String,
default: ''
},
// 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
// 只微信小程序、QQ小程序有效
appParameter: {
type: String,
default: ''
},
// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
hoverStopPropagation: {
type: Boolean,
default: false
},
// 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
lang: {
type: String,
default: 'en'
},
// 会话来源,open-type="contact"时有效。只微信小程序有效
sessionFrom: {
type: String,
default: ''
},
// 会话内消息卡片标题,open-type="contact"时有效
// 默认当前标题,只微信小程序有效
sendMessageTitle: {
type: String,
default: ''
},
// 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
// 默认当前分享路径,只微信小程序有效
sendMessagePath: {
type: String,
default: ''
},
// 会话内消息卡片图片,open-type="contact"时有效
// 默认当前页面截图,只微信小程序有效
sendMessageImg: {
type: String,
default: ''
},
// 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
// 用户点击后可以快速发送小程序消息,open-type="contact"时有效
showMessageCard: {
type: Boolean,
default: false
},
// 手指按(触摸)按钮时按钮时的背景颜色
hoverBgColor: {
type: String,
default: ''
},
// 水波纹的背景颜色
rippleBgColor: {
type: String,
default: ''
},
// 是否开启水波纹效果
ripple: {
type: Boolean,
default: false
},
// 按下的类名
hoverClass: {
type: String,
default: ''
},
// 自定义样式,对象形式
customStyle: {
type: Object,
default() {
return {};
}
},
// 额外传参参数,用于小程序的data-xxx属性,通过target.dataset.name获取
dataName: {
type: String,
default: ''
},
// 节流,一定时间内只能触发一次
throttleTime: {
type: [String, Number],
default: 1000
},
// 按住后多久出现点击态,单位毫秒
hoverStartTime: {
type: [String, Number],
default: 20
},
// 手指松开后点击态保留时间,单位毫秒
hoverStayTime: {
type: [String, Number],
default: 150
},
},
computed: {
// 当没有传bgColor变量时,按钮按下去的颜色类名
getHoverClass() {
// 如果开启水波纹效果,则不启用hover-class效果
if (this.loading || this.disabled || this.ripple || this.hoverClass) return '';
let hoverClass = '';
hoverClass = this.plain ? 'u-' + this.type + '-plain-hover' : 'u-' + this.type + '-hover';
return hoverClass;
},
// 在'primary', 'success', 'error', 'warning'类型下,不显示边框,否则会造成四角有毛刺现象
showHairLineBorder() {
if (['primary', 'success', 'error', 'warning'].indexOf(this.type) >= 0 && !this.plain) {
return '';
} else {
return 'u-hairline-border';
}
}
},
data() {
return {
rippleTop: 0, // 水波纹的起点Y坐标到按钮上边界的距离
rippleLeft: 0, // 水波纹起点X坐标到按钮左边界的距离
fields: {}, // 波纹按钮节点信息
waveActive: false // 激活水波纹
};
},
methods: {
// 按钮点击
click(e) {
// 进行节流控制,每this.throttle毫秒内,只在开始处执行
this.$u.throttle(() => {
// 如果按钮时disabled和loading状态,不触发水波纹效果
if (this.loading === true || this.disabled === true) return;
// 是否开启水波纹效果
if (this.ripple) {
// 每次点击时,移除上一次的类,再次添加,才能触发动画效果
this.waveActive = false;
this.$nextTick(function() {
this.getWaveQuery(e);
});
}
this.$emit('click', e);
}, this.throttleTime);
},
// 查询按钮的节点信息
getWaveQuery(e) {
this.getElQuery().then(res => {
// 查询返回的是一个数组节点
let data = res[0];
// 查询不到节点信息,不操作
if (!data.width || !data.width) return;
// 水波纹的最终形态是一个正方形(通过border-radius让其变为一个圆形),这里要保证正方形的边长等于按钮的最长边
// 最终的方形(变换后的圆形)才能覆盖整个按钮
data.targetWidth = data.height > data.width ? data.height : data.width;
if (!data.targetWidth) return;
this.fields = data;
let touchesX = '',
touchesY = '';
// #ifdef MP-BAIDU
touchesX = e.changedTouches[0].clientX;
touchesY = e.changedTouches[0].clientY;
// #endif
// #ifdef MP-ALIPAY
touchesX = e.detail.clientX;
touchesY = e.detail.clientY;
// #endif
// #ifndef MP-BAIDU || MP-ALIPAY
touchesX = e.touches[0].clientX;
touchesY = e.touches[0].clientY;
// #endif
// 获取触摸点相对于按钮上边和左边的x和y坐标,原理是通过屏幕的触摸点(touchesY),减去按钮的上边界data.top
// 但是由于`transform-origin`默认是center,所以这里再减去半径才是水波纹view应该的位置
// 总的来说,就是把水波纹的矩形(变换后的圆形)的中心点,移动到我们的触摸点位置
this.rippleTop = touchesY - data.top - data.targetWidth / 2;
this.rippleLeft = touchesX - data.left - data.targetWidth / 2;
this.$nextTick(() => {
this.waveActive = true;
});
});
},
// 获取节点信息
getElQuery() {
return new Promise(resolve => {
let queryInfo = '';
// 获取元素节点信息,请查看uniapp相关文档
// https://uniapp.dcloud.io/api/ui/nodes-info?id=nodesrefboundingclientrect
queryInfo = uni.createSelectorQuery().in(this);
//#ifdef MP-ALIPAY
queryInfo = uni.createSelectorQuery();
//#endif
queryInfo.select('.u-btn').boundingClientRect();
queryInfo.exec(data => {
resolve(data);
});
});
},
// 下面为对接uniapp官方按钮开放能力事件回调的对接
getphonenumber(res) {
this.$emit('getphonenumber', res);
},
getuserinfo(res) {
this.$emit('getuserinfo', res);
},
error(res) {
this.$emit('error', res);
},
opensetting(res) {
this.$emit('opensetting', res);
},
launchapp(res) {
this.$emit('launchapp', res);
}
}
};
</script>
<style scoped lang="scss">
@import '../../libs/css/style.components.scss';
.u-btn::after {
border: none;
}
.u-btn {
position: relative;
border: 0;
//border-radius: 10rpx;
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
// 避免边框某些场景可能被“裁剪”,不能设置为hidden
overflow: visible;
line-height: 1;
@include vue-flex;
align-items: center;
justify-content: center;
cursor: pointer;
padding: 0 40rpx;
z-index: 1;
box-sizing: border-box;
transition: all 0.15s;
&--bold-border {
border: 1px solid #ffffff;
}
&--default {
color: $u-content-color;
border-color: #c0c4cc;
background-color: #ffffff;
}
&--primary {
color: #ffffff;
border-color: $u-type-primary;
background-color: $u-type-primary;
}
&--success {
color: #ffffff;
border-color: $u-type-success;
background-color: $u-type-success;
}
&--error {
color: #ffffff;
border-color: $u-type-error;
background-color: $u-type-error;
}
&--warning {
color: #ffffff;
border-color: $u-type-warning;
background-color: $u-type-warning;
}
&--default--disabled {
color: #ffffff;
border-color: #e4e7ed;
background-color: #ffffff;
}
&--primary--disabled {
color: #ffffff!important;
border-color: $u-type-primary-disabled!important;
background-color: $u-type-primary-disabled!important;
}
&--success--disabled {
color: #ffffff!important;
border-color: $u-type-success-disabled!important;
background-color: $u-type-success-disabled!important;
}
&--error--disabled {
color: #ffffff!important;
border-color: $u-type-error-disabled!important;
background-color: $u-type-error-disabled!important;
}
&--warning--disabled {
color: #ffffff!important;
border-color: $u-type-warning-disabled!important;
background-color: $u-type-warning-disabled!important;
}
&--primary--plain {
color: $u-type-primary!important;
border-color: $u-type-primary-disabled!important;
background-color: $u-type-primary-light!important;
}
&--success--plain {
color: $u-type-success!important;
border-color: $u-type-success-disabled!important;
background-color: $u-type-success-light!important;
}
&--error--plain {
color: $u-type-error!important;
border-color: $u-type-error-disabled!important;
background-color: $u-type-error-light!important;
}
&--warning--plain {
color: $u-type-warning!important;
border-color: $u-type-warning-disabled!important;
background-color: $u-type-warning-light!important;
}
}
.u-hairline-border:after {
content: ' ';
position: absolute;
pointer-events: none;
// 设置为border-box,意味着下面的scale缩小为0.5,实际上缩小的是伪元素的内容(border-box意味着内容不含border)
box-sizing: border-box;
// 中心点作为变形(scale())的原点
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
left: 0;
top: 0;
width: 199.8%;
height: 199.7%;
-webkit-transform: scale(0.5, 0.5);
transform: scale(0.5, 0.5);
border: 1px solid currentColor;
z-index: 1;
}
.u-wave-ripple {
z-index: 0;
position: absolute;
border-radius: 100%;
background-clip: padding-box;
pointer-events: none;
user-select: none;
transform: scale(0);
opacity: 1;
transform-origin: center;
}
.u-wave-ripple.u-wave-active {
opacity: 0;
transform: scale(2);
transition: opacity 1s linear, transform 0.4s linear;
}
.u-round-circle {
border-radius: 100rpx;
}
.u-round-circle::after {
border-radius: 100rpx;
}
.u-loading::after {
background-color: hsla(0, 0%, 100%, 0.35);
}
.u-size-default {
font-size: 30rpx;
height: 80rpx;
line-height: 80rpx;
}
.u-size-medium {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
width: auto;
font-size: 26rpx;
height: 70rpx;
line-height: 70rpx;
padding: 0 80rpx;
}
.u-size-mini {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
width: auto;
font-size: 22rpx;
padding-top: 1px;
height: 50rpx;
line-height: 50rpx;
padding: 0 20rpx;
}
.u-primary-plain-hover {
color: #ffffff !important;
background: $u-type-primary-dark !important;
}
.u-default-plain-hover {
color: $u-type-primary-dark !important;
background: $u-type-primary-light !important;
}
.u-success-plain-hover {
color: #ffffff !important;
background: $u-type-success-dark !important;
}
.u-warning-plain-hover {
color: #ffffff !important;
background: $u-type-warning-dark !important;
}
.u-error-plain-hover {
color: #ffffff !important;
background: $u-type-error-dark !important;
}
.u-info-plain-hover {
color: #ffffff !important;
background: $u-type-info-dark !important;
}
.u-default-hover {
color: $u-type-primary-dark !important;
border-color: $u-type-primary-dark !important;
background-color: $u-type-primary-light !important;
}
.u-primary-hover {
background: $u-type-primary-dark !important;
color: #fff;
}
.u-success-hover {
background: $u-type-success-dark !important;
color: #fff;
}
.u-info-hover {
background: $u-type-info-dark !important;
color: #fff;
}
.u-warning-hover {
background: $u-type-warning-dark !important;
color: #fff;
}
.u-error-hover {
background: $u-type-error-dark !important;
color: #fff;
}
</style>
@@ -1,639 +0,0 @@
<template>
<u-popup closeable :maskCloseAble="maskCloseAble" mode="bottom" :popup="false" v-model="value" length="auto"
:safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex" :border-radius="borderRadius" :closeable="closeable">
<view class="u-calendar">
<view class="u-calendar__header">
<view class="u-calendar__header__text" v-if="!$slots['tooltip']">
{{toolTip}}
</view>
<slot v-else name="tooltip" />
</view>
<view class="u-calendar__action u-flex u-row-center">
<view class="u-calendar__action__icon">
<u-icon v-if="changeYear" name="arrow-left-double" :color="yearArrowColor" @click="changeYearHandler(0)"></u-icon>
</view>
<view class="u-calendar__action__icon">
<u-icon v-if="changeMonth" name="arrow-left" :color="monthArrowColor" @click="changeMonthHandler(0)"></u-icon>
</view>
<view class="u-calendar__action__text">{{ showTitle }}</view>
<view class="u-calendar__action__icon">
<u-icon v-if="changeMonth" name="arrow-right" :color="monthArrowColor" @click="changeMonthHandler(1)"></u-icon>
</view>
<view class="u-calendar__action__icon">
<u-icon v-if="changeYear" name="arrow-right-double" :color="yearArrowColor" @click="changeYearHandler(1)"></u-icon>
</view>
</view>
<view class="u-calendar__week-day">
<view class="u-calendar__week-day__text" v-for="(item, index) in weekDayZh" :key="index">{{item}}</view>
</view>
<view class="u-calendar__content">
<!-- 前置空白部分 -->
<block v-for="(item, index) in weekdayArr" :key="index">
<view class="u-calendar__content__item"></view>
</block>
<view class="u-calendar__content__item" :class="{
'u-hover-class':openDisAbled(year,month,index+1),
'u-calendar__content--start-date': (mode == 'range' && startDate==`${year}-${month}-${index+1}`) || mode== 'date',
'u-calendar__content--end-date':(mode== 'range' && endDate==`${year}-${month}-${index+1}`) || mode == 'date'
}" :style="{backgroundColor: getColor(index,1)}" v-for="(item, index) in daysArr" :key="index"
@tap="dateClick(index)">
<view class="u-calendar__content__item__inner" :style="{color: getColor(index,2)}">
<view>{{ index + 1 }}</view>
</view>
<view class="u-calendar__content__item__tips" :style="{color:activeColor}" v-if="mode== 'range' && startDate==`${year}-${month}-${index+1}` && startDate!=endDate">{{startText}}</view>
<view class="u-calendar__content__item__tips" :style="{color:activeColor}" v-if="mode== 'range' && endDate==`${year}-${month}-${index+1}`">{{endText}}</view>
</view>
<view class="u-calendar__content__bg-month">{{month}}</view>
</view>
<view class="u-calendar__bottom">
<view class="u-calendar__bottom__choose">
<text>{{mode == 'date' ? activeDate : startDate}}</text>
<text v-if="endDate">至{{endDate}}</text>
</view>
<view class="u-calendar__bottom__btn">
<u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)">确定</u-button>
</view>
</view>
</view>
</u-popup>
</template>
<script>
/**
* calendar 日历
* @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中。
* @tutorial http://uviewui.com/components/calendar.html
* @property {String} mode 选择日期的模式,date-为单个日期,range-为选择日期范围
* @property {Boolean} v-model 布尔值变量,用于控制日历的弹出与收起
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
* @property {Boolean} change-year 是否显示顶部的切换年份方向的按钮(默认true)
* @property {Boolean} change-month 是否显示顶部的切换月份方向的按钮(默认true)
* @property {String Number} max-year 可切换的最大年份(默认2050)
* @property {String Number} min-year 可切换的最小年份(默认1950)
* @property {String Number} min-date 最小可选日期(默认1950-01-01)
* @property {String Number} max-date 最大可选日期(默认当前日期)
* @property {String Number} 弹窗顶部左右两边的圆角值,单位rpx(默认20)
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭日历(默认true)
* @property {String} month-arrow-color 月份切换按钮箭头颜色(默认#606266)
* @property {String} year-arrow-color 年份切换按钮箭头颜色(默认#909399)
* @property {String} color 日期字体的默认颜色(默认#303133)
* @property {String} active-bg-color 起始/结束日期按钮的背景色(默认#2979ff)
* @property {String Number} z-index 弹出时的z-index值(默认10075)
* @property {String} active-color 起始/结束日期按钮的字体颜色(默认#ffffff)
* @property {String} range-bg-color 起始/结束日期之间的区域的背景颜色(默认rgba(41,121,255,0.13))
* @property {String} range-color 选择范围内字体颜色(默认#2979ff)
* @property {String} start-text 起始日期底部的提示文字(默认 '开始')
* @property {String} end-text 结束日期底部的提示文字(默认 '结束')
* @property {String} btn-type 底部确定按钮的主题(默认 'primary')
* @property {String} toolTip 顶部提示文字,如设置名为tooltip的slot,此参数将失效(默认 '选择日期')
* @property {Boolean} closeable 是否显示右上角的关闭图标(默认true)
* @example <u-calendar v-model="show" :mode="mode"></u-calendar>
*/
export default {
name: 'u-calendar',
props: {
safeAreaInsetBottom: {
type: Boolean,
default: false
},
// 是否允许通过点击遮罩关闭Picker
maskCloseAble: {
type: Boolean,
default: true
},
// 通过双向绑定控制组件的弹出与收起
value: {
type: Boolean,
default: false
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
},
// 是否允许切换年份
changeYear: {
type: Boolean,
default: true
},
// 是否允许切换月份
changeMonth: {
type: Boolean,
default: true
},
// date-单个日期选择,range-开始日期+结束日期选择
mode: {
type: String,
default: 'date'
},
// 可切换的最大年份
maxYear: {
type: [Number, String],
default: 2050
},
// 可切换的最小年份
minYear: {
type: [Number, String],
default: 1950
},
// 最小可选日期(不在范围内日期禁用不可选)
minDate: {
type: [Number, String],
default: '1950-01-01'
},
/**
* 最大可选日期
* 默认最大值为今天,之后的日期不可选
* 2030-12-31
* */
maxDate: {
type: [Number, String],
default: ''
},
// 弹窗顶部左右两边的圆角值
borderRadius: {
type: [String, Number],
default: 20
},
// 月份切换按钮箭头颜色
monthArrowColor: {
type: String,
default: '#606266'
},
// 年份切换按钮箭头颜色
yearArrowColor: {
type: String,
default: '#909399'
},
// 默认日期字体颜色
color: {
type: String,
default: '#303133'
},
// 选中|起始结束日期背景色
activeBgColor: {
type: String,
default: '#2979ff'
},
// 选中|起始结束日期字体颜色
activeColor: {
type: String,
default: '#ffffff'
},
// 范围内日期背景色
rangeBgColor: {
type: String,
default: 'rgba(41,121,255,0.13)'
},
// 范围内日期字体颜色
rangeColor: {
type: String,
default: '#2979ff'
},
// mode=range时生效,起始日期自定义文案
startText: {
type: String,
default: '开始'
},
// mode=range时生效,结束日期自定义文案
endText: {
type: String,
default: '结束'
},
//按钮样式类型
btnType: {
type: String,
default: 'primary'
},
// 当前选中日期带选中效果
isActiveCurrent: {
type: Boolean,
default: true
},
// 切换年月是否触发事件 mode=date时生效
isChange: {
type: Boolean,
default: false
},
// 是否显示右上角的关闭图标
closeable: {
type: Boolean,
default: true
},
// 顶部的提示文字
toolTip: {
type: String,
default: '选择日期'
}
},
data() {
return {
// 星期几,值为1-7
weekday: 1,
weekdayArr:[],
// 当前月有多少天
days: 0,
daysArr:[],
showTitle: '',
year: 2020,
month: 0,
day: 0,
startYear: 0,
startMonth: 0,
startDay: 0,
endYear: 0,
endMonth: 0,
endDay: 0,
today: '',
activeDate: '',
startDate: '',
endDate: '',
isStart: true,
min: null,
max: null,
weekDayZh: ['日', '一', '二', '三', '四', '五', '六']
};
},
computed: {
dataChange() {
return `${this.mode}-${this.minDate}-${this.maxDate}`;
},
uZIndex() {
// 如果用户有传递z-index值,优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
}
},
watch: {
dataChange(val) {
this.init()
}
},
created() {
this.init()
},
methods: {
getColor(index, type) {
let color = type == 1 ? '' : this.color;
let day = index + 1
let date = `${this.year}-${this.month}-${day}`
let timestamp = new Date(date.replace(/\-/g, '/')).getTime();
let start = this.startDate.replace(/\-/g, '/')
let end = this.endDate.replace(/\-/g, '/')
if ((this.isActiveCurrent && this.activeDate == date) || this.startDate == date || this.endDate == date) {
color = type == 1 ? this.activeBgColor : this.activeColor;
} else if (this.endDate && timestamp > new Date(start).getTime() && timestamp < new Date(end).getTime()) {
color = type == 1 ? this.rangeBgColor : this.rangeColor;
}
return color;
},
init() {
let now = new Date();
this.year = now.getFullYear();
this.month = now.getMonth() + 1;
this.day = now.getDate();
this.today = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
this.activeDate = this.today;
this.min = this.initDate(this.minDate);
this.max = this.initDate(this.maxDate || this.today);
this.startDate = "";
this.startYear = 0;
this.startMonth = 0;
this.startDay = 0;
this.endYear = 0;
this.endMonth = 0;
this.endDay = 0;
this.endDate = "";
this.isStart = true;
this.changeData();
},
//日期处理
initDate(date) {
let fdate = date.split('-');
return {
year: Number(fdate[0] || 1920),
month: Number(fdate[1] || 1),
day: Number(fdate[2] || 1)
}
},
openDisAbled: function(year, month, day) {
let bool = true;
let date = `${year}/${month}/${day}`;
// let today = this.today.replace(/\-/g, '/');
let min = `${this.min.year}/${this.min.month}/${this.min.day}`;
let max = `${this.max.year}/${this.max.month}/${this.max.day}`;
let timestamp = new Date(date).getTime();
if (timestamp >= new Date(min).getTime() && timestamp <= new Date(max).getTime()) {
bool = false;
}
return bool;
},
generateArray: function(start, end) {
return Array.from(new Array(end + 1).keys()).slice(start);
},
formatNum: function(num) {
return num < 10 ? '0' + num : num + '';
},
//一个月有多少天
getMonthDay(year, month) {
let days = new Date(year, month, 0).getDate();
return days;
},
getWeekday(year, month) {
let date = new Date(`${year}/${month}/01 00:00:00`);
return date.getDay();
},
checkRange(year) {
let overstep = false;
if (year < this.minYear || year > this.maxYear) {
uni.showToast({
title: "日期超出范围啦~",
icon: 'none'
})
overstep = true;
}
return overstep;
},
changeMonthHandler(isAdd) {
if (isAdd) {
let month = this.month + 1;
let year = month > 12 ? this.year + 1 : this.year;
if (!this.checkRange(year)) {
this.month = month > 12 ? 1 : month;
this.year = year;
this.changeData();
}
} else {
let month = this.month - 1;
let year = month < 1 ? this.year - 1 : this.year;
if (!this.checkRange(year)) {
this.month = month < 1 ? 12 : month;
this.year = year;
this.changeData();
}
}
},
changeYearHandler(isAdd) {
let year = isAdd ? this.year + 1 : this.year - 1;
if (!this.checkRange(year)) {
this.year = year;
this.changeData();
}
},
changeData() {
this.days = this.getMonthDay(this.year, this.month);
this.daysArr=this.generateArray(1,this.days)
this.weekday = this.getWeekday(this.year, this.month);
this.weekdayArr=this.generateArray(1,this.weekday)
this.showTitle = `${this.year}年${this.month}月`;
if (this.isChange && this.mode == 'date') {
this.btnFix(true);
}
},
dateClick: function(day) {
day += 1;
if (!this.openDisAbled(this.year, this.month, day)) {
this.day = day;
let date = `${this.year}-${this.month}-${day}`;
if (this.mode == 'date') {
this.activeDate = date;
} else {
let compare = new Date(date.replace(/\-/g, '/')).getTime() < new Date(this.startDate.replace(/\-/g, '/')).getTime()
if (this.isStart || compare) {
this.startDate = date;
this.startYear = this.year;
this.startMonth = this.month;
this.startDay = this.day;
this.endYear = 0;
this.endMonth = 0;
this.endDay = 0;
this.endDate = "";
this.activeDate = "";
this.isStart = false;
} else {
this.endDate = date;
this.endYear = this.year;
this.endMonth = this.month;
this.endDay = this.day;
this.isStart = true;
}
}
}
},
close() {
// 修改通过v-model绑定的父组件变量的值为false,从而隐藏日历弹窗
this.$emit('input', false);
},
getWeekText(date) {
date = new Date(`${date.replace(/\-/g, '/')} 00:00:00`);
let week = date.getDay();
return '星期' + ['日', '一', '二', '三', '四', '五', '六'][week];
},
btnFix(show) {
if (!show) {
this.close();
}
if (this.mode == 'date') {
let arr = this.activeDate.split('-')
let year = this.isChange ? this.year : Number(arr[0]);
let month = this.isChange ? this.month : Number(arr[1]);
let day = this.isChange ? this.day : Number(arr[2]);
//当前月有多少天
let days = this.getMonthDay(year, month);
let result = `${year}-${this.formatNum(month)}-${this.formatNum(day)}`;
let weekText = this.getWeekText(result);
let isToday = false;
if (`${year}-${month}-${day}` == this.today) {
//今天
isToday = true;
}
this.$emit('change', {
year: year,
month: month,
day: day,
days: days,
result: result,
week: weekText,
isToday: isToday,
// switch: show //是否是切换年月操作
});
} else {
if (!this.startDate || !this.endDate) return;
let startMonth = this.formatNum(this.startMonth);
let startDay = this.formatNum(this.startDay);
let startDate = `${this.startYear}-${startMonth}-${startDay}`;
let startWeek = this.getWeekText(startDate)
let endMonth = this.formatNum(this.endMonth);
let endDay = this.formatNum(this.endDay);
let endDate = `${this.endYear}-${endMonth}-${endDay}`;
let endWeek = this.getWeekText(endDate);
this.$emit('change', {
startYear: this.startYear,
startMonth: this.startMonth,
startDay: this.startDay,
startDate: startDate,
startWeek: startWeek,
endYear: this.endYear,
endMonth: this.endMonth,
endDay: this.endDay,
endDate: endDate,
endWeek: endWeek
});
}
}
}
};
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-calendar {
color: $u-content-color;
&__header {
width: 100%;
box-sizing: border-box;
font-size: 30rpx;
background-color: #fff;
color: $u-main-color;
&__text {
margin-top: 30rpx;
padding: 0 60rpx;
@include vue-flex;
justify-content: center;
align-items: center;
}
}
&__action {
padding: 40rpx 0 40rpx 0;
&__icon {
margin: 0 16rpx;
}
&__text {
padding: 0 16rpx;
color: $u-main-color;
font-size: 32rpx;
line-height: 32rpx;
font-weight: bold;
}
}
&__week-day {
@include vue-flex;
align-items: center;
justify-content: center;
padding: 6px 0;
overflow: hidden;
&__text {
flex: 1;
text-align: center;
}
}
&__content {
width: 100%;
@include vue-flex;
flex-wrap: wrap;
padding: 6px 0;
box-sizing: border-box;
background-color: #fff;
position: relative;
&--end-date {
border-top-right-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
&--start-date {
border-top-left-radius: 8rpx;
border-bottom-left-radius: 8rpx;
}
&__item {
width: 14.2857%;
@include vue-flex;
align-items: center;
justify-content: center;
padding: 6px 0;
overflow: hidden;
position: relative;
z-index: 2;
&__inner {
height: 84rpx;
@include vue-flex;
align-items: center;
justify-content: center;
flex-direction: column;
font-size: 32rpx;
position: relative;
border-radius: 50%;
&__desc {
width: 100%;
font-size: 24rpx;
line-height: 24rpx;
transform: scale(0.75);
transform-origin: center center;
position: absolute;
left: 0;
text-align: center;
bottom: 2rpx;
}
}
&__tips {
width: 100%;
font-size: 24rpx;
line-height: 24rpx;
position: absolute;
left: 0;
transform: scale(0.8);
transform-origin: center center;
text-align: center;
bottom: 8rpx;
z-index: 2;
}
}
&__bg-month {
position: absolute;
font-size: 130px;
line-height: 130px;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
color: #e4e7ed;
z-index: 1;
}
}
&__bottom {
width: 100%;
@include vue-flex;
align-items: center;
justify-content: center;
flex-direction: column;
background-color: #fff;
padding: 0 40rpx 30rpx;
box-sizing: border-box;
font-size: 24rpx;
color: $u-tips-color;
&__choose {
height: 50rpx;
}
&__btn {
width: 100%;
}
}
}
</style>
@@ -1,257 +0,0 @@
<template>
<view class="u-keyboard" @touchmove.stop.prevent="() => {}">
<view class="u-keyboard-grids">
<block>
<view class="u-keyboard-grids-item" v-for="(group, i) in abc ? EngKeyBoardList : areaList" :key="i">
<view :hover-stay-time="100" @tap="carInputClick(i, j)" hover-class="u-carinput-hover" class="u-keyboard-grids-btn"
v-for="(item, j) in group" :key="j">
{{ item }}
</view>
</view>
<view @touchstart="backspaceClick" @touchend="clearTimer" :hover-stay-time="100" class="u-keyboard-back"
hover-class="u-hover-class">
<u-icon :size="38" name="backspace" :bold="true"></u-icon>
</view>
<view :hover-stay-time="100" class="u-keyboard-change" hover-class="u-carinput-hover" @tap="changeCarInputMode">
<text class="zh" :class="[!abc ? 'active' : 'inactive']">中</text>
/
<text class="en" :class="[abc ? 'active' : 'inactive']">英</text>
</view>
</block>
</view>
</view>
</template>
<script>
export default {
name: "u-keyboard",
props: {
// 是否打乱键盘按键的顺序
random: {
type: Boolean,
default: false
}
},
data() {
return {
// 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称
abc: false
};
},
computed: {
areaList() {
let data = [
'京',
'沪',
'粤',
'津',
'冀',
'豫',
'云',
'辽',
'黑',
'湘',
'皖',
'鲁',
'苏',
'浙',
'赣',
'鄂',
'桂',
'甘',
'晋',
'陕',
'蒙',
'吉',
'闽',
'贵',
'渝',
'川',
'青',
'琼',
'宁',
'挂',
'藏',
'港',
'澳',
'新',
'使',
'学'
];
let tmp = [];
// 打乱顺序
if (this.random) data = this.$u.randomArray(data);
// 切割成二维数组
tmp[0] = data.slice(0, 10);
tmp[1] = data.slice(10, 20);
tmp[2] = data.slice(20, 30);
tmp[3] = data.slice(30, 36);
return tmp;
},
EngKeyBoardList() {
let data = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
0,
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'Z',
'X',
'C',
'V',
'B',
'N',
'M'
];
let tmp = [];
if (this.random) data = this.$u.randomArray(data);
tmp[0] = data.slice(0, 10);
tmp[1] = data.slice(10, 20);
tmp[2] = data.slice(20, 30);
tmp[3] = data.slice(30, 36);
return tmp;
}
},
methods: {
// 点击键盘按钮
carInputClick(i, j) {
let value = '';
// 不同模式,获取不同数组的值
if (this.abc) value = this.EngKeyBoardList[i][j];
else value = this.areaList[i][j];
this.$emit('change', value);
},
// 修改汽车牌键盘的输入模式,中文|英文
changeCarInputMode() {
this.abc = !this.abc;
},
// 点击退格键
backspaceClick() {
this.$emit('backspace');
clearInterval(this.timer); //再次清空定时器,防止重复注册定时器
this.timer = null;
this.timer = setInterval(() => {
this.$emit('backspace');
}, 250);
},
clearTimer() {
clearInterval(this.timer);
this.timer = null;
},
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-keyboard-grids {
background: rgb(215, 215, 217);
padding: 24rpx 0;
position: relative;
}
.u-keyboard-grids-item {
@include vue-flex;
align-items: center;
justify-content: center;
}
.u-keyboard-grids-btn {
text-decoration: none;
width: 62rpx;
flex: 0 0 64rpx;
height: 80rpx;
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
font-size: 36rpx;
text-align: center;
line-height: 80rpx;
background-color: #fff;
margin: 8rpx 5rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 0rpx #888992;
font-weight: 500;
justify-content: center;
}
.u-carinput-hover {
background-color: rgb(185, 188, 195) !important;
}
.u-keyboard-back {
position: absolute;
width: 96rpx;
right: 22rpx;
bottom: 32rpx;
height: 80rpx;
background-color: rgb(185, 188, 195);
@include vue-flex;
align-items: center;
border-radius: 8rpx;
justify-content: center;
box-shadow: 0 2rpx 0rpx #888992;
}
.u-keyboard-change {
font-size: 24rpx;
box-shadow: 0 2rpx 0rpx #888992;
position: absolute;
width: 96rpx;
left: 22rpx;
line-height: 1;
bottom: 32rpx;
height: 80rpx;
background-color: #ffffff;
@include vue-flex;
align-items: center;
border-radius: 8rpx;
justify-content: center;
}
.u-keyboard-change .inactive.zh {
transform: scale(0.85) translateY(-10rpx);
}
.u-keyboard-change .inactive.en {
transform: scale(0.85) translateY(10rpx);
}
.u-keyboard-change .active {
color: rgb(237, 112, 64);
font-size: 30rpx;
}
.u-keyboard-change .zh {
transform: translateY(-10rpx);
}
.u-keyboard-change .en {
transform: translateY(10rpx);
}
</style>
-299
View File
@@ -1,299 +0,0 @@
<template>
<view
class="u-card"
@tap.stop="click"
:class="{ 'u-border': border, 'u-card-full': full, 'u-card--border': borderRadius > 0 }"
:style="{
borderRadius: borderRadius + 'rpx',
margin: margin,
boxShadow: boxShadow
}"
>
<view
v-if="showHead"
class="u-card__head"
:style="[{padding: padding + 'rpx'}, headStyle]"
:class="{
'u-border-bottom': headBorderBottom
}"
@tap="headClick"
>
<view v-if="!$slots.head" class="u-flex u-row-between">
<view class="u-card__head--left u-flex u-line-1" v-if="title">
<image
:src="thumb"
class="u-card__head--left__thumb"
mode="aspectfull"
v-if="thumb"
:style="{
height: thumbWidth + 'rpx',
width: thumbWidth + 'rpx',
borderRadius: thumbCircle ? '100rpx' : '6rpx'
}"
></image>
<text
class="u-card__head--left__title u-line-1"
:style="{
fontSize: titleSize + 'rpx',
color: titleColor
}"
>
{{ title }}
</text>
</view>
<view class="u-card__head--right u-line-1" v-if="subTitle">
<text
class="u-card__head__title__text"
:style="{
fontSize: subTitleSize + 'rpx',
color: subTitleColor
}"
>
{{ subTitle }}
</text>
</view>
</view>
<slot name="head" v-else />
</view>
<view @tap="bodyClick" class="u-card__body" :style="[{padding: padding + 'rpx'}, bodyStyle]"><slot name="body" /></view>
<view
v-if="showFoot"
class="u-card__foot"
@tap="footClick"
:style="[{padding: $slots.foot ? padding + 'rpx' : 0}, footStyle]"
:class="{
'u-border-top': footBorderTop
}"
>
<slot name="foot" />
</view>
</view>
</template>
<script>
/**
* card 卡片
* @description 卡片组件一般用于多个列表条目,且风格统一的场景
* @tutorial https://www.uviewui.com/components/card.html
* @property {Boolean} full 卡片与屏幕两侧是否留空隙(默认false)
* @property {String} title 头部左边的标题
* @property {String} title-color 标题颜色(默认#303133)
* @property {String | Number} title-size 标题字体大小,单位rpx(默认30)
* @property {String} sub-title 头部右边的副标题
* @property {String} sub-title-color 副标题颜色(默认#909399)
* @property {String | Number} sub-title-size 副标题字体大小(默认26)
* @property {Boolean} border 是否显示边框(默认true)
* @property {String | Number} index 用于标识点击了第几个卡片
* @property {String} box-shadow 卡片外围阴影,字符串形式(默认none)
* @property {String} margin 卡片与屏幕两边和上下元素的间距,需带单位,如"30rpx 20rpx"(默认30rpx)
* @property {String | Number} border-radius 卡片整体的圆角值,单位rpx(默认16)
* @property {Object} head-style 头部自定义样式,对象形式
* @property {Object} body-style 中部自定义样式,对象形式
* @property {Object} foot-style 底部自定义样式,对象形式
* @property {Boolean} head-border-bottom 是否显示头部的下边框(默认true)
* @property {Boolean} foot-border-top 是否显示底部的上边框(默认true)
* @property {Boolean} show-head 是否显示头部(默认true)
* @property {Boolean} show-head 是否显示尾部(默认true)
* @property {String} thumb 缩略图路径,如设置将显示在标题的左边,不建议使用相对路径
* @property {String | Number} thumb-width 缩略图的宽度,高等于宽,单位rpx(默认60)
* @property {Boolean} thumb-circle 缩略图是否为圆形(默认false)
* @event {Function} click 整个卡片任意位置被点击时触发
* @event {Function} head-click 卡片头部被点击时触发
* @event {Function} body-click 卡片主体部分被点击时触发
* @event {Function} foot-click 卡片底部部分被点击时触发
* @example <u-card padding="30" title="card"></u-card>
*/
export default {
name: 'u-card',
props: {
// 与屏幕两侧是否留空隙
full: {
type: Boolean,
default: false
},
// 标题
title: {
type: String,
default: ''
},
// 标题颜色
titleColor: {
type: String,
default: '#303133'
},
// 标题字体大小,单位rpx
titleSize: {
type: [Number, String],
default: '30'
},
// 副标题
subTitle: {
type: String,
default: ''
},
// 副标题颜色
subTitleColor: {
type: String,
default: '#909399'
},
// 副标题字体大小,单位rpx
subTitleSize: {
type: [Number, String],
default: '26'
},
// 是否显示外部边框,只对full=false时有效(卡片与边框有空隙时)
border: {
type: Boolean,
default: true
},
// 用于标识点击了第几个
index: {
type: [Number, String, Object],
default: ''
},
// 用于隔开上下左右的边距,带单位的写法,如:"30rpx 30rpx","20rpx 20rpx 30rpx 30rpx"
margin: {
type: String,
default: '30rpx'
},
// card卡片的圆角
borderRadius: {
type: [Number, String],
default: '16'
},
// 头部自定义样式,对象形式
headStyle: {
type: Object,
default() {
return {};
}
},
// 主体自定义样式,对象形式
bodyStyle: {
type: Object,
default() {
return {};
}
},
// 底部自定义样式,对象形式
footStyle: {
type: Object,
default() {
return {};
}
},
// 头部是否下边框
headBorderBottom: {
type: Boolean,
default: true
},
// 底部是否有上边框
footBorderTop: {
type: Boolean,
default: true
},
// 标题左边的缩略图
thumb: {
type: String,
default: ''
},
// 缩略图宽高,单位rpx
thumbWidth: {
type: [String, Number],
default: '60'
},
// 缩略图是否为圆形
thumbCircle: {
type: Boolean,
default: false
},
// 给head,body,foot的内边距
padding: {
type: [String, Number],
default: '30'
},
// 是否显示头部
showHead: {
type: Boolean,
default: true
},
// 是否显示尾部
showFoot: {
type: Boolean,
default: true
},
// 卡片外围阴影,字符串形式
boxShadow: {
type: String,
default: 'none'
}
},
data() {
return {};
},
methods: {
click() {
this.$emit('click', this.index);
},
headClick() {
this.$emit('head-click', this.index);
},
bodyClick() {
this.$emit('body-click', this.index);
},
footClick() {
this.$emit('foot-click', this.index);
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-card {
position: relative;
overflow: hidden;
font-size: 28rpx;
background-color: #ffffff;
box-sizing: border-box;
&-full {
// 如果是与屏幕之间不留空隙,应该设置左右边距为0
margin-left: 0 !important;
margin-right: 0 !important;
width: 100%;
}
&--border:after {
border-radius: 16rpx;
}
&__head {
&--left {
color: $u-main-color;
&__thumb {
margin-right: 16rpx;
}
&__title {
max-width: 400rpx;
}
}
&--right {
color: $u-tips-color;
margin-left: 6rpx;
}
}
&__body {
color: $u-content-color;
}
&__foot {
color: $u-tips-color;
}
}
</style>
@@ -1,70 +0,0 @@
<template>
<view class="u-cell-box">
<view class="u-cell-title" v-if="title" :style="[titleStyle]">
{{title}}
</view>
<view class="u-cell-item-box" :class="{'u-border-bottom u-border-top': border}">
<slot />
</view>
</view>
</template>
<script>
/**
* cellGroup 单元格父组件Group
* @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。搭配u-cell-item
* @tutorial https://www.uviewui.com/components/cell.html
* @property {String} title 分组标题
* @property {Boolean} border 是否显示外边框(默认true)
* @property {Object} title-style 分组标题的的样式,对象形式,如{'font-size': '24rpx'} 或 {'fontSize': '24rpx'}
* @example <u-cell-group title="设置喜好">
*/
export default {
name: "u-cell-group",
props: {
// 分组标题
title: {
type: String,
default: ''
},
// 是否显示分组list上下边框
border: {
type: Boolean,
default: true
},
// 分组标题的样式,对象形式,注意驼峰属性写法
// 类似 {'font-size': '24rpx'} 和 {'fontSize': '24rpx'}
titleStyle: {
type: Object,
default () {
return {};
}
}
},
data() {
return {
index: 0,
}
},
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-cell-box {
width: 100%;
}
.u-cell-title {
padding: 30rpx 32rpx 10rpx 32rpx;
font-size: 30rpx;
text-align: left;
color: $u-tips-color;
}
.u-cell-item-box {
background-color: #FFFFFF;
flex-direction: row;
}
</style>
@@ -1,316 +0,0 @@
<template>
<view
@tap="click"
class="u-cell"
:class="{ 'u-border-bottom': borderBottom, 'u-border-top': borderTop, 'u-col-center': center, 'u-cell--required': required }"
hover-stay-time="150"
:hover-class="hoverClass"
:style="{
backgroundColor: bgColor
}"
>
<u-icon :size="iconSize" :name="icon" v-if="icon" :custom-style="iconStyle" class="u-cell__left-icon-wrap"></u-icon>
<view class="u-flex" v-else>
<slot name="icon"></slot>
</view>
<view
class="u-cell_title"
:style="[
{
width: titleWidth ? titleWidth + 'rpx' : 'auto'
},
titleStyle
]"
>
<block v-if="title !== ''">{{ title }}</block>
<slot name="title" v-else></slot>
<view class="u-cell__label" v-if="label || $slots.label" :style="[labelStyle]">
<block v-if="label !== ''">{{ label }}</block>
<slot name="label" v-else></slot>
</view>
</view>
<view class="u-cell__value" :style="[valueStyle]">
<block class="u-cell__value" v-if="value !== ''">{{ value }}</block>
<slot v-else></slot>
</view>
<view class="u-flex u-cell_right" v-if="$slots['right-icon']">
<slot name="right-icon"></slot>
</view>
<u-icon v-if="arrow" name="arrow-right" :style="[arrowStyle]" class="u-icon-wrap u-cell__right-icon-wrap"></u-icon>
</view>
</template>
<script>
/**
* cellItem 单元格Item
* @description cell单元格一般用于一组列表的情况,比如个人中心页,设置页等。搭配u-cell-group使用
* @tutorial https://www.uviewui.com/components/cell.html
* @property {String} title 左侧标题
* @property {String} icon 左侧图标名,只支持uView内置图标,见Icon 图标
* @property {Object} icon-style 左边图标的样式,对象形式
* @property {String} value 右侧内容
* @property {String} label 标题下方的描述信息
* @property {Boolean} border-bottom 是否显示cell的下边框(默认true)
* @property {Boolean} border-top 是否显示cell的上边框(默认false)
* @property {Boolean} center 是否使内容垂直居中(默认false)
* @property {String} hover-class 是否开启点击反馈,none为无效果(默认true)
* // @property {Boolean} border-gap border-bottom为true时,Cell列表中间的条目的下边框是否与左边有一个间隔(默认true)
* @property {Boolean} arrow 是否显示右侧箭头(默认true)
* @property {Boolean} required 箭头方向,可选值(默认right)
* @property {Boolean} arrow-direction 是否显示左边表示必填的星号(默认false)
* @property {Object} title-style 标题样式,对象形式
* @property {Object} value-style 右侧内容样式,对象形式
* @property {Object} label-style 标题下方描述信息的样式,对象形式
* @property {String} bg-color 背景颜色(默认transparent)
* @property {String Number} index 用于在click事件回调中返回,标识当前是第几个Item
* @property {String Number} title-width 标题的宽度,单位rpx
* @example <u-cell-item icon="integral-fill" title="会员等级" value="新版本"></u-cell-item>
*/
export default {
name: 'u-cell-item',
props: {
// 左侧图标名称(只能uView内置图标),或者图标src
icon: {
type: String,
default: ''
},
// 左侧标题
title: {
type: [String, Number],
default: ''
},
// 右侧内容
value: {
type: [String, Number],
default: ''
},
// 标题下方的描述信息
label: {
type: [String, Number],
default: ''
},
// 是否显示下边框
borderBottom: {
type: Boolean,
default: true
},
// 是否显示上边框
borderTop: {
type: Boolean,
default: false
},
// 多个cell中,中间的cell显示下划线时,下划线是否给一个到左边的距离
// 1.4.0版本废除此参数,默认边框由border-top和border-bottom提供,此参数会造成干扰
// borderGap: {
// type: Boolean,
// default: true
// },
// 是否开启点击反馈,即点击时cell背景为灰色,none为无效果
hoverClass: {
type: String,
default: 'u-cell-hover'
},
// 是否显示右侧箭头
arrow: {
type: Boolean,
default: true
},
// 内容是否垂直居中
center: {
type: Boolean,
default: false
},
// 是否显示左边表示必填的星号
required: {
type: Boolean,
default: false
},
// 标题的宽度,单位rpx
titleWidth: {
type: [Number, String],
default: ''
},
// 右侧箭头方向,可选值:right|up|down,默认为right
arrowDirection: {
type: String,
default: 'right'
},
// 控制标题的样式
titleStyle: {
type: Object,
default() {
return {};
}
},
// 右侧显示内容的样式
valueStyle: {
type: Object,
default() {
return {};
}
},
// 描述信息的样式
labelStyle: {
type: Object,
default() {
return {};
}
},
// 背景颜色
bgColor: {
type: String,
default: 'transparent'
},
// 用于识别被点击的是第几个cell
index: {
type: [String, Number],
default: ''
},
// 是否使用lable插槽
useLabelSlot: {
type: Boolean,
default: false
},
// 左边图标的大小,单位rpx,只对传入icon字段时有效
iconSize: {
type: [Number, String],
default: 34
},
// 左边图标的样式,对象形式
iconStyle: {
type: Object,
default() {
return {}
}
},
},
data() {
return {
};
},
computed: {
arrowStyle() {
let style = {};
if (this.arrowDirection == 'up') style.transform = 'rotate(-90deg)';
else if (this.arrowDirection == 'down') style.transform = 'rotate(90deg)';
else style.transform = 'rotate(0deg)';
return style;
}
},
methods: {
click() {
this.$emit('click', this.index);
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-cell {
@include vue-flex;
align-items: center;
position: relative;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
width: 100%;
padding: 26rpx 32rpx;
font-size: 28rpx;
line-height: 54rpx;
color: $u-content-color;
background-color: #fff;
text-align: left;
}
.u-cell_title {
font-size: 28rpx;
}
.u-cell__left-icon-wrap {
margin-right: 10rpx;
font-size: 32rpx;
}
.u-cell__right-icon-wrap {
margin-left: 10rpx;
color: #969799;
font-size: 28rpx;
}
.u-cell__left-icon-wrap,
.u-cell__right-icon-wrap {
@include vue-flex;
align-items: center;
height: 48rpx;
}
.u-cell-border:after {
position: absolute;
/* #ifndef APP-NVUE */
box-sizing: border-box;
content: ' ';
pointer-events: none;
border-bottom: 1px solid $u-border-color;
/* #endif */
right: 0;
left: 0;
top: 0;
transform: scaleY(0.5);
}
.u-cell-border {
position: relative;
}
.u-cell__label {
margin-top: 6rpx;
font-size: 26rpx;
line-height: 36rpx;
color: $u-tips-color;
/* #ifndef APP-NVUE */
word-wrap: break-word;
/* #endif */
}
.u-cell__value {
overflow: hidden;
text-align: right;
/* #ifndef APP-NVUE */
vertical-align: middle;
/* #endif */
color: $u-tips-color;
font-size: 30rpx;
}
.u-cell__title,
.u-cell__value {
flex: 1;
}
.u-cell--required {
/* #ifndef APP-NVUE */
overflow: visible;
/* #endif */
@include vue-flex;
align-items: center;
}
.u-cell--required:before {
position: absolute;
/* #ifndef APP-NVUE */
content: '*';
/* #endif */
left: 8px;
margin-top: 4rpx;
font-size: 14px;
color: $u-type-error;
}
.u-cell_right {
line-height: 1;
}
</style>
@@ -1,123 +0,0 @@
<template>
<view class="u-checkbox-group u-clearfix">
<slot></slot>
</view>
</template>
<script>
import Emitter from '../../libs/util/emitter.js';
/**
* checkboxGroup 开关选择器父组件Group
* @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便
* @tutorial https://www.uviewui.com/components/checkbox.html
* @property {String Number} max 最多能选中多少个checkbox(默认999)
* @property {String Number} size 组件整体的大小,单位rpx(默认40)
* @property {Boolean} disabled 是否禁用所有checkbox(默认false)
* @property {String Number} icon-size 图标大小,单位rpx(默认20)
* @property {Boolean} label-disabled 是否禁止点击文本操作checkbox(默认false)
* @property {String} width 宽度,需带单位
* @property {String} width 宽度,需带单位
* @property {String} shape 外观形状,shape-方形,circle-圆形(默认circle)
* @property {Boolean} wrap 是否每个checkbox都换行(默认false)
* @property {String} active-color 选中时的颜色,应用到所有子Checkbox组件(默认#2979ff)
* @event {Function} change 任一个checkbox状态发生变化时触发,回调为一个对象
* @example <u-checkbox-group></u-checkbox-group>
*/
export default {
name: 'u-checkbox-group',
mixins: [Emitter],
props: {
// 最多能选中多少个checkbox
max: {
type: [Number, String],
default: 999
},
// 所有选中项的 name
// value: {
// default: Array,
// default() {
// return []
// }
// },
// 是否禁用所有复选框
disabled: {
type: Boolean,
default: false
},
// 在表单内提交时的标识符
name: {
type: [Boolean, String],
default: ''
},
// 是否禁止点击提示语选中复选框
labelDisabled: {
type: Boolean,
default: false
},
// 形状,square为方形,circle为原型
shape: {
type: String,
default: 'square'
},
// 选中状态下的颜色
activeColor: {
type: String,
default: '#2979ff'
},
// 组件的整体大小
size: {
type: [String, Number],
default: 34
},
// 每个checkbox占u-checkbox-group的宽度
width: {
type: String,
default: 'auto'
},
// 是否每个checkbox都换行
wrap: {
type: Boolean,
default: false
},
// 图标的大小,单位rpx
iconSize: {
type: [String, Number],
default: 20
},
},
data() {
return {
}
},
created() {
// 如果将children定义在data中,在微信小程序会造成循环引用而报错
this.children = [];
},
methods: {
emitEvent() {
let values = [];
this.children.map(val => {
if(val.value) values.push(val.name);
})
this.$emit('change', values);
// 发出事件,用于在表单组件中嵌入checkbox的情况,进行验证
// 由于头条小程序执行迟钝,故需要用几十毫秒的延时
setTimeout(() => {
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-change', values);
}, 60)
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-checkbox-group {
/* #ifndef MP || APP-NVUE */
display: inline-flex;
flex-wrap: wrap;
/* #endif */
}
</style>
@@ -1,284 +0,0 @@
<template>
<view class="u-checkbox" :style="[checkboxStyle]">
<view class="u-checkbox__icon-wrap" @tap="toggle" :class="[iconClass]" :style="[iconStyle]">
<u-icon class="u-checkbox__icon-wrap__icon" name="checkbox-mark" :size="checkboxIconSize" :color="iconColor"/>
</view>
<view class="u-checkbox__label" @tap="onClickLabel" :style="{
fontSize: $u.addUnit(labelSize)
}">
<slot />
</view>
</view>
</template>
<script>
/**
* checkbox 复选框
* @description 该组件需要搭配checkboxGroup组件使用,以便用户进行操作时,获得当前复选框组的选中情况。
* @tutorial https://www.uviewui.com/components/checkbox.html
* @property {String Number} icon-size 图标大小,单位rpx(默认20)
* @property {String Number} label-size label字体大小,单位rpx(默认28)
* @property {String Number} name checkbox组件的标示符
* @property {String} shape 形状,见官网说明(默认circle)
* @property {Boolean} disabled 是否禁用
* @property {Boolean} label-disabled 是否禁止点击文本操作checkbox
* @property {String} active-color 选中时的颜色,如设置CheckboxGroup的active-color将失效
* @event {Function} change 某个checkbox状态发生变化时触发,回调为一个对象
* @example <u-checkbox v-model="checked" :disabled="false">天涯</u-checkbox>
*/
export default {
name: "u-checkbox",
props: {
// checkbox的名称
name: {
type: [String, Number],
default: ''
},
// 形状,square为方形,circle为原型
shape: {
type: String,
default: ''
},
// 是否为选中状态
value: {
type: Boolean,
default: false
},
// 是否禁用
disabled: {
type: [String, Boolean],
default: ''
},
// 是否禁止点击提示语选中复选框
labelDisabled: {
type: [String, Boolean],
default: ''
},
// 选中状态下的颜色,如设置此值,将会覆盖checkboxGroup的activeColor值
activeColor: {
type: String,
default: ''
},
// 图标的大小,单位rpx
iconSize: {
type: [String, Number],
default: ''
},
// label的字体大小,rpx单位
labelSize: {
type: [String, Number],
default: ''
},
// 组件的整体大小
size: {
type: [String, Number],
default: ''
},
},
data() {
return {
parentDisabled: false,
newParams: {},
};
},
created() {
// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环应用
this.parent = this.$u.$parent.call(this, 'u-checkbox-group');
// 如果存在u-checkbox-group,将本组件的this塞进父组件的children中
this.parent && this.parent.children.push(this);
},
computed: {
// 是否禁用,如果父组件u-checkbox-group禁用的话,将会忽略子组件的配置
isDisabled() {
return this.disabled !== '' ? this.disabled : this.parent ? this.parent.disabled : false;
},
// 是否禁用label点击
isLabelDisabled() {
return this.labelDisabled !== '' ? this.labelDisabled : this.parent ? this.parent.labelDisabled : false;
},
// 组件尺寸,对应size的值,默认值为34rpx
checkboxSize() {
return this.size ? this.size : (this.parent ? this.parent.size : 34);
},
// 组件的勾选图标的尺寸,默认20
checkboxIconSize() {
return this.iconSize ? this.iconSize : (this.parent ? this.parent.iconSize : 20);
},
// 组件选中激活时的颜色
elActiveColor() {
return this.activeColor ? this.activeColor : (this.parent ? this.parent.activeColor : 'primary');
},
// 组件的形状
elShape() {
return this.shape ? this.shape : (this.parent ? this.parent.shape : 'square');
},
iconStyle() {
let style = {};
// 既要判断是否手动禁用,还要判断用户v-model绑定的值,如果绑定为false,那么也无法选中
if (this.elActiveColor && this.value && !this.isDisabled) {
style.borderColor = this.elActiveColor;
style.backgroundColor = this.elActiveColor;
}
style.width = this.$u.addUnit(this.checkboxSize);
style.height = this.$u.addUnit(this.checkboxSize);
return style;
},
// checkbox内部的勾选图标,如果选中状态,为白色,否则为透明色即可
iconColor() {
return this.value ? '#ffffff' : 'transparent';
},
iconClass() {
let classes = [];
classes.push('u-checkbox__icon-wrap--' + this.elShape);
if (this.value == true) classes.push('u-checkbox__icon-wrap--checked');
if (this.isDisabled) classes.push('u-checkbox__icon-wrap--disabled');
if (this.value && this.isDisabled) classes.push('u-checkbox__icon-wrap--disabled--checked');
// 支付宝小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
return classes.join(' ');
},
checkboxStyle() {
let style = {};
if(this.parent && this.parent.width) {
style.width = this.parent.width;
// #ifdef MP
// 各家小程序因为它们特殊的编译结构,使用float布局
style.float = 'left';
// #endif
// #ifndef MP
// H5和APP使用flex布局
style.flex = `0 0 ${this.parent.width}`;
// #endif
}
if(this.parent && this.parent.wrap) {
style.width = '100%';
// #ifndef MP
// H5和APP使用flex布局,将宽度设置100%,即可自动换行
style.flex = '0 0 100%';
// #endif
}
return style;
}
},
methods: {
onClickLabel() {
if (!this.isLabelDisabled && !this.isDisabled) {
this.setValue();
}
},
toggle() {
if (!this.isDisabled) {
this.setValue();
}
},
emitEvent() {
this.$emit('change', {
value: !this.value,
name: this.name
})
// 执行父组件u-checkbox-group的事件方法
// 等待下一个周期再执行,因为this.$emit('input')作用于父组件,再反馈到子组件内部,需要时间
setTimeout(() => {
if(this.parent && this.parent.emitEvent) this.parent.emitEvent();
}, 80);
},
// 设置input的值,这里通过input事件,设置通过v-model绑定的组件的值
setValue() {
// 判断是否超过了可选的最大数量
let checkedNum = 0;
if(this.parent && this.parent.children) {
// 只要父组件的某一个子元素的value为true,就加1(已有的选中数量)
this.parent.children.map(val => {
if (val.value) checkedNum++;
})
}
// 如果原来为选中状态,那么可以取消
if (this.value == true) {
this.emitEvent();
this.$emit('input', !this.value);
} else {
// 如果超出最多可选项,提示
if(this.parent && checkedNum >= this.parent.max) {
return this.$u.toast(`最多可选${this.parent.max}项`);
}
// 如果原来为未选中状态,需要选中的数量少于父组件中设置的max值,才可以选中
this.emitEvent();
this.$emit('input', !this.value);
}
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-checkbox {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
align-items: center;
overflow: hidden;
user-select: none;
line-height: 1.8;
&__icon-wrap {
color: $u-content-color;
flex: none;
display: -webkit-flex;
@include vue-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 42rpx;
height: 42rpx;
color: transparent;
text-align: center;
transition-property: color, border-color, background-color;
font-size: 20px;
border: 1px solid #c8c9cc;
transition-duration: 0.2s;
/* #ifdef MP-TOUTIAO */
// 头条小程序兼容性问题,需要设置行高为0,否则图标偏下
&__icon {
line-height: 0;
}
/* #endif */
&--circle {
border-radius: 100%;
}
&--square {
border-radius: 6rpx;
}
&--checked {
color: #fff;
background-color: $u-type-primary;
border-color: $u-type-primary;
}
&--disabled {
background-color: #ebedf0;
border-color: #c8c9cc;
}
&--disabled--checked {
color: #c8c9cc !important;
}
}
&__label {
word-wrap: break-word;
margin-left: 10rpx;
margin-right: 24rpx;
color: $u-content-color;
font-size: 30rpx;
&--disabled {
color: #c8c9cc;
}
}
}
</style>
@@ -1,220 +0,0 @@
<template>
<view
class="u-circle-progress"
:style="{
width: widthPx + 'px',
height: widthPx + 'px',
backgroundColor: bgColor
}"
>
<!-- 支付宝小程序不支持canvas-id属性,必须用id属性 -->
<canvas
class="u-canvas-bg"
:canvas-id="elBgId"
:id="elBgId"
:style="{
width: widthPx + 'px',
height: widthPx + 'px'
}"
></canvas>
<canvas
class="u-canvas"
:canvas-id="elId"
:id="elId"
:style="{
width: widthPx + 'px',
height: widthPx + 'px'
}"
></canvas>
<slot></slot>
</view>
</template>
<script>
/**
* circleProgress 环形进度条
* @description 展示操作或任务的当前进度,比如上传文件,是一个圆形的进度条。注意:此组件的percent值只能动态增加,不能动态减少。
* @tutorial https://www.uviewui.com/components/circleProgress.html
* @property {String Number} percent 圆环进度百分比值,为数值类型,0-100
* @property {String} inactive-color 圆环的底色,默认为灰色(该值无法动态变更)(默认#ececec)
* @property {String} active-color 圆环激活部分的颜色(该值无法动态变更)(默认#19be6b)
* @property {String Number} width 整个圆环组件的宽度,高度默认等于宽度值,单位rpx(默认200)
* @property {String Number} border-width 圆环的边框宽度,单位rpx(默认14)
* @property {String Number} duration 整个圆环执行一圈的时间,单位ms(默认呢1500)
* @property {String} type 如设置,active-color值将会失效
* @property {String} bg-color 整个组件背景颜色,默认为白色
* @example <u-circle-progress active-color="#2979ff" :percent="80"></u-circle-progress>
*/
export default {
name: 'u-circle-progress',
props: {
// 圆环进度百分比值
percent: {
type: Number,
default: 0,
// 限制值在0到100之间
validator: val => {
return val >= 0 && val <= 100;
}
},
// 底部圆环的颜色(灰色的圆环)
inactiveColor: {
type: String,
default: '#ececec'
},
// 圆环激活部分的颜色
activeColor: {
type: String,
default: '#19be6b'
},
// 圆环线条的宽度,单位rpx
borderWidth: {
type: [Number, String],
default: 14
},
// 整个圆形的宽度,单位rpx
width: {
type: [Number, String],
default: 200
},
// 整个圆环执行一圈的时间,单位ms
duration: {
type: [Number, String],
default: 1500
},
// 主题类型
type: {
type: String,
default: ''
},
// 整个圆环进度区域的背景色
bgColor: {
type: String,
default: '#ffffff'
}
},
data() {
return {
// #ifdef MP-WEIXIN
elBgId: 'uCircleProgressBgId', // 微信小程序中不能使用this.$u.guid()形式动态生成id值,否则会报错
elId: 'uCircleProgressElId',
// #endif
// #ifndef MP-WEIXIN
elBgId: this.$u.guid(), // 非微信端的时候,需用动态的id,否则一个页面多个圆形进度条组件数据会混乱
elId: this.$u.guid(),
// #endif
widthPx: uni.upx2px(this.width), // 转成px后的整个组件的背景宽度
borderWidthPx: uni.upx2px(this.borderWidth), // 转成px后的圆环的宽度
startAngle: -Math.PI / 2, // canvas画圆的起始角度,默认为3点钟方向,定位到12点钟方向
progressContext: null, // 活动圆的canvas上下文
newPercent: 0, // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
oldPercent: 0 // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
};
},
watch: {
percent(nVal, oVal = 0) {
if (nVal > 100) nVal = 100;
if (nVal < 0) oVal = 0;
// 此值其实等于this.percent,命名一个新
this.newPercent = nVal;
this.oldPercent = oVal;
setTimeout(() => {
// 无论是百分比值增加还是减少,需要操作还是原来的旧的百分比值
// 将此值减少或者新增到新的百分比值
this.drawCircleByProgress(oVal);
}, 50);
}
},
created() {
// 赋值,用于加载后第一个画圆使用
this.newPercent = this.percent;
this.oldPercent = 0;
},
computed: {
// 有type主题时,优先起作用
circleColor() {
if (['success', 'error', 'info', 'primary', 'warning'].indexOf(this.type) >= 0) return this.$u.color[this.type];
else return this.activeColor;
}
},
mounted() {
// 在h5端,必须要做一点延时才起作用,this.$nextTick()无效(HX2.4.7)
setTimeout(() => {
this.drawProgressBg();
this.drawCircleByProgress(this.oldPercent);
}, 50);
},
methods: {
drawProgressBg() {
let ctx = uni.createCanvasContext(this.elBgId, this);
ctx.setLineWidth(this.borderWidthPx); // 设置圆环宽度
ctx.setStrokeStyle(this.inactiveColor); // 线条颜色
ctx.beginPath(); // 开始描绘路径
// 设置一个原点(110,110),半径为100的圆的路径到当前路径
let radius = this.widthPx / 2;
ctx.arc(radius, radius, radius - this.borderWidthPx, 0, 2 * Math.PI, false);
ctx.stroke(); // 对路径进行描绘
ctx.draw();
},
drawCircleByProgress(progress) {
// 第一次操作进度环时将上下文保存到了this.data中,直接使用即可
let ctx = this.progressContext;
if (!ctx) {
ctx = uni.createCanvasContext(this.elId, this);
this.progressContext = ctx;
}
// 表示进度的两端为圆形
ctx.setLineCap('round');
// 设置线条的宽度和颜色
ctx.setLineWidth(this.borderWidthPx);
ctx.setStrokeStyle(this.circleColor);
// 将总过渡时间除以100,得出每修改百分之一进度所需的时间
let time = Math.floor(this.duration / 100);
// 结束角的计算依据为:将2π分为100份,乘以当前的进度值,得出终止点的弧度值,加起始角,为整个圆从默认的
// 3点钟方向开始画图,转为更好理解的12点钟方向开始作图,这需要起始角和终止角同时加上this.startAngle值
let endAngle = ((2 * Math.PI) / 100) * progress + this.startAngle;
ctx.beginPath();
// 半径为整个canvas宽度的一半
let radius = this.widthPx / 2;
ctx.arc(radius, radius, radius - this.borderWidthPx, this.startAngle, endAngle, false);
ctx.stroke();
ctx.draw();
// 如果变更后新值大于旧值,意味着增大了百分比
if (this.newPercent > this.oldPercent) {
// 每次递增百分之一
progress++;
// 如果新增后的值,大于需要设置的值百分比值,停止继续增加
if (progress > this.newPercent) return;
} else {
// 同理于上面
progress--;
if (progress < this.newPercent) return;
}
setTimeout(() => {
// 定时器,每次操作间隔为time值,为了让进度条有动画效果
this.drawCircleByProgress(progress);
}, time);
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-circle-progress {
position: relative;
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
align-items: center;
justify-content: center;
}
.u-canvas-bg {
position: absolute;
}
.u-canvas {
position: absolute;
}
</style>
@@ -1,147 +0,0 @@
<template>
<view class="u-progress" :style="{
borderRadius: round ? '100rpx' : 0,
height: height + 'rpx',
backgroundColor: inactiveColor
}">
<view :class="[
type ? `u-type-${type}-bg` : '',
striped ? 'u-striped' : '',
striped && stripedActive ? 'u-striped-active' : ''
]" class="u-active" :style="[progressStyle]">
<slot v-if="$slots.default || $slots.$default" />
<block v-else-if="showPercent">
{{percent + '%'}}
</block>
</view>
</view>
</template>
<script>
/**
* lineProgress 线型进度条
* @description 展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。
* @tutorial https://www.uviewui.com/components/lineProgress.html
* @property {String Number} percent 进度条百分比值,为数值类型,0-100
* @property {Boolean} round 进度条两端是否为半圆(默认true)
* @property {String} type 如设置,active-color值将会失效
* @property {String} active-color 进度条激活部分的颜色(默认#19be6b)
* @property {String} inactive-color 进度条的底色(默认#ececec)
* @property {Boolean} show-percent 是否在进度条内部显示当前的百分比值数值(默认true)
* @property {String Number} height 进度条的高度,单位rpx(默认28)
* @property {Boolean} striped 是否显示进度条激活部分的条纹(默认false)
* @property {Boolean} striped-active 条纹是否具有动态效果(默认false)
* @example <u-line-progress :percent="70" :show-percent="true"></u-line-progress>
*/
export default {
name: "u-line-progress",
props: {
// 两端是否显示半圆形
round: {
type: Boolean,
default: true
},
// 主题颜色
type: {
type: String,
default: ''
},
// 激活部分的颜色
activeColor: {
type: String,
default: '#19be6b'
},
inactiveColor: {
type: String,
default: '#ececec'
},
// 进度百分比,数值
percent: {
type: Number,
default: 0
},
// 是否在进度条内部显示百分比的值
showPercent: {
type: Boolean,
default: true
},
// 进度条的高度,单位rpx
height: {
type: [Number, String],
default: 28
},
// 是否显示条纹
striped: {
type: Boolean,
default: false
},
// 条纹是否显示活动状态
stripedActive: {
type: Boolean,
default: false
}
},
data() {
return {
}
},
computed: {
progressStyle() {
let style = {};
style.width = this.percent + '%';
if(this.activeColor) style.backgroundColor = this.activeColor;
return style;
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-progress {
overflow: hidden;
height: 15px;
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
align-items: center;
width: 100%;
border-radius: 100rpx;
}
.u-active {
width: 0;
height: 100%;
align-items: center;
@include vue-flex;
justify-items: flex-end;
justify-content: space-around;
font-size: 20rpx;
color: #ffffff;
transition: all 0.4s ease;
}
.u-striped {
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-size: 39px 39px;
}
.u-striped-active {
animation: progress-stripes 2s linear infinite;
}
@keyframes progress-stripes {
0% {
background-position: 0 0;
}
100% {
background-position: 39px 0;
}
}
</style>
-156
View File
@@ -1,156 +0,0 @@
<template>
<view class="u-col" :class="[
'u-col-' + span
]" :style="{
padding: `0 ${Number(gutter)/2 + 'rpx'}`,
marginLeft: 100 / 12 * offset + '%',
flex: `0 0 ${100 / 12 * span}%`,
alignItems: uAlignItem,
justifyContent: uJustify,
textAlign: textAlign
}"
@tap="click">
<slot></slot>
</view>
</template>
<script>
/**
* col 布局单元格
* @description 通过基础的 12 分栏,迅速简便地创建布局(搭配<u-row>使用)
* @tutorial https://www.uviewui.com/components/layout.html
* @property {String Number} span 栅格占据的列数,总12等分(默认0)
* @property {String} text-align 文字水平对齐方式(默认left)
* @property {String Number} offset 分栏左边偏移,计算方式与span相同(默认0)
* @example <u-col span="3"><view class="demo-layout bg-purple"></view></u-col>
*/
export default {
name: "u-col",
props: {
// 占父容器宽度的多少等分,总分为12份
span: {
type: [Number, String],
default: 12
},
// 指定栅格左侧的间隔数(总12栏)
offset: {
type: [Number, String],
default: 0
},
// 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`)
justify: {
type: String,
default: 'start'
},
// 垂直对齐方式,可选值为top、center、bottom
align: {
type: String,
default: 'center'
},
// 文字对齐方式
textAlign: {
type: String,
default: 'left'
},
// 是否阻止事件传播
stop: {
type: Boolean,
default: true
}
},
data() {
return {
gutter: 20, // 给col添加间距,左右边距各占一半,从父组件u-row获取
}
},
created() {
this.parent = false;
},
mounted() {
// 获取父组件实例,并赋值给对应的参数
this.parent = this.$u.$parent.call(this, 'u-row');
if (this.parent) {
this.gutter = this.parent.gutter;
}
},
computed: {
uJustify() {
if (this.justify == 'end' || this.justify == 'start') return 'flex-' + this.justify;
else if (this.justify == 'around' || this.justify == 'between') return 'space-' + this.justify;
else return this.justify;
},
uAlignItem() {
if (this.align == 'top') return 'flex-start';
if (this.align == 'bottom') return 'flex-end';
else return this.align;
}
},
methods: {
click(e) {
this.$emit('click');
}
}
}
</script>
<style lang="scss">
@import "../../libs/css/style.components.scss";
.u-col {
/* #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO */
float: left;
/* #endif */
}
.u-col-0 {
width: 0;
}
.u-col-1 {
width: calc(100%/12);
}
.u-col-2 {
width: calc(100%/12 * 2);
}
.u-col-3 {
width: calc(100%/12 * 3);
}
.u-col-4 {
width: calc(100%/12 * 4);
}
.u-col-5 {
width: calc(100%/12 * 5);
}
.u-col-6 {
width: calc(100%/12 * 6);
}
.u-col-7 {
width: calc(100%/12 * 7);
}
.u-col-8 {
width: calc(100%/12 * 8);
}
.u-col-9 {
width: calc(100%/12 * 9);
}
.u-col-10 {
width: calc(100%/12 * 10);
}
.u-col-11 {
width: calc(100%/12 * 11);
}
.u-col-12 {
width: calc(100%/12 * 12);
}
</style>
@@ -1,204 +0,0 @@
<template>
<view class="u-collapse-item" :style="[itemStyle]">
<view :hover-stay-time="200" class="u-collapse-head" @tap.stop="headClick" :hover-class="hoverClass" :style="[headStyle]">
<block v-if="!$slots['title-all']">
<view v-if="!$slots['title']" class="u-collapse-title u-line-1" :style="[{ textAlign: align ? align : 'left' },
isShow && activeStyle && !arrow ? activeStyle : '']">
{{ title }}
</view>
<slot v-else name="title" />
<view class="u-icon-wrap">
<u-icon v-if="arrow" :color="arrowColor" :class="{ 'u-arrow-down-icon-active': isShow }"
class="u-arrow-down-icon" name="arrow-down"></u-icon>
</view>
</block>
<slot v-else name="title-all" />
</view>
<view class="u-collapse-body" :style="[{
height: isShow ? height + 'px' : '0'
}]">
<view class="u-collapse-content" :id="elId" :style="[bodyStyle]">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
/**
* collapseItem 手风琴Item
* @description 通过折叠面板收纳内容区域(搭配u-collapse使用)
* @tutorial https://www.uviewui.com/components/collapse.html
* @property {String} title 面板标题
* @property {String Number} index 主要用于事件的回调,标识那个Item被点击
* @property {Boolean} disabled 面板是否可以打开或收起(默认false)
* @property {Boolean} open 设置某个面板的初始状态是否打开(默认false)
* @property {String Number} name 唯一标识符,如不设置,默认用当前collapse-item的索引值
* @property {String} align 标题的对齐方式(默认left)
* @property {Object} active-style 不显示箭头时,可以添加当前选择的collapse-item活动样式,对象形式
* @event {Function} change 某个item被打开或者收起时触发
* @example <u-collapse-item :title="item.head" v-for="(item, index) in itemList" :key="index">{{item.body}}</u-collapse-item>
*/
export default {
name: "u-collapse-item",
props: {
// 标题
title: {
type: String,
default: ''
},
// 标题的对齐方式
align: {
type: String,
default: 'left'
},
// 是否可以点击收起
disabled: {
type: Boolean,
default: false
},
// collapse显示与否
open: {
type: Boolean,
default: false
},
// 唯一标识符
name: {
type: [Number, String],
default: ''
},
//活动样式
activeStyle: {
type: Object,
default () {
return {}
}
},
// 标识当前为第几个
index: {
type: [String, Number],
default: ''
}
},
data() {
return {
isShow: false,
elId: this.$u.guid(),
height: 0, // body内容的高度
headStyle: {}, // 头部样式,对象形式
bodyStyle: {}, // 主体部分样式
itemStyle: {}, // 每个item的整体样式
arrowColor: '', // 箭头的颜色
hoverClass: '', // 头部按下时的效果样式类
arrow: true, // 是否显示右侧箭头
};
},
watch: {
open(val) {
this.isShow = val;
}
},
created() {
this.parent = false;
// 获取u-collapse的信息,放在u-collapse是为了方便,不用每个u-collapse-item写一遍
this.isShow = this.open;
},
methods: {
// 异步获取内容,或者动态修改了内容时,需要重新初始化
init() {
this.parent = this.$u.$parent.call(this, 'u-collapse');
if(this.parent) {
this.nameSync = this.name ? this.name : this.parent.childrens.length;
this.parent.childrens.push(this);
this.headStyle = this.parent.headStyle;
this.bodyStyle = this.parent.bodyStyle;
this.arrowColor = this.parent.arrowColor;
this.hoverClass = this.parent.hoverClass;
this.arrow = this.parent.arrow;
this.itemStyle = this.parent.itemStyle;
}
this.$nextTick(() => {
this.queryRect();
});
},
// 点击collapsehead头部
headClick() {
if (this.disabled) return;
if (this.parent && this.parent.accordion == true) {
this.parent.childrens.map(val => {
// 自身不设置为false,因为后面有this.isShow = !this.isShow;处理了
if (this != val) {
val.isShow = false;
}
});
}
this.isShow = !this.isShow;
// 触发本组件的事件
this.$emit('change', {
index: this.index,
show: this.isShow
})
// 只有在打开时才发出事件
if (this.isShow) this.parent && this.parent.onChange();
this.$forceUpdate();
},
// 查询内容高度
queryRect() {
// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://www.uviewui.com/js/getRect.html
// 组件内部一般用this.$uGetRect,对外的为this.$u.getRect,二者功能一致,名称不同
this.$uGetRect('#' + this.elId).then(res => {
this.height = res.height;
})
}
},
mounted() {
this.init();
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-collapse-head {
position: relative;
@include vue-flex;
justify-content: space-between;
align-items: center;
color: $u-main-color;
font-size: 30rpx;
line-height: 1;
padding: 8rpx 0 24rpx;
text-align: left;
}
.u-collapse-title {
flex: 1;
overflow: hidden;
}
.u-arrow-down-icon {
transition: all 0.3s;
margin-right: 20rpx;
margin-left: 14rpx;
}
.u-arrow-down-icon-active {
transform: rotate(180deg);
transform-origin: center center;
}
.u-collapse-body {
overflow: hidden;
transition: all 0.3s;
}
.u-collapse-content {
overflow: hidden;
font-size: 28rpx;
color: $u-tips-color;
text-align: left;
}
</style>
@@ -1,99 +0,0 @@
<template>
<view class="u-collapse">
<slot />
</view>
</template>
<script>
/**
* collapse 手风琴
* @description 通过折叠面板收纳内容区域
* @tutorial https://www.uviewui.com/components/collapse.html
* @property {Boolean} accordion 是否手风琴模式(默认true)
* @property {Boolean} arrow 是否显示标题右侧的箭头(默认true)
* @property {String} arrow-color 标题右侧箭头的颜色(默认#909399)
* @property {Object} head-style 标题自定义样式,对象形式
* @property {Object} body-style 主体自定义样式,对象形式
* @property {String} hover-class 样式类名,按下时有效(默认u-hover-class)
* @event {Function} change 当前激活面板展开时触发(如果是手风琴模式,参数activeNames类型为String,否则为Array)
* @example <u-collapse></u-collapse>
*/
export default {
name:"u-collapse",
props: {
// 是否手风琴模式
accordion: {
type: Boolean,
default: true
},
// 头部的样式
headStyle: {
type: Object,
default () {
return {}
}
},
// 主体的样式
bodyStyle: {
type: Object,
default () {
return {}
}
},
// 每一个item的样式
itemStyle: {
type: Object,
default () {
return {}
}
},
// 是否显示右侧的箭头
arrow: {
type: Boolean,
default: true
},
// 箭头的颜色
arrowColor: {
type: String,
default: '#909399'
},
// 标题部分按压时的样式类,"none"为无效果
hoverClass: {
type: String,
default: 'u-hover-class'
}
},
created() {
this.childrens = []
},
data() {
return {
}
},
methods: {
// 重新初始化一次内部的所有子元素的高度计算,用于异步获取数据渲染的情况
init() {
this.childrens.forEach((vm, index) => {
vm.init();
})
},
// collapse item被点击,由collapse item调用父组件方法
onChange() {
let activeItem = [];
this.childrens.forEach((vm, index) => {
if (vm.isShow) {
activeItem.push(vm.nameSync);
}
})
// 如果是手风琴模式,只有一个匹配结果,也即activeItem长度为1,将其转为字符串
if (this.accordion) activeItem = activeItem.join('');
this.$emit('change', activeItem);
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
</style>
@@ -1,237 +0,0 @@
<template>
<view
class="u-notice-bar"
:style="{
background: computeBgColor,
padding: padding
}"
:class="[
type ? `u-type-${type}-light-bg` : ''
]"
>
<view class="u-icon-wrap">
<u-icon class="u-left-icon" v-if="volumeIcon" name="volume-fill" :size="volumeSize" :color="computeColor"></u-icon>
</view>
<swiper :disable-touch="disableTouch" @change="change" :autoplay="autoplay && playState == 'play'" :vertical="vertical" circular :interval="duration" class="u-swiper">
<swiper-item v-for="(item, index) in list" :key="index" class="u-swiper-item">
<view
class="u-news-item u-line-1"
:style="[textStyle]"
@tap="click(index)"
:class="['u-type-' + type]"
>
{{ item }}
</view>
</swiper-item>
</swiper>
<view class="u-icon-wrap">
<u-icon @click="getMore" class="u-right-icon" v-if="moreIcon" name="arrow-right" :size="26" :color="computeColor"></u-icon>
<u-icon @click="close" class="u-right-icon" v-if="closeIcon" name="close" :size="24" :color="computeColor"></u-icon>
</view>
</view>
</template>
<script>
export default {
props: {
// 显示的内容,数组
list: {
type: Array,
default() {
return [];
}
},
// 显示的主题,success|error|primary|info|warning
type: {
type: String,
default: 'warning'
},
// 是否显示左侧的音量图标
volumeIcon: {
type: Boolean,
default: true
},
// 是否显示右侧的右箭头图标
moreIcon: {
type: Boolean,
default: false
},
// 是否显示右侧的关闭图标
closeIcon: {
type: Boolean,
default: false
},
// 是否自动播放
autoplay: {
type: Boolean,
default: true
},
// 文字颜色,各图标也会使用文字颜色
color: {
type: String,
default: ''
},
// 背景颜色
bgColor: {
type: String,
default: ''
},
// 滚动方向,row-水平滚动,column-垂直滚动
direction: {
type: String,
default: 'row'
},
// 是否显示
show: {
type: Boolean,
default: true
},
// 字体大小,单位rpx
fontSize: {
type: [Number, String],
default: 26
},
// 滚动一个周期的时间长,单位ms
duration: {
type: [Number, String],
default: 2000
},
// 音量喇叭的大小
volumeSize: {
type: [Number, String],
default: 34
},
// 水平滚动时的滚动速度,即每秒滚动多少rpx,这有利于控制文字无论多少时,都能有一个恒定的速度
speed: {
type: Number,
default: 160
},
// 水平滚动时,是否采用衔接形式滚动
isCircular: {
type: Boolean,
default: true
},
// 滚动方向,horizontal-水平滚动,vertical-垂直滚动
mode: {
type: String,
default: 'horizontal'
},
// 播放状态,play-播放,paused-暂停
playState: {
type: String,
default: 'play'
},
// 是否禁止用手滑动切换
// 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序
disableTouch: {
type: Boolean,
default: true
},
// 通知的边距
padding: {
type: [Number, String],
default: '18rpx 24rpx'
}
},
computed: {
// 计算字体颜色,如果没有自定义的,就用uview主题颜色
computeColor() {
if (this.color) return this.color;
// 如果是无主题,就默认使用content-color
else if(this.type == 'none') return '#606266';
else return this.type;
},
// 文字内容的样式
textStyle() {
let style = {};
if (this.color) style.color = this.color;
else if(this.type == 'none') style.color = '#606266';
style.fontSize = this.fontSize + 'rpx';
return style;
},
// 垂直或者水平滚动
vertical() {
if(this.mode == 'horizontal') return false;
else return true;
},
// 计算背景颜色
computeBgColor() {
if (this.bgColor) return this.bgColor;
else if(this.type == 'none') return 'transparent';
}
},
data() {
return {
// animation: false
};
},
methods: {
// 点击通告栏
click(index) {
this.$emit('click', index);
},
// 点击关闭按钮
close() {
this.$emit('close');
},
// 点击更多箭头按钮
getMore() {
this.$emit('getMore');
},
change(e) {
let index = e.detail.current;
if(index == this.list.length - 1) {
this.$emit('end');
}
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-notice-bar {
width: 100%;
@include vue-flex;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
padding: 18rpx 24rpx;
overflow: hidden;
}
.u-swiper {
font-size: 26rpx;
height: 32rpx;
@include vue-flex;
align-items: center;
flex: 1;
margin-left: 12rpx;
}
.u-swiper-item {
@include vue-flex;
align-items: center;
overflow: hidden;
}
.u-news-item {
overflow: hidden;
}
.u-right-icon {
margin-left: 12rpx;
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
align-items: center;
}
.u-left-icon {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
align-items: center;
}
</style>
@@ -1,318 +0,0 @@
<template>
<view class="u-countdown">
<view class="u-countdown-item" :style="[itemStyle]" v-if="showDays && (hideZeroDay || (!hideZeroDay && d != '00'))">
<view class="u-countdown-time" :style="[letterStyle]">
{{ d }}
</view>
</view>
<view
class="u-countdown-colon"
:style="{fontSize: separatorSize + 'rpx', color: separatorColor, paddingBottom: separator == 'colon' ? '4rpx' : 0}"
v-if="showDays && (hideZeroDay || (!hideZeroDay && d != '00'))"
>
{{ separator == 'colon' ? ':' : '天' }}
</view>
<view class="u-countdown-item" :style="[itemStyle]" v-if="showHours">
<view class="u-countdown-time" :style="{ fontSize: fontSize + 'rpx', color: color}">
{{ h }}
</view>
</view>
<view
class="u-countdown-colon"
:style="{fontSize: separatorSize + 'rpx', color: separatorColor, paddingBottom: separator == 'colon' ? '4rpx' : 0}"
v-if="showHours"
>
{{ separator == 'colon' ? ':' : '时' }}
</view>
<view class="u-countdown-item" :style="[itemStyle]" v-if="showMinutes">
<view class="u-countdown-time" :style="{ fontSize: fontSize + 'rpx', color: color}">
{{ i }}
</view>
</view>
<view
class="u-countdown-colon"
:style="{fontSize: separatorSize + 'rpx', color: separatorColor, paddingBottom: separator == 'colon' ? '4rpx' : 0}"
v-if="showMinutes"
>
{{ separator == 'colon' ? ':' : '分' }}
</view>
<view class="u-countdown-item" :style="[itemStyle]" v-if="showSeconds">
<view class="u-countdown-time" :style="{ fontSize: fontSize + 'rpx', color: color}">
{{ s }}
</view>
</view>
<view
class="u-countdown-colon"
:style="{fontSize: separatorSize + 'rpx', color: separatorColor, paddingBottom: separator == 'colon' ? '4rpx' : 0}"
v-if="showSeconds && separator == 'zh'"
>
秒
</view>
</view>
</template>
<script>
/**
* countDown 倒计时
* @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。
* @tutorial https://www.uviewui.com/components/countDown.html
* @property {String Number} timestamp 倒计时,单位为秒
* @property {Boolean} autoplay 是否自动开始倒计时,如果为false,需手动调用开始方法。见官网说明(默认true)
* @property {String} separator 分隔符,colon为英文冒号,zh为中文(默认colon)
* @property {String Number} separator-size 分隔符的字体大小,单位rpx(默认30)
* @property {String} separator-color 分隔符的颜色(默认#303133)
* @property {String Number} font-size 倒计时字体大小,单位rpx(默认30)
* @property {Boolean} show-border 是否显示倒计时数字的边框(默认false)
* @property {Boolean} hide-zero-day 当"天"的部分为0时,隐藏该字段 (默认true)
* @property {String} border-color 数字边框的颜色(默认#303133)
* @property {String} bg-color 倒计时数字的背景颜色(默认#ffffff)
* @property {String} color 倒计时数字的颜色(默认#303133)
* @property {String} height 数字高度值(宽度等同此值),设置边框时看情况是否需要设置此值,单位rpx(默认auto)
* @property {Boolean} show-days 是否显示倒计时的"天"部分(默认true)
* @property {Boolean} show-hours 是否显示倒计时的"时"部分(默认true)
* @property {Boolean} show-minutes 是否显示倒计时的"分"部分(默认true)
* @property {Boolean} show-seconds 是否显示倒计时的"秒"部分(默认true)
* @event {Function} end 倒计时结束
* @event {Function} change 每秒触发一次,回调为当前剩余的倒计秒数
* @example <u-count-down ref="uCountDown" :timestamp="86400" :autoplay="false"></u-count-down>
*/
export default {
name: 'u-count-down',
props: {
// 倒计时的时间,秒为单位
timestamp: {
type: [Number, String],
default: 0
},
// 是否自动开始倒计时
autoplay: {
type: Boolean,
default: true
},
// 用英文冒号(colon)或者中文(zh)当做分隔符,false的时候为中文,如:"11:22"或"11时22秒"
separator: {
type: String,
default: 'colon'
},
// 分隔符的大小,单位rpx
separatorSize: {
type: [Number, String],
default: 30
},
// 分隔符颜色
separatorColor: {
type: String,
default: "#303133"
},
// 字体颜色
color: {
type: String,
default: '#303133'
},
// 字体大小,单位rpx
fontSize: {
type: [Number, String],
default: 30
},
// 背景颜色
bgColor: {
type: String,
default: '#fff'
},
// 数字框高度,单位rpx
height: {
type: [Number, String],
default: 'auto'
},
// 是否显示数字框
showBorder: {
type: Boolean,
default: false
},
// 边框颜色
borderColor: {
type: String,
default: '#303133'
},
// 是否显示秒
showSeconds: {
type: Boolean,
default: true
},
// 是否显示分钟
showMinutes: {
type: Boolean,
default: true
},
// 是否显示小时
showHours: {
type: Boolean,
default: true
},
// 是否显示“天”
showDays: {
type: Boolean,
default: true
},
// 当"天"的部分为0时,不显示
hideZeroDay: {
type: Boolean,
default: false
}
},
watch: {
// 监听时间戳的变化
timestamp(newVal, oldVal) {
// 如果倒计时间发生变化,清除定时器,重新开始倒计时
this.clearTimer();
this.start();
}
},
data() {
return {
d: '00', // 天的默认值
h: '00', // 小时的默认值
i: '00', // 分钟的默认值
s: '00', // 秒的默认值
timer: null ,// 定时器
seconds: 0, // 记录不停倒计过程中变化的秒数
};
},
computed: {
// 倒计时item的样式,item为分别的时分秒部分的数字
itemStyle() {
let style = {};
if(this.height) {
style.height = this.height + 'rpx';
style.width = this.height + 'rpx';
}
if(this.showBorder) {
style.borderStyle = 'solid';
style.borderColor = this.borderColor;
style.borderWidth = '1px';
}
if(this.bgColor) {
style.backgroundColor = this.bgColor;
}
return style;
},
// 倒计时数字的样式
letterStyle() {
let style = {};
if(this.fontSize) style.fontSize = this.fontSize + 'rpx';
if(this.color) style.color = this.color;
return style;
}
},
mounted() {
// 如果自动倒计时
this.autoplay && this.timestamp && this.start();
},
methods: {
// 倒计时
start() {
// 避免可能出现的倒计时重叠情况
this.clearTimer();
if (this.timestamp <= 0) return;
this.seconds = Number(this.timestamp);
this.formatTime(this.seconds);
this.timer = setInterval(() => {
this.seconds--;
// 发出change事件
this.$emit('change', this.seconds);
if (this.seconds < 0) {
return this.end();
}
this.formatTime(this.seconds);
}, 1000);
},
// 格式化时间
formatTime(seconds) {
// 小于等于0的话,结束倒计时
seconds <= 0 && this.end();
let [day, hour, minute, second] = [0, 0, 0, 0];
day = Math.floor(seconds / (60 * 60 * 24));
// 判断是否显示“天”参数,如果不显示,将天部分的值,加入到小时中
// hour为给后面计算秒和分等用的(基于显示天的前提下计算)
hour = Math.floor(seconds / (60 * 60)) - day * 24;
// showHour为需要显示的小时
let showHour = null;
if(this.showDays) {
showHour = hour;
} else {
// 如果不显示天数,将“天”部分的时间折算到小时中去
showHour = Math.floor(seconds / (60 * 60));
}
minute = Math.floor(seconds / 60) - hour * 60 - day * 24 * 60;
second = Math.floor(seconds) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60;
// 如果小于10,在前面补上一个"0"
showHour = showHour < 10 ? '0' + showHour : showHour;
minute = minute < 10 ? '0' + minute : minute;
second = second < 10 ? '0' + second : second;
day = day < 10 ? '0' + day : day;
this.d = day;
this.h = showHour;
this.i = minute;
this.s = second;
},
// 停止倒计时
end() {
this.clearTimer();
this.$emit('end', {});
},
// 清除定时器
clearTimer() {
if(this.timer) {
// 清除定时器
clearInterval(this.timer);
this.timer = null;
}
}
},
beforeDestroy() {
clearInterval(this.timer);
this.timer = null;
}
};
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-countdown {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
align-items: center;
}
.u-countdown-item {
@include vue-flex;
align-items: center;
justify-content: center;
padding: 2rpx;
border-radius: 6rpx;
white-space: nowrap;
transform: translateZ(0);
}
.u-countdown-time {
margin: 0;
padding: 0;
line-height: 1;
}
.u-countdown-colon {
@include vue-flex;
justify-content: center;
padding: 0 5rpx;
line-height: 1;
align-items: center;
padding-bottom: 4rpx;
}
.u-countdown-scale {
transform: scale(0.9);
transform-origin: center center;
}
</style>
@@ -1,241 +0,0 @@
<template>
<view
class="u-count-num"
:style="{
fontSize: fontSize + 'rpx',
fontWeight: bold ? 'bold' : 'normal',
color: color
}"
>
{{ displayValue }}
</view>
</template>
<script>
/**
* countTo 数字滚动
* @description 该组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值。
* @tutorial https://www.uviewui.com/components/countTo.html
* @property {String Number} start-val 开始值
* @property {String Number} end-val 结束值
* @property {String Number} duration 滚动过程所需的时间,单位ms(默认2000)
* @property {Boolean} autoplay 是否自动开始滚动(默认true)
* @property {String Number} decimals 要显示的小数位数,见官网说明(默认0)
* @property {Boolean} use-easing 滚动结束时,是否缓动结尾,见官网说明(默认true)
* @property {String} separator 千位分隔符,见官网说明
* @property {String} color 字体颜色(默认#303133)
* @property {String Number} font-size 字体大小,单位rpx(默认50)
* @property {Boolean} bold 字体是否加粗(默认false)
* @event {Function} end 数值滚动到目标值时触发
* @example <u-count-to ref="uCountTo" :end-val="endVal" :autoplay="autoplay"></u-count-to>
*/
export default {
name: 'u-count-to',
props: {
// 开始的数值,默认从0增长到某一个数
startVal: {
type: [Number, String],
default: 0
},
// 要滚动的目标数值,必须
endVal: {
type: [Number, String],
default: 0,
required: true
},
// 滚动到目标数值的动画持续时间,单位为毫秒(ms)
duration: {
type: [Number, String],
default: 2000
},
// 设置数值后是否自动开始滚动
autoplay: {
type: Boolean,
default: true
},
// 要显示的小数位数
decimals: {
type: [Number, String],
default: 0
},
// 是否在即将到达目标数值的时候,使用缓慢滚动的效果
useEasing: {
type: Boolean,
default: true
},
// 十进制分割
decimal: {
type: [Number, String],
default: '.'
},
// 字体颜色
color: {
type: String,
default: '#303133'
},
// 字体大小
fontSize: {
type: [Number, String],
default: 50
},
// 是否加粗字体
bold: {
type: Boolean,
default: false
},
// 千位分隔符,类似金额的分割(¥23,321.05中的",")
separator: {
type: String,
default: ''
}
},
data() {
return {
localStartVal: this.startVal,
displayValue: this.formatNumber(this.startVal),
printVal: null,
paused: false, // 是否暂停
localDuration: Number(this.duration),
startTime: null, // 开始的时间
timestamp: null, // 时间戳
remaining: null, // 停留的时间
rAF: null,
lastTime: 0 // 上一次的时间
};
},
computed: {
countDown() {
return this.startVal > this.endVal;
}
},
watch: {
startVal() {
this.autoplay && this.start();
},
endVal() {
this.autoplay && this.start();
}
},
mounted() {
this.autoplay && this.start();
},
methods: {
easingFn(t, b, c, d) {
return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b;
},
requestAnimationFrame(callback) {
const currTime = new Date().getTime();
// 为了使setTimteout的尽可能的接近每秒60帧的效果
const timeToCall = Math.max(0, 16 - (currTime - this.lastTime));
const id = setTimeout(() => {
callback(currTime + timeToCall);
}, timeToCall);
this.lastTime = currTime + timeToCall;
return id;
},
cancelAnimationFrame(id) {
clearTimeout(id);
},
// 开始滚动数字
start() {
this.localStartVal = this.startVal;
this.startTime = null;
this.localDuration = this.duration;
this.paused = false;
this.rAF = this.requestAnimationFrame(this.count);
},
// 暂定状态,重新再开始滚动;或者滚动状态下,暂停
reStart() {
if (this.paused) {
this.resume();
this.paused = false;
} else {
this.stop();
this.paused = true;
}
},
// 暂停
stop() {
this.cancelAnimationFrame(this.rAF);
},
// 重新开始(暂停的情况下)
resume() {
this.startTime = null;
this.localDuration = this.remaining;
this.localStartVal = this.printVal;
this.requestAnimationFrame(this.count);
},
// 重置
reset() {
this.startTime = null;
this.cancelAnimationFrame(this.rAF);
this.displayValue = this.formatNumber(this.startVal);
},
count(timestamp) {
if (!this.startTime) this.startTime = timestamp;
this.timestamp = timestamp;
const progress = timestamp - this.startTime;
this.remaining = this.localDuration - progress;
if (this.useEasing) {
if (this.countDown) {
this.printVal = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration);
} else {
this.printVal = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration);
}
} else {
if (this.countDown) {
this.printVal = this.localStartVal - (this.localStartVal - this.endVal) * (progress / this.localDuration);
} else {
this.printVal = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration);
}
}
if (this.countDown) {
this.printVal = this.printVal < this.endVal ? this.endVal : this.printVal;
} else {
this.printVal = this.printVal > this.endVal ? this.endVal : this.printVal;
}
this.displayValue = this.formatNumber(this.printVal);
if (progress < this.localDuration) {
this.rAF = this.requestAnimationFrame(this.count);
} else {
this.$emit('end');
}
},
// 判断是否数字
isNumber(val) {
return !isNaN(parseFloat(val));
},
formatNumber(num) {
// 将num转为Number类型,因为其值可能为字符串数值,调用toFixed会报错
num = Number(num);
num = num.toFixed(Number(this.decimals));
num += '';
const x = num.split('.');
let x1 = x[0];
const x2 = x.length > 1 ? this.decimal + x[1] : '';
const rgx = /(\d+)(\d{3})/;
if (this.separator && !this.isNumber(this.separator)) {
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + this.separator + '$2');
}
}
return x1 + x2;
},
destroyed() {
this.cancelAnimationFrame(this.rAF);
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-count-num {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
text-align: center;
}
</style>
@@ -1,153 +0,0 @@
<template>
<view class="u-divider" :style="{
height: height == 'auto' ? 'auto' : height + 'rpx',
backgroundColor: bgColor,
marginBottom: marginBottom + 'rpx',
marginTop: marginTop + 'rpx'
}" @tap="click">
<view class="u-divider-line" :class="[type ? 'u-divider-line--bordercolor--' + type : '']" :style="[lineStyle]"></view>
<view v-if="useSlot" class="u-divider-text" :style="{
color: color,
fontSize: fontSize + 'rpx'
}"><slot /></view>
<view class="u-divider-line" :class="[type ? 'u-divider-line--bordercolor--' + type : '']" :style="[lineStyle]"></view>
</view>
</template>
<script>
/**
* divider 分割线
* @description 区隔内容的分割线,一般用于页面底部"没有更多"的提示。
* @tutorial https://www.uviewui.com/components/divider.html
* @property {String Number} half-width 文字左或右边线条宽度,数值或百分比,数值时单位为rpx
* @property {String} border-color 线条颜色,优先级高于type(默认#dcdfe6)
* @property {String} color 文字颜色(默认#909399)
* @property {String Number} fontSize 字体大小,单位rpx(默认26)
* @property {String} bg-color 整个divider的背景颜色(默认呢#ffffff)
* @property {String Number} height 整个divider的高度,单位rpx(默认40)
* @property {String} type 将线条设置主题色(默认primary)
* @property {Boolean} useSlot 是否使用slot传入内容,如果不传入,中间不会有空隙(默认true)
* @property {String Number} margin-top 与前一个组件的距离,单位rpx(默认0)
* @property {String Number} margin-bottom 与后一个组件的距离,单位rpx(0)
* @event {Function} click divider组件被点击时触发
* @example <u-divider color="#fa3534">长河落日圆</u-divider>
*/
export default {
name: 'u-divider',
props: {
// 单一边divider横线的宽度(数值),单位rpx。或者百分比
halfWidth: {
type: [Number, String],
default: 150
},
// divider横线的颜色,如设置,
borderColor: {
type: String,
default: '#dcdfe6'
},
// 主题色,可以是primary|info|success|warning|error之一值
type: {
type: String,
default: 'primary'
},
// 文字颜色
color: {
type: String,
default: '#909399'
},
// 文字大小,单位rpx
fontSize: {
type: [Number, String],
default: 26
},
// 整个divider的背景颜色
bgColor: {
type: String,
default: '#ffffff'
},
// 整个divider的高度单位rpx
height: {
type: [Number, String],
default: 'auto'
},
// 上边距
marginTop: {
type: [String, Number],
default: 0
},
// 下边距
marginBottom: {
type: [String, Number],
default: 0
},
// 是否使用slot传入内容,如果不用slot传入内容,先的中间就不会有空隙
useSlot: {
type: Boolean,
default: true
}
},
computed: {
lineStyle() {
let style = {};
if(String(this.halfWidth).indexOf('%') != -1) style.width = this.halfWidth;
else style.width = this.halfWidth + 'rpx';
// borderColor优先级高于type值
if(this.borderColor) style.borderColor = this.borderColor;
return style;
}
},
methods: {
click() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-divider {
width: 100%;
position: relative;
text-align: center;
@include vue-flex;
justify-content: center;
align-items: center;
overflow: hidden;
flex-direction: row;
}
.u-divider-line {
border-bottom: 1px solid $u-border-color;
transform: scale(1, 0.5);
transform-origin: center;
&--bordercolor--primary {
border-color: $u-type-primary;
}
&--bordercolor--success {
border-color: $u-type-success;
}
&--bordercolor--error {
border-color: $u-type-primary;
}
&--bordercolor--info {
border-color: $u-type-info;
}
&--bordercolor--warning {
border-color: $u-type-warning;
}
}
.u-divider-text {
white-space: nowrap;
padding: 0 16rpx;
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
}
</style>
@@ -1,132 +0,0 @@
<template>
<view class="u-dropdown-item" v-if="active" @touchmove.stop.prevent="() => {}" @tap.stop.prevent="() => {}">
<block v-if="!$slots.default && !$slots.$default">
<scroll-view scroll-y="true" :style="{
height: $u.addUnit(height)
}">
<view class="u-dropdown-item__options">
<u-cell-group>
<u-cell-item @click="cellClick(item.value)" :arrow="false" :title="item.label" v-for="(item, index) in options"
:key="index" :title-style="{
color: value == item.value ? activeColor : inactiveColor
}">
<u-icon v-if="value == item.value" name="checkbox-mark" :color="activeColor" size="32"></u-icon>
</u-cell-item>
</u-cell-group>
</view>
</scroll-view>
</block>
<slot v-else />
</view>
</template>
<script>
/**
* dropdown-item 下拉菜单
* @description 该组件一般用于向下展开菜单,同时可切换多个选项卡的场景
* @tutorial http://uviewui.com/components/dropdown.html
* @property {String | Number} v-model 双向绑定选项卡选择值
* @property {String} title 菜单项标题
* @property {Array[Object]} options 选项数据,如果传入了默认slot,此参数无效
* @property {Boolean} disabled 是否禁用此选项卡(默认false)
* @property {String | Number} duration 选项卡展开和收起的过渡时间,单位ms(默认300)
* @property {String | Number} height 弹窗下拉内容的高度(内容超出将会滚动)(默认auto)
* @example <u-dropdown-item title="标题"></u-dropdown-item>
*/
export default {
name: 'u-dropdown-item',
props: {
// 当前选中项的value值
value: {
type: [Number, String, Array],
default: ''
},
// 菜单项标题
title: {
type: [String, Number],
default: ''
},
// 选项数据,如果传入了默认slot,此参数无效
options: {
type: Array,
default () {
return []
}
},
// 是否禁用此菜单项
disabled: {
type: Boolean,
default: false
},
// 下拉弹窗的高度
height: {
type: [Number, String],
default: 'auto'
},
},
data() {
return {
active: false, // 当前项是否处于展开状态
activeColor: '#2979ff', // 激活时左边文字和右边对勾图标的颜色
inactiveColor: '#606266', // 未激活时左边文字和右边对勾图标的颜色
}
},
computed: {
// 监听props是否发生了变化,有些值需要传递给父组件u-dropdown,无法双向绑定
propsChange() {
return `${this.title}-${this.disabled}`;
}
},
watch: {
propsChange(n) {
// 当值变化时,通知父组件重新初始化,让父组件执行每个子组件的init()方法
// 将所有子组件数据重新整理一遍
if (this.parent) this.parent.init();
}
},
created() {
// 父组件的实例
this.parent = false;
},
methods: {
init() {
// 获取父组件u-dropdown
let parent = this.$u.$parent.call(this, 'u-dropdown');
if (parent) {
this.parent = parent;
// 将子组件的激活颜色配置为父组件设置的激活和未激活时的颜色
this.activeColor = parent.activeColor;
this.inactiveColor = parent.inactiveColor;
// 将本组件的this,放入到父组件的children数组中,让父组件可以操作本(子)组件的方法和属性
// push进去前,显判断是否已经存在了本实例,因为在子组件内部数据变化时,会通过父组件重新初始化子组件
let exist = parent.children.find(val => {
return this === val;
})
if (!exist) parent.children.push(this);
if (parent.children.length == 1) this.active = true;
// 父组件无法监听children的变化,故将子组件的title,传入父组件的menuList数组中
parent.menuList.push({
title: this.title,
disabled: this.disabled
});
}
},
// cell被点击
cellClick(value) {
// 修改通过v-model绑定的值
this.$emit('input', value);
// 通知父组件(u-dropdown)收起菜单
this.parent.close();
// 发出事件,抛出当前勾选项的value
this.$emit('change', value);
}
},
mounted() {
this.init();
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
</style>
@@ -1,298 +0,0 @@
<template>
<view class="u-dropdown">
<view class="u-dropdown__menu" :style="{
height: $u.addUnit(height)
}" :class="{
'u-border-bottom': borderBottom
}">
<view class="u-dropdown__menu__item" v-for="(item, index) in menuList" :key="index" @tap.stop="menuClick(index)">
<view class="u-flex">
<text class="u-dropdown__menu__item__text" :style="{
color: item.disabled ? '#c0c4cc' : (index === current || highlightIndex == index) ? activeColor : inactiveColor,
fontSize: $u.addUnit(titleSize)
}">{{item.title}}</text>
<view class="u-dropdown__menu__item__arrow" :class="{
'u-dropdown__menu__item__arrow--rotate': index === current
}">
<u-icon :custom-style="{display: 'flex'}" :name="menuIcon" :size="$u.addUnit(menuIconSize)" :color="index === current || highlightIndex == index ? activeColor : '#c0c4cc'"></u-icon>
</view>
</view>
</view>
</view>
<view class="u-dropdown__content" :style="[contentStyle, {
transition: `opacity ${duration / 1000}s linear`,
top: $u.addUnit(height),
height: contentHeight + 'px'
}]"
@tap="maskClick" @touchmove.stop.prevent>
<view @tap.stop.prevent class="u-dropdown__content__popup" :style="[popupStyle]">
<slot></slot>
</view>
<view class="u-dropdown__content__mask"></view>
</view>
</view>
</template>
<script>
/**
* dropdown 下拉菜单
* @description 该组件一般用于向下展开菜单,同时可切换多个选项卡的场景
* @tutorial http://uviewui.com/components/dropdown.html
* @property {String} active-color 标题和选项卡选中的颜色(默认#2979ff)
* @property {String} inactive-color 标题和选项卡未选中的颜色(默认#606266)
* @property {Boolean} close-on-click-mask 点击遮罩是否关闭菜单(默认true)
* @property {Boolean} close-on-click-self 点击当前激活项标题是否关闭菜单(默认true)
* @property {String | Number} duration 选项卡展开和收起的过渡时间,单位ms(默认300)
* @property {String | Number} height 标题菜单的高度,单位任意(默认80)
* @property {String | Number} border-radius 菜单展开内容下方的圆角值,单位任意(默认0)
* @property {Boolean} border-bottom 标题菜单是否显示下边框(默认false)
* @property {String | Number} title-size 标题的字体大小,单位任意,数值默认为rpx单位(默认28)
* @event {Function} open 下拉菜单被打开时触发
* @event {Function} close 下拉菜单被关闭时触发
* @example <u-dropdown></u-dropdown>
*/
export default {
name: 'u-dropdown',
props: {
// 菜单标题和选项的激活态颜色
activeColor: {
type: String,
default: '#2979ff'
},
// 菜单标题和选项的未激活态颜色
inactiveColor: {
type: String,
default: '#606266'
},
// 点击遮罩是否关闭菜单
closeOnClickMask: {
type: Boolean,
default: true
},
// 点击当前激活项标题是否关闭菜单
closeOnClickSelf: {
type: Boolean,
default: true
},
// 过渡时间
duration: {
type: [Number, String],
default: 300
},
// 标题菜单的高度,单位任意,数值默认为rpx单位
height: {
type: [Number, String],
default: 80
},
// 是否显示下边框
borderBottom: {
type: Boolean,
default: false
},
// 标题的字体大小
titleSize: {
type: [Number, String],
default: 28
},
// 下拉出来的内容部分的圆角值
borderRadius: {
type: [Number, String],
default: 0
},
// 菜单右侧的icon图标
menuIcon: {
type: String,
default: 'arrow-down'
},
// 菜单右侧图标的大小
menuIconSize: {
type: [Number, String],
default: 26
}
},
data() {
return {
showDropdown: true, // 是否打开下来菜单,
menuList: [], // 显示的菜单
active: false, // 下拉菜单的状态
// 当前是第几个菜单处于激活状态,小程序中此处不能写成false或者"",否则后续将current赋值为0,
// 无能的TX没有使用===而是使用==判断,导致程序认为前后二者没有变化,从而不会触发视图更新
current: 99999,
// 外层内容的样式,初始时处于底层,且透明
contentStyle: {
zIndex: -1,
opacity: 0
},
// 让某个菜单保持高亮的状态
highlightIndex: 99999,
contentHeight: 0
}
},
computed: {
// 下拉出来部分的样式
popupStyle() {
let style = {};
// 进行Y轴位移,展开状态时,恢复原位。收齐状态时,往上位移100%,进行隐藏
style.transform = `translateY(${this.active ? 0 : '-100%'})`
style['transition-duration'] = this.duration / 1000 + 's';
style.borderRadius = `0 0 ${this.$u.addUnit(this.borderRadius)} ${this.$u.addUnit(this.borderRadius)}`;
return style;
}
},
created() {
// 引用所有子组件(u-dropdown-item)的this,不能在data中声明变量,否则在微信小程序会造成循环引用而报错
this.children = [];
},
mounted() {
this.getContentHeight();
},
methods: {
init() {
// 当某个子组件内容变化时,触发父组件的init,父组件再让每一个子组件重新初始化一遍
// 以保证数据的正确性
this.menuList = [];
this.children.map(child => {
child.init();
})
},
// 点击菜单
menuClick(index) {
// 判断是否被禁用
if (this.menuList[index].disabled) return;
// 如果点击时的索引和当前激活项索引相同,意味着点击了激活项,需要收起下拉菜单
if (index === this.current && this.closeOnClickSelf) {
this.close();
// 等动画结束后,再移除下拉菜单中的内容,否则直接移除,也就没有下拉菜单收起的效果了
setTimeout(() => {
this.children[index].active = false;
}, this.duration)
return;
}
this.open(index);
},
// 打开下拉菜单
open(index) {
// 重置高亮索引,否则会造成多个菜单同时高亮
// this.highlightIndex = 9999;
// 展开时,设置下拉内容的样式
this.contentStyle = {
zIndex: 11,
}
// 标记展开状态以及当前展开项的索引
this.active = true;
this.current = index;
// 历遍所有的子元素,将索引匹配的项标记为激活状态,因为子元素是通过v-if控制切换的
// 之所以不是因display: none,是因为nvue没有display这个属性
this.children.map((val, idx) => {
val.active = index == idx ? true : false;
})
this.$emit('open', this.current);
},
// 设置下拉菜单处于收起状态
close() {
this.$emit('close', this.current);
// 设置为收起状态,同时current归位,设置为空字符串
this.active = false;
this.current = 99999;
// 下拉内容的样式进行调整,不透明度设置为0
this.contentStyle = {
zIndex: -1,
opacity: 0
}
},
// 点击遮罩
maskClick() {
// 如果不允许点击遮罩,直接返回
if (!this.closeOnClickMask) return;
this.close();
},
// 外部手动设置某个菜单高亮
highlight(index = undefined) {
this.highlightIndex = index !== undefined ? index : 99999;
},
// 获取下拉菜单内容的高度
getContentHeight() {
// 这里的原理为,因为dropdown组件是相对定位的,它的下拉出来的内容,必须给定一个高度
// 才能让遮罩占满菜单一下,直到屏幕底部的高度
// this.$u.sys()为uView封装的获取设备信息的方法
let windowHeight = this.$u.sys().windowHeight;
this.$uGetRect('.u-dropdown__menu').then(res => {
// 这里获取的是dropdown的尺寸,在H5上,uniapp获取尺寸是有bug的(以前提出修复过,后来又出现了此bug,目前hx2.8.11版本)
// H5端bug表现为元素尺寸的top值为导航栏底部到到元素的上边沿的距离,但是元素的bottom值确是导航栏顶部到元素底部的距离
// 二者是互相矛盾的,本质原因是H5端导航栏非原生,uni的开发者大意造成
// 这里取菜单栏的botton值合理的,不能用res.top,否则页面会造成滚动
this.contentHeight = windowHeight - res.bottom;
})
}
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-dropdown {
flex: 1;
width: 100%;
position: relative;
&__menu {
@include vue-flex;
position: relative;
z-index: 11;
height: 80rpx;
&__item {
flex: 1;
@include vue-flex;
justify-content: center;
align-items: center;
&__text {
font-size: 28rpx;
color: $u-content-color;
}
&__arrow {
margin-left: 6rpx;
transition: transform .3s;
align-items: center;
@include vue-flex;
&--rotate {
transform: rotate(180deg);
}
}
}
}
&__content {
position: absolute;
z-index: 8;
width: 100%;
left: 0px;
bottom: 0;
overflow: hidden;
&__mask {
position: absolute;
z-index: 9;
background: rgba(0, 0, 0, .3);
width: 100%;
left: 0;
top: 0;
bottom: 0;
}
&__popup {
position: relative;
z-index: 10;
transition: all 0.3s;
transform: translate3D(0, -100%, 0);
overflow: hidden;
}
}
}
</style>
-193
View File
@@ -1,193 +0,0 @@
<template>
<view class="u-empty" v-if="show" :style="{
marginTop: marginTop + 'rpx'
}">
<u-icon
:name="src ? src : 'empty-' + mode"
:custom-style="iconStyle"
:label="text ? text : icons[mode]"
label-pos="bottom"
:label-color="color"
:label-size="fontSize"
:size="iconSize"
:color="iconColor"
margin-top="14"
></u-icon>
<view class="u-slot-wrap">
<slot name="bottom"></slot>
</view>
</view>
</template>
<script>
/**
* empty 内容为空
* @description 该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个"没有内容"的场景, 我们精心挑选了十几个场景的图标,方便您使用。
* @tutorial https://www.uviewui.com/components/empty.html
* @property {String} color 文字颜色(默认#c0c4cc)
* @property {String} text 文字提示(默认“无内容”)
* @property {String} src 自定义图标路径,如定义,mode参数会失效
* @property {String Number} font-size 提示文字的大小,单位rpx(默认28)
* @property {String} mode 内置的图标,见官网说明(默认data)
* @property {String Number} img-width 图标的宽度,单位rpx(默认240)
* @property {String} img-height 图标的高度,单位rpx(默认auto)
* @property {String Number} margin-top 组件距离上一个元素之间的距离(默认0)
* @property {Boolean} show 是否显示组件(默认true)
* @event {Function} click 点击组件时触发
* @event {Function} close 点击关闭按钮时触发
* @example <u-empty text="所谓伊人,在水一方" mode="list"></u-empty>
*/
export default {
name: "u-empty",
props: {
// 图标路径
src: {
type: String,
default: ''
},
// 提示文字
text: {
type: String,
default: ''
},
// 文字颜色
color: {
type: String,
default: '#c0c4cc'
},
// 图标的颜色
iconColor: {
type: String,
default: '#c0c4cc'
},
// 图标的大小
iconSize: {
type: [String, Number],
default: 120
},
// 文字大小,单位rpx
fontSize: {
type: [String, Number],
default: 26
},
// 选择预置的图标类型
mode: {
type: String,
default: 'data'
},
// 图标宽度,单位rpx
imgWidth: {
type: [String, Number],
default: 120
},
// 图标高度,单位rpx
imgHeight: {
type: [String, Number],
default: 'auto'
},
// 是否显示组件
show: {
type: Boolean,
default: true
},
// 组件距离上一个元素之间的距离
marginTop: {
type: [String, Number],
default: 0
},
iconStyle: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
icons: {
car: '购物车为空',
page: '页面不存在',
search: '没有搜索结果',
address: '没有收货地址',
wifi: '没有WiFi',
order: '订单为空',
coupon: '没有优惠券',
favor: '暂无收藏',
permission: '无权限',
history: '无历史记录',
news: '无新闻列表',
message: '消息列表为空',
list: '列表为空',
data: '数据为空'
},
// icons: [{
// icon: 'car',
// text: '购物车为空'
// },{
// icon: 'page',
// text: '页面不存在'
// },{
// icon: 'search',
// text: '没有搜索结果'
// },{
// icon: 'address',
// text: '没有收货地址'
// },{
// icon: 'wifi',
// text: '没有WiFi'
// },{
// icon: 'order',
// text: '订单为空'
// },{
// icon: 'coupon',
// text: '没有优惠券'
// },{
// icon: 'favor',
// text: '暂无收藏'
// },{
// icon: 'permission',
// text: '无权限'
// },{
// icon: 'history',
// text: '无历史记录'
// },{
// icon: 'news',
// text: '无新闻列表'
// },{
// icon: 'message',
// text: '消息列表为空'
// },{
// icon: 'list',
// text: '列表为空'
// },{
// icon: 'data',
// text: '数据为空'
// }],
}
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-empty {
@include vue-flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
}
.u-image {
margin-bottom: 20rpx;
}
.u-slot-wrap {
@include vue-flex;
justify-content: center;
align-items: center;
margin-top: 20rpx;
}
</style>
-384
View File
@@ -1,384 +0,0 @@
<template>
<view class="u-field" :class="{'u-border-top': borderTop, 'u-border-bottom': borderBottom }">
<view class="u-field-inner" :class="[type == 'textarea' ? 'u-textarea-inner' : '', 'u-label-postion-' + labelPosition]">
<view class="u-label" :class="[required ? 'u-required' : '']" :style="{
justifyContent: justifyContent,
flex: labelPosition == 'left' ? `0 0 ${labelWidth}rpx` : '1'
}">
<view class="u-icon-wrap" v-if="icon">
<u-icon size="32" :custom-style="iconStyle" :name="icon" :color="iconColor" class="u-icon"></u-icon>
</view>
<slot name="icon"></slot>
<text class="u-label-text" :class="[this.$slots.icon || icon ? 'u-label-left-gap' : '']">{{ label }}</text>
</view>
<view class="fild-body">
<view class="u-flex-1 u-flex" :style="[inputWrapStyle]">
<textarea v-if="type == 'textarea'" class="u-flex-1 u-textarea-class" :style="[fieldStyle]" :value="value"
:placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" :maxlength="inputMaxlength"
:focus="focus" :autoHeight="autoHeight" :fixed="fixed" @input="onInput" @blur="onBlur" @focus="onFocus" @confirm="onConfirm"
@tap="fieldClick" />
<input
v-else
:style="[fieldStyle]"
:type="type"
class="u-flex-1 u-field__input-wrap"
:value="value"
:password="password || this.type === 'password'"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled"
:maxlength="inputMaxlength"
:focus="focus"
:confirmType="confirmType"
@focus="onFocus"
@blur="onBlur"
@input="onInput"
@confirm="onConfirm"
@tap="fieldClick"
/>
</view>
<u-icon :size="clearSize" v-if="clearable && value != '' && focused" name="close-circle-fill" color="#c0c4cc" class="u-clear-icon" @click="onClear"/>
<view class="u-button-wrap"><slot name="right" /></view>
<u-icon v-if="rightIcon" @click="rightIconClick" :name="rightIcon" color="#c0c4cc" :style="[rightIconStyle]" size="26" class="u-arror-right" />
</view>
</view>
<view v-if="errorMessage !== false && errorMessage != ''" class="u-error-message" :style="{
paddingLeft: labelWidth + 'rpx'
}">{{ errorMessage }}</view>
</view>
</template>
<script>
/**
* field 输入框
* @description 借助此组件,可以实现表单的输入, 有"text"和"textarea"类型的,此外,借助uView的picker和actionSheet组件可以快速实现上拉菜单,时间,地区选择等, 为表单解决方案的利器。
* @tutorial https://www.uviewui.com/components/field.html
* @property {String} type 输入框的类型(默认text)
* @property {String} icon label左边的图标,限uView的图标名称
* @property {Object} icon-style 左边图标的样式,对象形式
* @property {Boolean} right-icon 输入框右边的图标名称,限uView的图标名称(默认false)
* @property {Boolean} required 是否必填,左边您显示红色"*"号(默认false)
* @property {String} label 输入框左边的文字提示
* @property {Boolean} password 是否密码输入方式(用点替换文字),type为text时有效(默认false)
* @property {Boolean} clearable 是否显示右侧清空内容的图标控件(输入框有内容,且获得焦点时才显示),点击可清空输入框内容(默认true)
* @property {Number String} label-width label的宽度,单位rpx(默认130)
* @property {String} label-align label的文字对齐方式(默认left)
* @property {Object} field-style 自定义输入框的样式,对象形式
* @property {Number | String} clear-size 清除图标的大小,单位rpx(默认30)
* @property {String} input-align 输入框内容对齐方式(默认left)
* @property {Boolean} border-bottom 是否显示field的下边框(默认true)
* @property {Boolean} border-top 是否显示field的上边框(默认false)
* @property {String} icon-color 左边通过icon配置的图标的颜色(默认#606266)
* @property {Boolean} auto-height 是否自动增高输入区域,type为textarea时有效(默认true)
* @property {String Boolean} error-message 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息
* @property {String} placeholder 输入框的提示文字
* @property {String} placeholder-style placeholder的样式(内联样式,字符串),如"color: #ddd"
* @property {Boolean} focus 是否自动获得焦点(默认false)
* @property {Boolean} fixed 如果type为textarea,且在一个"position:fixed"的区域,需要指明为true(默认false)
* @property {Boolean} disabled 是否不可输入(默认false)
* @property {Number String} maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认140)
* @property {String} confirm-type 设置键盘右下角按钮的文字,仅在type="text"时生效(默认done)
* @event {Function} input 输入框内容发生变化时触发
* @event {Function} focus 输入框获得焦点时触发
* @event {Function} blur 输入框失去焦点时触发
* @event {Function} confirm 点击完成按钮时触发
* @event {Function} right-icon-click 通过right-icon生成的图标被点击时触发
* @event {Function} click 输入框被点击或者通过right-icon生成的图标被点击时触发,这样设计是考虑到传递右边的图标,一般都为需要弹出"picker"等操作时的场景,点击倒三角图标,理应发出此事件,见上方说明
* @example <u-field v-model="mobile" label="手机号" required :error-message="errorMessage"></u-field>
*/
export default {
name:"u-field",
props: {
icon: String,
rightIcon: String,
// arrowDirection: {
// type: String,
// default: 'right'
// },
required: Boolean,
label: String,
password: Boolean,
clearable: {
type: Boolean,
default: true
},
// 左边标题的宽度单位rpx
labelWidth: {
type: [Number, String],
default: 130
},
// 对齐方式,left|center|right
labelAlign: {
type: String,
default: 'left'
},
inputAlign: {
type: String,
default: 'left'
},
iconColor: {
type: String,
default: '#606266'
},
autoHeight: {
type: Boolean,
default: true
},
errorMessage: {
type: [String, Boolean],
default: ''
},
placeholder: String,
placeholderStyle: String,
focus: Boolean,
fixed: Boolean,
value: [Number, String],
type: {
type: String,
default: 'text'
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
confirmType: {
type: String,
default: 'done'
},
// lable的位置,可选为 left-左边,top-上边
labelPosition: {
type: String,
default: 'left'
},
// 输入框的自定义样式
fieldStyle: {
type: Object,
default() {
return {}
}
},
// 清除按钮的大小
clearSize: {
type: [Number, String],
default: 30
},
// lable左边的图标样式,对象形式
iconStyle: {
type: Object,
default() {
return {}
}
},
// 是否显示上边框
borderTop: {
type: Boolean,
default: false
},
// 是否显示下边框
borderBottom: {
type: Boolean,
default: true
},
// 是否自动去除两端的空格
trim: {
type: Boolean,
default: true
}
},
data() {
return {
focused: false,
itemIndex: 0,
};
},
computed: {
inputWrapStyle() {
let style = {};
style.textAlign = this.inputAlign;
// 判断lable的位置,如果是left的话,让input左边两边有间隙
if(this.labelPosition == 'left') {
style.margin = `0 8rpx`;
} else {
// 如果lable是top的,input的左边就没必要有间隙了
style.marginRight = `8rpx`;
}
return style;
},
rightIconStyle() {
let style = {};
if (this.arrowDirection == 'top') style.transform = 'roate(-90deg)';
if (this.arrowDirection == 'bottom') style.transform = 'roate(90deg)';
else style.transform = 'roate(0deg)';
return style;
},
labelStyle() {
let style = {};
if(this.labelAlign == 'left') style.justifyContent = 'flext-start';
if(this.labelAlign == 'center') style.justifyContent = 'center';
if(this.labelAlign == 'right') style.justifyContent = 'flext-end';
return style;
},
// uni不支持在computed中写style.justifyContent = 'center'的形式,故用此方法
justifyContent() {
if(this.labelAlign == 'left') return 'flex-start';
if(this.labelAlign == 'center') return 'center';
if(this.labelAlign == 'right') return 'flex-end';
},
// 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,给用户可以传入字符串数值
inputMaxlength() {
return Number(this.maxlength)
},
// label的位置
fieldInnerStyle() {
let style = {};
if(this.labelPosition == 'left') {
style.flexDirection = 'row';
} else {
style.flexDirection = 'column';
}
return style;
}
},
methods: {
onInput(event) {
let value = event.detail.value;
// 判断是否去除空格
if(this.trim) value = this.$u.trim(value);
this.$emit('input', value);
},
onFocus(event) {
this.focused = true;
this.$emit('focus', event);
},
onBlur(event) {
// 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错
// 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时
setTimeout(() => {
this.focused = false;
}, 100)
this.$emit('blur', event);
},
onConfirm(e) {
this.$emit('confirm', e.detail.value);
},
onClear(event) {
this.$emit('input', '');
},
rightIconClick() {
this.$emit('right-icon-click');
this.$emit('click');
},
fieldClick() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-field {
font-size: 28rpx;
padding: 20rpx 28rpx;
text-align: left;
position: relative;
color: $u-main-color;
}
.u-field-inner {
@include vue-flex;
align-items: center;
}
.u-textarea-inner {
align-items: flex-start;
}
.u-textarea-class {
min-height: 96rpx;
width: auto;
font-size: 28rpx;
}
.fild-body {
@include vue-flex;
flex: 1;
align-items: center;
}
.u-arror-right {
margin-left: 8rpx;
}
.u-label-text {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
}
.u-label-left-gap {
margin-left: 6rpx;
}
.u-label-postion-top {
flex-direction: column;
align-items: flex-start;
}
.u-label {
width: 130rpx;
flex: 1 1 130rpx;
text-align: left;
position: relative;
@include vue-flex;
align-items: center;
}
.u-required::before {
content: '*';
position: absolute;
left: -16rpx;
font-size: 14px;
color: $u-type-error;
height: 9px;
line-height: 1;
}
.u-field__input-wrap {
position: relative;
overflow: hidden;
font-size: 28rpx;
height: 48rpx;
flex: 1;
width: auto;
}
.u-clear-icon {
@include vue-flex;
align-items: center;
}
.u-error-message {
color: $u-type-error;
font-size: 26rpx;
text-align: left;
}
.placeholder-style {
color: rgb(150, 151, 153);
}
.u-input-class {
font-size: 28rpx;
}
.u-button-wrap {
margin-left: 8rpx;
}
</style>
@@ -1,431 +0,0 @@
<template>
<view class="u-form-item" :class="{'u-border-bottom': elBorderBottom, 'u-form-item__border-bottom--error': validateState === 'error' && showError('border-bottom')}">
<view class="u-form-item__body" :style="{
flexDirection: elLabelPosition == 'left' ? 'row' : 'column'
}">
<!-- 微信小程序中,将一个参数设置空字符串,结果会变成字符串"true" -->
<view class="u-form-item--left" :style="{
width: uLabelWidth,
flex: `0 0 ${uLabelWidth}`,
marginBottom: elLabelPosition == 'left' ? 0 : '10rpx',
}">
<!-- 为了块对齐 -->
<view class="u-form-item--left__content" v-if="required || leftIcon || label">
<!-- nvue不支持伪元素before -->
<text v-if="required" class="u-form-item--left__content--required">*</text>
<view class="u-form-item--left__content__icon" v-if="leftIcon">
<u-icon :name="leftIcon" :custom-style="leftIconStyle"></u-icon>
</view>
<view class="u-form-item--left__content__label" :style="[elLabelStyle, {
'justify-content': elLabelAlign == 'left' ? 'flex-start' : elLabelAlign == 'center' ? 'center' : 'flex-end'
}]">
{{label}}
</view>
</view>
</view>
<view class="u-form-item--right u-flex">
<view class="u-form-item--right__content">
<view class="u-form-item--right__content__slot ">
<slot />
</view>
<view class="u-form-item--right__content__icon u-flex" v-if="$slots.right || rightIcon">
<u-icon :custom-style="rightIconStyle" v-if="rightIcon" :name="rightIcon"></u-icon>
<slot name="right" />
</view>
</view>
</view>
</view>
<view class="u-form-item__message" v-if="validateState === 'error' && showError('message')" :style="{
paddingLeft: elLabelPosition == 'left' ? $u.addUnit(elLabelWidth) : '0',
}">{{validateMessage}}</view>
</view>
</template>
<script>
import Emitter from '../../libs/util/emitter.js';
import schema from '../../libs/util/async-validator';
// 去除警告信息
schema.warning = function() {};
/**
* form-item 表单item
* @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
* @tutorial http://uviewui.com/components/form.html
* @property {String} label 左侧提示文字
* @property {Object} prop 表单域model对象的属性名,在使用 validate、resetFields 方法的情况下,该属性是必填的
* @property {Boolean} border-bottom 是否显示表单域的下划线边框
* @property {String} label-position 表单域提示文字的位置,left-左侧,top-上方
* @property {String Number} label-width 提示文字的宽度,单位rpx(默认90)
* @property {Object} label-style lable的样式,对象形式
* @property {String} label-align lable的对齐方式
* @property {String} right-icon 右侧自定义字体图标(限uView内置图标)或图片地址
* @property {String} left-icon 左侧自定义字体图标(限uView内置图标)或图片地址
* @property {Object} left-icon-style 左侧图标的样式,对象形式
* @property {Object} right-icon-style 右侧图标的样式,对象形式
* @property {Boolean} required 是否显示左边的"*"号,这里仅起展示作用,如需校验必填,请通过rules配置必填规则(默认false)
* @example <u-form-item label="姓名"><u-input v-model="form.name" /></u-form-item>
*/
export default {
name: 'u-form-item',
mixins: [Emitter],
inject: {
uForm: {
default () {
return null
}
}
},
props: {
// input的label提示语
label: {
type: String,
default: ''
},
// 绑定的值
prop: {
type: String,
default: ''
},
// 是否显示表单域的下划线边框
borderBottom: {
type: [String, Boolean],
default: ''
},
// label的位置,left-左边,top-上边
labelPosition: {
type: String,
default: ''
},
// label的宽度,单位rpx
labelWidth: {
type: [String, Number],
default: ''
},
// lable的样式,对象形式
labelStyle: {
type: Object,
default () {
return {}
}
},
// lable字体的对齐方式
labelAlign: {
type: String,
default: ''
},
// 右侧图标
rightIcon: {
type: String,
default: ''
},
// 左侧图标
leftIcon: {
type: String,
default: ''
},
// 左侧图标的样式
leftIconStyle: {
type: Object,
default () {
return {}
}
},
// 左侧图标的样式
rightIconStyle: {
type: Object,
default () {
return {}
}
},
// 是否显示左边的必填星号,只作显示用,具体校验必填的逻辑,请在rules中配置
required: {
type: Boolean,
default: false
}
},
data() {
return {
initialValue: '', // 存储的默认值
// isRequired: false, // 是否必填,由于人性化考虑,必填"*"号通过props的required配置,不再通过rules的规则自动生成
validateState: '', // 是否校验成功
validateMessage: '', // 校验失败的提示语
// 有错误时的提示方式,message-提示信息,border-如果input设置了边框,变成呈红色,
errorType: ['message'],
fieldValue: '', // 获取当前子组件input的输入的值
// 父组件的参数,在computed计算中,无法得知this.parent发生变化,故将父组件的参数值,放到data中
parentData: {
borderBottom: true,
labelWidth: 90,
labelPosition: 'left',
labelStyle: {},
labelAlign: 'left',
}
};
},
watch: {
validateState(val) {
this.broadcastInputError();
},
// 监听u-form组件的errorType的变化
"uForm.errorType"(val) {
this.errorType = val;
this.broadcastInputError();
},
},
computed: {
// 计算后的label宽度,由于需要多个判断,故放到computed中
uLabelWidth() {
// 如果用户设置label为空字符串(微信小程序空字符串最终会变成字符串的'true'),意味着要将label的位置宽度设置为auto
return this.elLabelPosition == 'left' ? (this.label === 'true' || this.label === '' ? 'auto' : this.$u.addUnit(this
.elLabelWidth)) : '100%';
},
showError() {
return type => {
// 如果errorType数组中含有none,或者toast提示类型
if (this.errorType.indexOf('none') >= 0) return false;
else if (this.errorType.indexOf(type) >= 0) return true;
else return false;
}
},
// label的宽度
elLabelWidth() {
// label默认宽度为90,优先使用本组件的值,如果没有(如果设置为0,也算是配置了值,依然起效),则用u-form的值
return (this.labelWidth != 0 || this.labelWidth != '') ? this.labelWidth : (this.parentData.labelWidth ? this.parentData
.labelWidth :
90);
},
// label的样式
elLabelStyle() {
return Object.keys(this.labelStyle).length ? this.labelStyle : (this.parentData.labelStyle ? this.parentData.labelStyle :
{});
},
// label的位置,左侧或者上方
elLabelPosition() {
return this.labelPosition ? this.labelPosition : (this.parentData.labelPosition ? this.parentData.labelPosition :
'left');
},
// label的对齐方式
elLabelAlign() {
return this.labelAlign ? this.labelAlign : (this.parentData.labelAlign ? this.parentData.labelAlign : 'left');
},
// label的下划线
elBorderBottom() {
// 子组件的borderBottom默认为空字符串,如果不等于空字符串,意味着子组件设置了值,优先使用子组件的值
return this.borderBottom !== '' ? this.borderBottom : this.parentData.borderBottom ? this.parentData.borderBottom :
true;
}
},
methods: {
broadcastInputError() {
// 子组件发出事件,第三个参数为true或者false,true代表有错误
this.broadcast('u-input', 'on-form-item-error', this.validateState === 'error' && this.showError('border'));
},
// 判断是否需要required校验
setRules() {
let that = this;
// 由于人性化考虑,必填"*"号通过props的required配置,不再通过rules的规则自动生成
// 从父组件u-form拿到当前u-form-item需要验证 的规则
// let rules = this.getRules();
// if (rules.length) {
// this.isRequired = rules.some(rule => {
// // 如果有必填项,就返回,没有的话,就是undefined
// return rule.required;
// });
// }
// blur事件
this.$on('on-form-blur', that.onFieldBlur);
// change事件
this.$on('on-form-change', that.onFieldChange);
},
// 从u-form的rules属性中,取出当前u-form-item的校验规则
getRules() {
// 父组件的所有规则
let rules = this.parent.rules;
rules = rules ? rules[this.prop] : [];
// 保证返回的是一个数组形式
return [].concat(rules || []);
},
// blur事件时进行表单校验
onFieldBlur() {
this.validation('blur');
},
// change事件进行表单校验
onFieldChange() {
this.validation('change');
},
// 过滤出符合要求的rule规则
getFilteredRule(triggerType = '') {
let rules = this.getRules();
// 整体验证表单时,triggerType为空字符串,此时返回所有规则进行验证
if (!triggerType) return rules;
// 历遍判断规则是否有对应的事件,比如blur,change触发等的事件
// 使用indexOf判断,是因为某些时候设置的验证规则的trigger属性可能为多个,比如['blur','change']
// 某些场景可能的判断规则,可能不存在trigger属性,故先判断是否存在此属性
return rules.filter(res => res.trigger && res.trigger.indexOf(triggerType) !== -1);
},
// 校验数据
validation(trigger, callback = () => {}) {
// 检验之间,先获取需要校验的值
this.fieldValue = this.parent.model[this.prop];
// blur和change是否有当前方式的校验规则
let rules = this.getFilteredRule(trigger);
// 判断是否有验证规则,如果没有规则,也调用回调方法,否则父组件u-form会因为
// 对count变量的统计错误而无法进入上一层的回调
if (!rules || rules.length === 0) {
return callback('');
}
// 设置当前的装填,标识为校验中
this.validateState = 'validating';
// 调用async-validator的方法
let validator = new schema({
[this.prop]: rules
});
validator.validate({
[this.prop]: this.fieldValue
}, {
firstFields: true
}, (errors, fields) => {
// 记录状态和报错信息
this.validateState = !errors ? 'success' : 'error';
this.validateMessage = errors ? errors[0].message : '';
// 调用回调方法
callback(this.validateMessage);
});
},
// 清空当前的u-form-item
resetField() {
this.parent.model[this.prop] = this.initialValue;
// 设置为`success`状态,只是为了清空错误标记
this.validateState = 'success';
}
},
// 组件创建完成时,将当前实例保存到u-form中
mounted() {
// 支付宝、头条小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环应用
this.parent = this.$u.$parent.call(this, 'u-form');
if (this.parent) {
// 历遍parentData中的属性,将parent中的同名属性赋值给parentData
Object.keys(this.parentData).map(key => {
this.parentData[key] = this.parent[key];
});
// 如果没有传入prop,或者uForm为空(如果u-form-input单独使用,就不会有uForm注入),就不进行校验
if (this.prop) {
// 将本实例添加到父组件中
this.parent.fields.push(this);
this.errorType = this.parent.errorType;
// 设置初始值
this.initialValue = this.fieldValue;
// 添加表单校验,这里必须要写在$nextTick中,因为u-form的rules是通过ref手动传入的
// 不在$nextTick中的话,可能会造成执行此处代码时,父组件还没通过ref把规则给u-form,导致规则为空
this.$nextTick(() => {
this.setRules();
})
}
}
},
// 组件销毁前,将实例从u-form的缓存中移除
beforeDestroy() {
// 如果当前没有prop的话表示当前不要进行删除(因为没有注入)
if (this.parent && this.prop) {
this.parent.fields.map((item, index) => {
if (item === this) this.parent.fields.splice(index, 1);
})
}
},
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-form-item {
@include vue-flex;
// align-items: flex-start;
padding: 20rpx 0;
font-size: 28rpx;
color: $u-main-color;
box-sizing: border-box;
line-height: $u-form-item-height;
flex-direction: column;
&__border-bottom--error:after {
border-color: $u-type-error;
}
&__body {
@include vue-flex;
}
&--left {
@include vue-flex;
align-items: center;
&__content {
position: relative;
@include vue-flex;
align-items: center;
padding-right: 10rpx;
flex: 1;
&__icon {
margin-right: 8rpx;
}
&--required {
position: absolute;
left: -16rpx;
vertical-align: middle;
color: $u-type-error;
padding-top: 6rpx;
}
&__label {
@include vue-flex;
align-items: center;
flex: 1;
}
}
}
&--right {
flex: 1;
&__content {
@include vue-flex;
align-items: center;
flex: 1;
&__slot {
flex: 1;
/* #ifndef MP */
@include vue-flex;
align-items: center;
/* #endif */
}
&__icon {
margin-left: 10rpx;
color: $u-light-color;
font-size: 30rpx;
}
}
}
&__message {
font-size: 24rpx;
line-height: 24rpx;
color: $u-type-error;
margin-top: 12rpx;
}
}
</style>
-134
View File
@@ -1,134 +0,0 @@
<template>
<view class="u-form"><slot /></view>
</template>
<script>
/**
* form 表单
* @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
* @tutorial http://uviewui.com/components/form.html
* @property {Object} model 表单数据对象
* @property {Boolean} border-bottom 是否显示表单域的下划线边框
* @property {String} label-position 表单域提示文字的位置,left-左侧,top-上方
* @property {String Number} label-width 提示文字的宽度,单位rpx(默认90)
* @property {Object} label-style lable的样式,对象形式
* @property {String} label-align lable的对齐方式
* @property {Object} rules 通过ref设置,见官网说明
* @property {Array} error-type 错误的提示方式,数组形式,见上方说明(默认['message'])
* @example <u-form :model="form" ref="uForm"></u-form>
*/
export default {
name: 'u-form',
props: {
// 当前form的需要验证字段的集合
model: {
type: Object,
default() {
return {};
}
},
// 验证规则
// rules: {
// type: [Object, Function, Array],
// default() {
// return {};
// }
// },
// 有错误时的提示方式,message-提示信息,border-如果input设置了边框,变成呈红色,
// border-bottom-下边框呈现红色,none-无提示
errorType: {
type: Array,
default() {
return ['message', 'toast']
}
},
// 是否显示表单域的下划线边框
borderBottom: {
type: Boolean,
default: true
},
// label的位置,left-左边,top-上边
labelPosition: {
type: String,
default: 'left'
},
// label的宽度,单位rpx
labelWidth: {
type: [String, Number],
default: 90
},
// lable字体的对齐方式
labelAlign: {
type: String,
default: 'left'
},
// lable的样式,对象形式
labelStyle: {
type: Object,
default() {
return {}
}
},
},
provide() {
return {
uForm: this
};
},
data() {
return {
rules: {}
};
},
created() {
// 存储当前form下的所有u-form-item的实例
// 不能定义在data中,否则微信小程序会造成循环引用而报错
this.fields = [];
},
methods: {
setRules(rules) {
this.rules = rules;
},
// 清空所有u-form-item组件的内容,本质上是调用了u-form-item组件中的resetField()方法
resetFields() {
this.fields.map(field => {
field.resetField();
});
},
// 校验全部数据
validate(callback) {
return new Promise(resolve => {
// 对所有的u-form-item进行校验
let valid = true; // 默认通过
let count = 0; // 用于标记是否检查完毕
let errorArr = []; // 存放错误信息
this.fields.map(field => {
// 调用每一个u-form-item实例的validation的校验方法
field.validation('', error => {
// 如果任意一个u-form-item校验不通过,就意味着整个表单不通过
if (error) {
valid = false;
errorArr.push(error);
}
// 当历遍了所有的u-form-item时,调用promise的then方法
if (++count === this.fields.length) {
resolve(valid); // 进入promise的then方法
// 判断是否设置了toast的提示方式,只提示最前面的表单域的第一个错误信息
if(this.errorType.indexOf('none') === -1 && this.errorType.indexOf('toast') >= 0 && errorArr.length) {
this.$u.toast(errorArr[0]);
}
// 调用回调方法
if (typeof callback == 'function') callback(valid);
}
});
});
});
}
}
};
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
</style>
@@ -1,52 +0,0 @@
<template>
<u-modal v-model="show" :show-cancel-button="true" confirm-text="升级" title="发现新版本" @cancel="cancel" @confirm="confirm">
<view class="u-update-content">
<rich-text :nodes="content"></rich-text>
</view>
</u-modal>
</template>
<script>
export default {
data() {
return {
show: false,
content: `
1. 修复badge组件的size参数无效问题<br>
2. 新增Modal模态框组件<br>
3. 新增压窗屏组件,可以在APP上以弹窗的形式遮盖导航栏和底部tabbar<br>
4. 修复键盘组件在微信小程序上遮罩无效的问题
`,
}
},
onReady() {
this.show = true;
},
methods: {
cancel() {
this.closeModal();
},
confirm() {
this.closeModal();
},
closeModal() {
uni.navigateBack();
}
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-full-content {
background-color: #00C777;
}
.u-update-content {
font-size: 26rpx;
color: $u-content-color;
line-height: 1.7;
padding: 30rpx;
}
</style>
-54
View File
@@ -1,54 +0,0 @@
<template>
<view class="u-gap" :style="[gapStyle]"></view>
</template>
<script>
/**
* gap 间隔槽
* @description 该组件一般用于内容块之间的用一个灰色块隔开的场景,方便用户风格统一,减少工作量
* @tutorial https://www.uviewui.com/components/gap.html
* @property {String} bg-color 背景颜色(默认#f3f4f6)
* @property {String Number} height 分割槽高度,单位rpx(默认30)
* @property {String Number} margin-top 与前一个组件的距离,单位rpx(默认0)
* @property {String Number} margin-bottom 与后一个组件的距离,单位rpx(0)
* @example <u-gap height="80" bg-color="#bbb"></u-gap>
*/
export default {
name: "u-gap",
props: {
bgColor: {
type: String,
default: 'transparent ' // 背景透明
},
// 高度
height: {
type: [String, Number],
default: 30
},
// 与上一个组件的距离
marginTop: {
type: [String, Number],
default: 0
},
// 与下一个组件的距离
marginBottom: {
type: [String, Number],
default: 0
},
},
computed: {
gapStyle() {
return {
backgroundColor: this.bgColor,
height: this.height + 'rpx',
marginTop: this.marginTop + 'rpx',
marginBottom: this.marginBottom + 'rpx'
};
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
</style>
@@ -1,126 +0,0 @@
<template>
<view class="u-grid-item" :hover-class="parentData.hoverClass"
:hover-stay-time="200" @tap="click" :style="{
background: bgColor,
width: width,
}">
<view class="u-grid-item-box" :style="[customStyle]" :class="[parentData.border ? 'u-border-right u-border-bottom' : '']">
<slot />
</view>
</view>
</template>
<script>
/**
* gridItem 提示
* @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。搭配u-grid使用
* @tutorial https://www.uviewui.com/components/grid.html
* @property {String} bg-color 宫格的背景颜色(默认#ffffff)
* @property {String Number} index 点击宫格时,返回的值
* @property {Object} custom-style 自定义样式,对象形式
* @event {Function} click 点击宫格触发
* @example <u-grid-item></u-grid-item>
*/
export default {
name: "u-grid-item",
props: {
// 背景颜色
bgColor: {
type: String,
default: '#ffffff'
},
// 点击时返回的index
index: {
type: [Number, String],
default: ''
},
// 自定义样式,对象形式
customStyle: {
type: Object,
default() {
return {
padding: '30rpx 0'
}
}
}
},
data() {
return {
parentData: {
hoverClass: '', // 按下去的时候,是否显示背景灰色
col: 3, // 父组件划分的宫格数
border: true, // 是否显示边框,根据父组件决定
}
};
},
created() {
// 父组件的实例
this.updateParentData();
// this.parent在updateParentData()中定义
this.parent.children.push(this);
},
computed: {
// 每个grid-item的宽度
width() {
return 100 / Number(this.parentData.col) + '%';
},
},
methods: {
// 获取父组件的参数
updateParentData() {
// 此方法写在mixin中
this.getParentData('u-grid');
},
click() {
this.$emit('click', this.index);
this.parent && this.parent.click(this.index);
}
}
};
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-grid-item {
box-sizing: border-box;
background: #fff;
@include vue-flex;
align-items: center;
justify-content: center;
position: relative;
flex-direction: column;
/* #ifdef MP */
position: relative;
float: left;
/* #endif */
}
.u-grid-item-hover {
background: #f7f7f7 !important;
}
.u-grid-marker-box {
position: absolute;
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
line-height: 0;
}
.u-grid-marker-wrap {
position: absolute;
}
.u-grid-item-box {
padding: 30rpx 0;
@include vue-flex;
align-items: center;
justify-content: center;
flex-direction: column;
flex: 1;
width: 100%;
height: 100%;
}
</style>
-108
View File
@@ -1,108 +0,0 @@
<template>
<view class="u-grid" :class="{'u-border-top u-border-left': border}" :style="[gridStyle]"><slot /></view>
</template>
<script>
/**
* grid 宫格布局
* @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。
* @tutorial https://www.uviewui.com/components/grid.html
* @property {String Number} col 宫格的列数(默认3)
* @property {Boolean} border 是否显示宫格的边框(默认true)
* @property {Boolean} hover-class 点击宫格的时候,是否显示按下的灰色背景(默认false)
* @event {Function} click 点击宫格触发
* @example <u-grid :col="3" @click="click"></u-grid>
*/
export default {
name: 'u-grid',
props: {
// 分成几列
col: {
type: [Number, String],
default: 3
},
// 是否显示边框
border: {
type: Boolean,
default: true
},
// 宫格对齐方式,表现为数量少的时候,靠左,居中,还是靠右
align: {
type: String,
default: 'left'
},
// 宫格按压时的样式类,"none"为无效果
hoverClass: {
type: String,
default: 'u-hover-class'
}
},
data() {
return {
index: 0,
}
},
watch: {
// 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件
parentData() {
if(this.children.length) {
this.children.map(child => {
// 判断子组件(u-radio)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
typeof(child.updateParentData) == 'function' && child.updateParentData();
})
}
},
},
created() {
// 如果将children定义在data中,在微信小程序会造成循环引用而报错
this.children = [];
},
computed: {
// 计算父组件的值是否发生变化
parentData() {
return [this.hoverClass, this.col, this.size, this.border];
},
// 宫格对齐方式
gridStyle() {
let style = {};
switch(this.align) {
case 'left':
style.justifyContent = 'flex-start';
break;
case 'center':
style.justifyContent = 'center';
break;
case 'right':
style.justifyContent = 'flex-end';
break;
default: style.justifyContent = 'flex-start';
};
return style;
}
},
methods: {
click(index) {
this.$emit('click', index);
}
}
};
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-grid {
width: 100%;
/* #ifdef MP */
position: relative;
box-sizing: border-box;
overflow: hidden;
/* #endif */
/* #ifndef MP */
@include vue-flex;
flex-wrap: wrap;
align-items: center;
/* #endif */
}
</style>
-336
View File
@@ -1,336 +0,0 @@
<template>
<view :style="[customStyle]" class="u-icon" @tap="click" :class="['u-icon--' + labelPos]">
<image class="u-icon__img" v-if="isImg" :src="name" :mode="imgMode" :style="[imgStyle]"></image>
<text v-else class="u-icon__icon" :class="customClass" :style="[iconStyle]" :hover-class="hoverClass"
@touchstart="touchstart">
<text v-if="showDecimalIcon" :style="[decimalIconStyle]" :class="decimalIconClass" :hover-class="hoverClass"
class="u-icon__decimal">
</text>
</text>
<!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示 -->
<text v-if="label !== ''" class="u-icon__label" :style="{
color: labelColor,
fontSize: $u.addUnit(labelSize),
marginLeft: labelPos == 'right' ? $u.addUnit(marginLeft) : 0,
marginTop: labelPos == 'bottom' ? $u.addUnit(marginTop) : 0,
marginRight: labelPos == 'left' ? $u.addUnit(marginRight) : 0,
marginBottom: labelPos == 'top' ? $u.addUnit(marginBottom) : 0,
}">{{ label }}
</text>
</view>
</template>
<script>
/**
* icon 图标
* @description 基于字体的图标集,包含了大多数常见场景的图标。
* @tutorial https://www.uviewui.com/components/icon.html
* @property {String} name 图标名称,见示例图标集
* @property {String} color 图标颜色(默认inherit)
* @property {String | Number} size 图标字体大小,单位rpx(默认32)
* @property {String | Number} label-size label字体大小,单位rpx(默认28)
* @property {String} label 图标右侧的label文字(默认28)
* @property {String} label-pos label文字相对于图标的位置,只能right或bottom(默认right)
* @property {String} label-color label字体颜色(默认#606266)
* @property {Object} custom-style icon的样式,对象形式
* @property {String} custom-prefix 自定义字体图标库时,需要写上此值
* @property {String | Number} margin-left label在右侧时与图标的距离,单位rpx(默认6)
* @property {String | Number} margin-top label在下方时与图标的距离,单位rpx(默认6)
* @property {String | Number} margin-bottom label在上方时与图标的距离,单位rpx(默认6)
* @property {String | Number} margin-right label在左侧时与图标的距离,单位rpx(默认6)
* @property {String} label-pos label相对于图标的位置,只能right或bottom(默认right)
* @property {String} index 一个用于区分多个图标的值,点击图标时通过click事件传出
* @property {String} hover-class 图标按下去的样式类,用法同uni的view组件的hover-class参数,详情见官网
* @property {String} width 显示图片小图标时的宽度
* @property {String} height 显示图片小图标时的高度
* @property {String} top 图标在垂直方向上的定位
* @property {String} top 图标在垂直方向上的定位
* @property {String} top 图标在垂直方向上的定位
* @property {Boolean} show-decimal-icon 是否为DecimalIcon
* @property {String} inactive-color 背景颜色,可接受主题色,仅Decimal时有效
* @property {String | Number} percent 显示的百分比,仅Decimal时有效
* @event {Function} click 点击图标时触发
* @example <u-icon name="photo" color="#2979ff" size="28"></u-icon>
*/
export default {
name: 'u-icon',
props: {
// 图标类名
name: {
type: String,
default: ''
},
// 图标颜色,可接受主题色
color: {
type: String,
default: ''
},
// 字体大小,单位rpx
size: {
type: [Number, String],
default: 'inherit'
},
// 是否显示粗体
bold: {
type: Boolean,
default: false
},
// 点击图标的时候传递事件出去的index(用于区分点击了哪一个)
index: {
type: [Number, String],
default: ''
},
// 触摸图标时的类名
hoverClass: {
type: String,
default: ''
},
// 自定义扩展前缀,方便用户扩展自己的图标库
customPrefix: {
type: String,
default: 'uicon'
},
// 图标右边或者下面的文字
label: {
type: [String, Number],
default: ''
},
// label的位置,只能右边或者下边
labelPos: {
type: String,
default: 'right'
},
// label的大小
labelSize: {
type: [String, Number],
default: '28'
},
// label的颜色
labelColor: {
type: String,
default: '#606266'
},
// label与图标的距离(横向排列)
marginLeft: {
type: [String, Number],
default: '6'
},
// label与图标的距离(竖向排列)
marginTop: {
type: [String, Number],
default: '6'
},
// label与图标的距离(竖向排列)
marginRight: {
type: [String, Number],
default: '6'
},
// label与图标的距离(竖向排列)
marginBottom: {
type: [String, Number],
default: '6'
},
// 图片的mode
imgMode: {
type: String,
default: 'widthFix'
},
// 自定义样式
customStyle: {
type: Object,
default() {
return {}
}
},
// 用于显示图片小图标时,图片的宽度
width: {
type: [String, Number],
default: ''
},
// 用于显示图片小图标时,图片的高度
height: {
type: [String, Number],
default: ''
},
// 用于解决某些情况下,让图标垂直居中的用途
top: {
type: [String, Number],
default: 0
},
// 是否为DecimalIcon
showDecimalIcon: {
type: Boolean,
default: false
},
// 背景颜色,可接受主题色,仅Decimal时有效
inactiveColor: {
type: String,
default: '#ececec'
},
// 显示的百分比,仅Decimal时有效
percent: {
type: [Number, String],
default: '50'
}
},
computed: {
customClass() {
let classes = []
classes.push(this.customPrefix + '-' + this.name)
// uView的自定义图标类名为u-iconfont
if (this.customPrefix == 'uicon') {
classes.push('u-iconfont')
} else {
classes.push(this.customPrefix)
}
// 主题色,通过类配置
if (this.showDecimalIcon && this.inactiveColor && this.$u.config.type.includes(this.inactiveColor)) {
classes.push('u-icon__icon--' + this.inactiveColor)
} else if (this.color && this.$u.config.type.includes(this.color)) classes.push('u-icon__icon--' + this.color)
// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
classes = classes.join(' ')
//#endif
return classes
},
iconStyle() {
let style = {}
style = {
fontSize: this.size == 'inherit' ? 'inherit' : this.$u.addUnit(this.size),
fontWeight: this.bold ? 'bold' : 'normal',
// 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中
top: this.$u.addUnit(this.top)
}
// 非主题色值时,才当作颜色值
if (this.showDecimalIcon && this.inactiveColor && !this.$u.config.type.includes(this.inactiveColor)) {
style.color = this.inactiveColor
} else if (this.color && !this.$u.config.type.includes(this.color)) style.color = this.color
return style
},
// 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
isImg() {
return this.name.indexOf('/') !== -1
},
imgStyle() {
let style = {}
// 如果设置width和height属性,则优先使用,否则使用size属性
style.width = this.width ? this.$u.addUnit(this.width) : this.$u.addUnit(this.size)
style.height = this.height ? this.$u.addUnit(this.height) : this.$u.addUnit(this.size)
return style
},
decimalIconStyle() {
let style = {}
style = {
fontSize: this.size == 'inherit' ? 'inherit' : this.$u.addUnit(this.size),
fontWeight: this.bold ? 'bold' : 'normal',
// 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中
top: this.$u.addUnit(this.top),
width: this.percent + '%'
}
// 非主题色值时,才当作颜色值
if (this.color && !this.$u.config.type.includes(this.color)) style.color = this.color
return style
},
decimalIconClass() {
let classes = []
classes.push(this.customPrefix + '-' + this.name)
// uView的自定义图标类名为u-iconfont
if (this.customPrefix == 'uicon') {
classes.push('u-iconfont')
} else {
classes.push(this.customPrefix)
}
// 主题色,通过类配置
if (this.color && this.$u.config.type.includes(this.color)) classes.push('u-icon__icon--' + this.color)
else classes.push('u-icon__icon--primary')
// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
classes = classes.join(' ')
//#endif
return classes
}
},
mounted(){
},
methods: {
click() {
this.$emit('click', this.index)
},
touchstart() {
this.$emit('touchstart', this.index)
}
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
@import '../../iconfont.css';
.u-icon {
display: inline-flex;
align-items: center;
&--left {
flex-direction: row-reverse;
align-items: center;
}
&--right {
flex-direction: row;
align-items: center;
}
&--top {
flex-direction: column-reverse;
justify-content: center;
}
&--bottom {
flex-direction: column;
justify-content: center;
}
&__icon {
position: relative;
&--primary {
color: $u-type-primary;
}
&--success {
color: $u-type-success;
}
&--error {
color: $u-type-error;
}
&--warning {
color: $u-type-warning;
}
&--info {
color: $u-type-info;
}
}
&__decimal {
position: absolute;
top: 0;
left: 0;
display: inline-block;
overflow: hidden;
}
&__img {
height: auto;
will-change: transform;
}
&__label {
line-height: 1;
}
}
</style>
-267
View File
@@ -1,267 +0,0 @@
<template>
<view class="u-image" @tap="onClick" :style="[wrapStyle, backgroundStyle]">
<image
v-if="!isError"
:src="src"
:mode="mode"
@error="onErrorHandler"
@load="onLoadHandler"
:lazy-load="lazyLoad"
class="u-image__image"
:show-menu-by-longpress="showMenuByLongpress"
:style="{
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius)
}"
></image>
<view
v-if="showLoading && loading"
class="u-image__loading"
:style="{
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius),
backgroundColor: this.bgColor
}"
>
<slot v-if="$slots.loading" name="loading" />
<u-icon v-else :name="loadingIcon" :width="width" :height="height"></u-icon>
</view>
<view
v-if="showError && isError && !loading"
class="u-image__error"
:style="{
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius)
}"
>
<slot v-if="$slots.error" name="error" />
<u-icon v-else :name="errorIcon" :width="width" :height="height"></u-icon>
</view>
</view>
</template>
<script>
/**
* Image 图片
* @description 此组件为uni-app的image组件的加强版,在继承了原有功能外,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
* @tutorial https://uviewui.com/components/image.html
* @property {String} src 图片地址
* @property {String} mode 裁剪模式,见官网说明
* @property {String | Number} width 宽度,单位任意,如果为数值,则为rpx单位(默认100%)
* @property {String | Number} height 高度,单位任意,如果为数值,则为rpx单位(默认 auto)
* @property {String} shape 图片形状,circle-圆形,square-方形(默认square)
* @property {String | Number} border-radius 圆角值,单位任意,如果为数值,则为rpx单位(默认 0)
* @property {Boolean} lazy-load 是否懒加载,仅微信小程序、App、百度小程序、字节跳动小程序有效(默认 true)
* @property {Boolean} show-menu-by-longpress 是否开启长按图片显示识别小程序码菜单,仅微信小程序有效(默认 false)
* @property {String} loading-icon 加载中的图标,或者小图片(默认 photo)
* @property {String} error-icon 加载失败的图标,或者小图片(默认 error-circle)
* @property {Boolean} show-loading 是否显示加载中的图标或者自定义的slot(默认 true)
* @property {Boolean} show-error 是否显示加载错误的图标或者自定义的slot(默认 true)
* @property {Boolean} fade 是否需要淡入效果(默认 true)
* @property {String Number} width 传入图片路径时图片的宽度
* @property {String Number} height 传入图片路径时图片的高度
* @property {Boolean} webp 只支持网络资源,只对微信小程序有效(默认 false)
* @property {String | Number} duration 搭配fade参数的过渡时间,单位ms(默认 500)
* @event {Function} click 点击图片时触发
* @event {Function} error 图片加载失败时触发
* @event {Function} load 图片加载成功时触发
* @example <u-image width="100%" height="300rpx" :src="src"></u-image>
*/
export default {
name: 'u-image',
props: {
// 图片地址
src: {
type: String,
default: ''
},
// 裁剪模式
mode: {
type: String,
default: 'aspectFill'
},
// 宽度,单位任意
width: {
type: [String, Number],
default: '100%'
},
// 高度,单位任意
height: {
type: [String, Number],
default: 'auto'
},
// 图片形状,circle-圆形,square-方形
shape: {
type: String,
default: 'square'
},
// 圆角,单位任意
borderRadius: {
type: [String, Number],
default: 0
},
// 是否懒加载,微信小程序、App、百度小程序、字节跳动小程序
lazyLoad: {
type: Boolean,
default: true
},
// 开启长按图片显示识别微信小程序码菜单
showMenuByLongpress: {
type: Boolean,
default: true
},
// 加载中的图标,或者小图片
loadingIcon: {
type: String,
default: 'photo'
},
// 加载失败的图标,或者小图片
errorIcon: {
type: String,
default: 'error-circle'
},
// 是否显示加载中的图标或者自定义的slot
showLoading: {
type: Boolean,
default: true
},
// 是否显示加载错误的图标或者自定义的slot
showError: {
type: Boolean,
default: true
},
// 是否需要淡入效果
fade: {
type: Boolean,
default: true
},
// 只支持网络资源,只对微信小程序有效
webp: {
type: Boolean,
default: false
},
// 过渡时间,单位ms
duration: {
type: [String, Number],
default: 500
},
// 背景颜色,用于深色页面加载图片时,为了和背景色融合
bgColor: {
type: String,
default: '#f3f4f6'
}
},
data() {
return {
// 图片是否加载错误,如果是,则显示错误占位图
isError: false,
// 初始化组件时,默认为加载中状态
loading: true,
// 不透明度,为了实现淡入淡出的效果
opacity: 1,
// 过渡时间,因为props的值无法修改,故需要一个中间值
durationTime: this.duration,
// 图片加载完成时,去掉背景颜色,因为如果是png图片,就会显示灰色的背景
backgroundStyle: {}
};
},
watch: {
src: {
immediate: true,
handler (n) {
if(!n) {
// 如果传入null或者'',或者false,或者undefined,标记为错误状态
this.isError = true;
this.loading = false;
} else {
this.isError = false;
}
}
}
},
computed: {
wrapStyle() {
let style = {};
// 通过调用addUnit()方法,如果有单位,如百分比,px单位等,直接返回,如果是纯粹的数值,则加上rpx单位
style.width = this.$u.addUnit(this.width);
style.height = this.$u.addUnit(this.height);
// 如果是配置了圆形,设置50%的圆角,否则按照默认的配置值
style.borderRadius = this.shape == 'circle' ? '50%' : this.$u.addUnit(this.borderRadius);
// 如果设置圆角,必须要有hidden,否则可能圆角无效
style.overflow = this.borderRadius > 0 ? 'hidden' : 'visible';
if (this.fade) {
style.opacity = this.opacity;
style.transition = `opacity ${Number(this.durationTime) / 1000}s ease-in-out`;
}
return style;
}
},
methods: {
// 点击图片
onClick() {
this.$emit('click');
},
// 图片加载失败
onErrorHandler(err) {
this.loading = false;
this.isError = true;
this.$emit('error', err);
},
// 图片加载完成,标记loading结束
onLoadHandler() {
this.loading = false;
this.isError = false;
this.$emit('load');
// 如果不需要动画效果,就不执行下方代码,同时移除加载时的背景颜色
// 否则无需fade效果时,png图片依然能看到下方的背景色
if (!this.fade) return this.removeBgColor();
// 原来opacity为1(不透明,是为了显示占位图),改成0(透明,意味着该元素显示的是背景颜色,默认的灰色),再改成1,是为了获得过渡效果
this.opacity = 0;
// 这里设置为0,是为了图片展示到背景全透明这个过程时间为0,延时之后延时之后重新设置为duration,是为了获得背景透明(灰色)
// 到图片展示的过程中的淡入效果
this.durationTime = 0;
// 延时50ms,否则在浏览器H5,过渡效果无效
setTimeout(() => {
this.durationTime = this.duration;
this.opacity = 1;
setTimeout(() => {
this.removeBgColor();
}, this.durationTime);
}, 50);
},
// 移除图片的背景色
removeBgColor() {
// 淡入动画过渡完成后,将背景设置为透明色,否则png图片会看到灰色的背景
this.backgroundStyle = {
backgroundColor: 'transparent'
};
}
}
};
</script>
<style scoped lang="scss">
@import '../../libs/css/style.components.scss';
.u-image {
position: relative;
transition: opacity 0.5s ease-in-out;
&__image {
width: 100%;
height: 100%;
}
&__loading,
&__error {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
@include vue-flex;
align-items: center;
justify-content: center;
background-color: $u-bg-color;
color: $u-tips-color;
font-size: 46rpx;
}
}
</style>
@@ -1,89 +0,0 @@
<template>
<!-- 支付宝小程序使用$u.getRect()获取组件的根元素尺寸,所以在外面套一个"壳" -->
<view>
<view class="u-index-anchor-wrapper" :id="$u.guid()" :style="[wrapperStyle]">
<view class="u-index-anchor " :class="[active ? 'u-index-anchor--active' : '']" :style="[customAnchorStyle]">
<slot v-if="useSlot" />
<block v-else>
<text>{{ index }}</text>
</block>
</view>
</view>
</view>
</template>
<script>
/**
* indexAnchor 索引列表锚点
* @description 通过折叠面板收纳内容区域,搭配<u-index-anchor>使用
* @tutorial https://www.uviewui.com/components/indexList.html#indexanchor-props
* @property {Boolean} use-slot 是否使用自定义内容的插槽(默认false)
* @property {String Number} index 索引字符,如果定义了use-slot,此参数自动失效
* @property {Object} custStyle 自定义样式,对象形式,如"{color: 'red'}"
* @event {Function} default 锚点位置显示内容,默认为索引字符
* @example <u-index-anchor :index="item" />
*/
export default {
name: "u-index-anchor",
props: {
useSlot: {
type: Boolean,
default: false
},
index: {
type: String,
default: ''
},
customStyle: {
type: Object,
default () {
return {}
}
}
},
data() {
return {
active: false,
wrapperStyle: {},
anchorStyle: {}
}
},
created() {
this.parent = false;
},
mounted() {
this.parent = this.$u.$parent.call(this, 'u-index-list');
if(this.parent) {
this.parent.children.push(this);
this.parent.updateData();
}
},
computed: {
customAnchorStyle() {
return Object.assign(this.anchorStyle, this.customStyle);
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-index-anchor {
box-sizing: border-box;
padding: 14rpx 24rpx;
color: #606266;
width: 100%;
font-weight: 500;
font-size: 28rpx;
line-height: 1.2;
background-color: rgb(245, 245, 245);
}
.u-index-anchor--active {
right: 0;
left: 0;
color: #2979ff;
background-color: #fff;
}
</style>
@@ -1,315 +0,0 @@
<template>
<!-- 支付宝小程序使用$u.getRect()获取组件的根元素尺寸,所以在外面套一个"壳" -->
<view>
<view class="u-index-bar">
<slot />
<view v-if="showSidebar" class="u-index-bar__sidebar" @touchstart.stop.prevent="onTouchMove" @touchmove.stop.prevent="onTouchMove"
@touchend.stop.prevent="onTouchStop" @touchcancel.stop.prevent="onTouchStop">
<view v-for="(item, index) in indexList" :key="index" class="u-index-bar__index" :style="{zIndex: zIndex + 1, color: activeAnchorIndex === index ? activeColor : ''}"
:data-index="index">
{{ item }}
</view>
</view>
<view class="u-indexed-list-alert" v-if="touchmove && indexList[touchmoveIndex]" :style="{
zIndex: alertZIndex
}">
<text>{{indexList[touchmoveIndex]}}</text>
</view>
</view>
</view>
</template>
<script>
var indexList = function() {
var indexList = [];
var charCodeOfA = 'A'.charCodeAt(0);
for (var i = 0; i < 26; i++) {
indexList.push(String.fromCharCode(charCodeOfA + i));
}
return indexList;
};
/**
* indexList 索引列表
* @description 通过折叠面板收纳内容区域,搭配<u-index-anchor>使用
* @tutorial https://www.uviewui.com/components/indexList.html#indexanchor-props
* @property {Number String} scroll-top 当前滚动高度,自定义组件无法获得滚动条事件,所以依赖接入方传入
* @property {Array} index-list 索引字符列表,数组(默认A-Z)
* @property {Number String} z-index 锚点吸顶时的层级(默认965)
* @property {Boolean} sticky 是否开启锚点自动吸顶(默认true)
* @property {Number String} offset-top 锚点自动吸顶时与顶部的距离(默认0)
* @property {String} highlight-color 锚点和右边索引字符高亮颜色(默认#2979ff)
* @event {Function} select 选中右边索引字符时触发
* @example <u-index-list :scrollTop="scrollTop"></u-index-list>
*/
export default {
name: "u-index-list",
props: {
sticky: {
type: Boolean,
default: true
},
zIndex: {
type: [Number, String],
default: ''
},
scrollTop: {
type: [Number, String],
default: 0,
},
offsetTop: {
type: [Number, String],
default: 0
},
indexList: {
type: Array,
default () {
return indexList()
}
},
activeColor: {
type: String,
default: '#2979ff'
}
},
created() {
// #ifdef H5
this.stickyOffsetTop = this.offsetTop ? uni.upx2px(this.offsetTop) : 44;
// #endif
// #ifndef H5
this.stickyOffsetTop = this.offsetTop ? uni.upx2px(this.offsetTop) : 0;
// #endif
// 只能在created生命周期定义children,如果在data定义,会因为循环引用而报错
this.children = [];
},
data() {
return {
activeAnchorIndex: 0,
showSidebar: true,
// children: [],
touchmove: false,
touchmoveIndex: 0,
}
},
watch: {
scrollTop() {
this.updateData()
}
},
computed: {
// 弹出toast的z-index值
alertZIndex() {
return this.$u.zIndex.toast;
}
},
methods: {
updateData() {
this.timer && clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.showSidebar = !!this.children.length;
this.setRect().then(() => {
this.onScroll();
});
}, 0);
},
setRect() {
return Promise.all([
this.setAnchorsRect(),
this.setListRect(),
this.setSiderbarRect()
]);
},
setAnchorsRect() {
return Promise.all(this.children.map((anchor, index) => anchor
.$uGetRect('.u-index-anchor-wrapper')
.then((rect) => {
Object.assign(anchor, {
height: rect.height,
top: rect.top
});
})));
},
setListRect() {
return this.$uGetRect('.u-index-bar').then((rect) => {
Object.assign(this, {
height: rect.height,
top: rect.top + this.scrollTop
});
});
},
setSiderbarRect() {
return this.$uGetRect('.u-index-bar__sidebar').then(rect => {
this.sidebar = {
height: rect.height,
top: rect.top
};
});
},
getActiveAnchorIndex() {
const {
children
} = this;
const {
sticky
} = this;
for (let i = this.children.length - 1; i >= 0; i--) {
const preAnchorHeight = i > 0 ? children[i - 1].height : 0;
const reachTop = sticky ? preAnchorHeight : 0;
if (reachTop >= children[i].top) {
return i;
}
}
return -1;
},
onScroll() {
const {
children = []
} = this;
if (!children.length) {
return;
}
const {
sticky,
stickyOffsetTop,
zIndex,
scrollTop,
activeColor
} = this;
const active = this.getActiveAnchorIndex();
this.activeAnchorIndex = active;
if (sticky) {
let isActiveAnchorSticky = false;
if (active !== -1) {
isActiveAnchorSticky =
children[active].top <= 0;
}
children.forEach((item, index) => {
if (index === active) {
let wrapperStyle = '';
let anchorStyle = {
color: `${activeColor}`
};
if (isActiveAnchorSticky) {
wrapperStyle = {
height: `${children[index].height}px`
};
anchorStyle = {
position: 'fixed',
top: `${stickyOffsetTop}px`,
zIndex: `${zIndex ? zIndex : this.$u.zIndex.indexListSticky}`,
color: `${activeColor}`
};
}
item.active = active;
item.wrapperStyle = wrapperStyle;
item.anchorStyle = anchorStyle;
} else if (index === active - 1) {
const currentAnchor = children[index];
const currentOffsetTop = currentAnchor.top;
const targetOffsetTop = index === children.length - 1 ?
this.top :
children[index + 1].top;
const parentOffsetHeight = targetOffsetTop - currentOffsetTop;
const translateY = parentOffsetHeight - currentAnchor.height;
const anchorStyle = {
position: 'relative',
transform: `translate3d(0, ${translateY}px, 0)`,
zIndex: `${zIndex ? zIndex : this.$u.zIndex.indexListSticky}`,
color: `${activeColor}`
};
item.active = active;
item.anchorStyle = anchorStyle;
} else {
item.active = false;
item.anchorStyle = '';
item.wrapperStyle = '';
}
});
}
},
onTouchMove(event) {
this.touchmove = true;
const sidebarLength = this.children.length;
const touch = event.touches[0];
const itemHeight = this.sidebar.height / sidebarLength;
let clientY = 0;
clientY = touch.clientY;
let index = Math.floor((clientY - this.sidebar.top) / itemHeight);
if (index < 0) {
index = 0;
} else if (index > sidebarLength - 1) {
index = sidebarLength - 1;
}
this.touchmoveIndex = index;
this.scrollToAnchor(index);
},
onTouchStop() {
this.touchmove = false;
this.scrollToAnchorIndex = null;
},
scrollToAnchor(index) {
if (this.scrollToAnchorIndex === index) {
return;
}
this.scrollToAnchorIndex = index;
const anchor = this.children.find((item) => item.index === this.indexList[index]);
if (anchor) {
this.$emit('select', anchor.index);
uni.pageScrollTo({
duration: 0,
scrollTop: anchor.top + this.scrollTop
});
}
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-index-bar {
position: relative
}
.u-index-bar__sidebar {
position: fixed;
top: 50%;
right: 0;
@include vue-flex;
flex-direction: column;
text-align: center;
transform: translateY(-50%);
user-select: none;
z-index: 99;
}
.u-index-bar__index {
font-weight: 500;
padding: 8rpx 18rpx;
font-size: 22rpx;
line-height: 1
}
.u-indexed-list-alert {
position: fixed;
width: 120rpx;
height: 120rpx;
right: 90rpx;
top: 50%;
margin-top: -60rpx;
border-radius: 24rpx;
font-size: 50rpx;
color: #fff;
background-color: rgba(0, 0, 0, 0.65);
@include vue-flex;
justify-content: center;
align-items: center;
padding: 0;
z-index: 9999999;
}
.u-indexed-list-alert text {
line-height: 50rpx;
}
</style>
-387
View File
@@ -1,387 +0,0 @@
<template>
<view
class="u-input"
:class="{
'u-input--border': border,
'u-input--error': validateState
}"
:style="{
padding: `0 ${border ? 20 : 0}rpx`,
borderColor: borderColor,
textAlign: inputAlign
}"
@tap.stop="inputClick"
>
<textarea
v-if="type == 'textarea'"
class="u-input__input u-input__textarea"
:style="[getStyle]"
:value="defaultValue"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled"
:maxlength="inputMaxlength"
:fixed="fixed"
:focus="focus"
:autoHeight="autoHeight"
:selection-end="uSelectionEnd"
:selection-start="uSelectionStart"
:cursor-spacing="getCursorSpacing"
:show-confirm-bar="showConfirmbar"
@input="handleInput"
@blur="handleBlur"
@focus="onFocus"
@confirm="onConfirm"
/>
<input
v-else
class="u-input__input"
:type="type == 'password' ? 'text' : type"
:style="[getStyle]"
:value="defaultValue"
:password="type == 'password' && !showPassword"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled || type === 'select'"
:maxlength="inputMaxlength"
:focus="focus"
:confirmType="confirmType"
:cursor-spacing="getCursorSpacing"
:selection-end="uSelectionEnd"
:selection-start="uSelectionStart"
:show-confirm-bar="showConfirmbar"
@focus="onFocus"
@blur="handleBlur"
@input="handleInput"
@confirm="onConfirm"
/>
<view class="u-input__right-icon u-flex">
<view class="u-input__right-icon__clear u-input__right-icon__item" @tap="onClear" v-if="clearable && value != '' && focused">
<u-icon size="32" name="close-circle-fill" color="#c0c4cc"/>
</view>
<view class="u-input__right-icon__clear u-input__right-icon__item" v-if="passwordIcon && type == 'password'">
<u-icon size="32" :name="!showPassword ? 'eye' : 'eye-fill'" color="#c0c4cc" @click="showPassword = !showPassword"/>
</view>
<view class="u-input__right-icon--select u-input__right-icon__item" v-if="type == 'select'" :class="{
'u-input__right-icon--select--reverse': selectOpen
}">
<u-icon name="arrow-down-fill" size="26" color="#c0c4cc"></u-icon>
</view>
</view>
</view>
</template>
<script>
import Emitter from '../../libs/util/emitter.js';
/**
* input 输入框
* @description 此组件为一个输入框,默认没有边框和样式,是专门为配合表单组件u-form而设计的,利用它可以快速实现表单验证,输入内容,下拉选择等功能。
* @tutorial http://uviewui.com/components/input.html
* @property {String} type 模式选择,见官网说明
* @property {Boolean} clearable 是否显示右侧的清除图标(默认true)
* @property {} v-model 用于双向绑定输入框的值
* @property {String} input-align 输入框文字的对齐方式(默认left)
* @property {String} placeholder placeholder显示值(默认 '请输入内容')
* @property {Boolean} disabled 是否禁用输入框(默认false)
* @property {String Number} maxlength 输入框的最大可输入长度(默认140)
* @property {String Number} selection-start 光标起始位置,自动聚焦时有效,需与selection-end搭配使用(默认-1)
* @property {String Number} maxlength 光标结束位置,自动聚焦时有效,需与selection-start搭配使用(默认-1)
* @property {String Number} cursor-spacing 指定光标与键盘的距离,单位px(默认0)
* @property {String} placeholderStyle placeholder的样式,字符串形式,如"color: red;"(默认 "color: #c0c4cc;")
* @property {String} confirm-type 设置键盘右下角按钮的文字,仅在type为text时生效(默认done)
* @property {Object} custom-style 自定义输入框的样式,对象形式
* @property {Boolean} focus 是否自动获得焦点(默认false)
* @property {Boolean} fixed 如果type为textarea,且在一个"position:fixed"的区域,需要指明为true(默认false)
* @property {Boolean} password-icon type为password时,是否显示右侧的密码查看图标(默认true)
* @property {Boolean} border 是否显示边框(默认false)
* @property {String} border-color 输入框的边框颜色(默认#dcdfe6)
* @property {Boolean} auto-height 是否自动增高输入区域,type为textarea时有效(默认true)
* @property {String Number} height 高度,单位rpx(text类型时为70,textarea时为100)
* @example <u-input v-model="value" :type="type" :border="border" />
*/
export default {
name: 'u-input',
mixins: [Emitter],
props: {
value: {
type: [String, Number],
default: ''
},
// 输入框的类型,textarea,text,number
type: {
type: String,
default: 'text'
},
inputAlign: {
type: String,
default: 'left'
},
placeholder: {
type: String,
default: '请输入内容'
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
placeholderStyle: {
type: String,
default: 'color: #c0c4cc;'
},
confirmType: {
type: String,
default: 'done'
},
// 输入框的自定义样式
customStyle: {
type: Object,
default() {
return {};
}
},
// 如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true
fixed: {
type: Boolean,
default: false
},
// 是否自动获得焦点
focus: {
type: Boolean,
default: false
},
// 密码类型时,是否显示右侧的密码图标
passwordIcon: {
type: Boolean,
default: true
},
// input|textarea是否显示边框
border: {
type: Boolean,
default: false
},
// 输入框的边框颜色
borderColor: {
type: String,
default: '#dcdfe6'
},
autoHeight: {
type: Boolean,
default: true
},
// type=select时,旋转右侧的图标,标识当前处于打开还是关闭select的状态
// open-打开,close-关闭
selectOpen: {
type: Boolean,
default: false
},
// 高度,单位rpx
height: {
type: [Number, String],
default: ''
},
// 是否可清空
clearable: {
type: Boolean,
default: true
},
// 指定光标与键盘的距离,单位 px
cursorSpacing: {
type: [Number, String],
default: 0
},
// 光标起始位置,自动聚焦时有效,需与selection-end搭配使用
selectionStart: {
type: [Number, String],
default: -1
},
// 光标结束位置,自动聚焦时有效,需与selection-start搭配使用
selectionEnd: {
type: [Number, String],
default: -1
},
// 是否自动去除两端的空格
trim: {
type: Boolean,
default: true
},
// 是否显示键盘上方带有”完成“按钮那一栏
showConfirmbar:{
type:Boolean,
default:true
}
},
data() {
return {
defaultValue: this.value,
inputHeight: 70, // input的高度
textareaHeight: 100, // textarea的高度
validateState: false, // 当前input的验证状态,用于错误时,边框是否改为红色
focused: false, // 当前是否处于获得焦点的状态
showPassword: false, // 是否预览密码
lastValue: '', // 用于头条小程序,判断@input中,前后的值是否发生了变化,因为头条中文下,按下键没有输入内容,也会触发@input时间
};
},
watch: {
value(nVal, oVal) {
this.defaultValue = nVal;
// 当值发生变化,且为select类型时(此时input被设置为disabled,不会触发@input事件),模拟触发@input事件
if(nVal != oVal && this.type == 'select') this.handleInput({
detail: {
value: nVal
}
})
},
},
computed: {
// 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,给用户可以传入字符串数值
inputMaxlength() {
return Number(this.maxlength);
},
getStyle() {
let style = {};
// 如果没有自定义高度,就根据type为input还是textare来分配一个默认的高度
style.minHeight = this.height ? this.height + 'rpx' : this.type == 'textarea' ?
this.textareaHeight + 'rpx' : this.inputHeight + 'rpx';
style = Object.assign(style, this.customStyle);
return style;
},
//
getCursorSpacing() {
return Number(this.cursorSpacing);
},
// 光标起始位置
uSelectionStart() {
return String(this.selectionStart);
},
// 光标结束位置
uSelectionEnd() {
return String(this.selectionEnd);
}
},
created() {
// 监听u-form-item发出的错误事件,将输入框边框变红色
this.$on('on-form-item-error', this.onFormItemError);
},
methods: {
/**
* change 事件
* @param event
*/
handleInput(event) {
let value = event.detail.value;
// 判断是否去除空格
if(this.trim) value = this.$u.trim(value);
// vue 原生的方法 return 出去
this.$emit('input', value);
// 当前model 赋值
this.defaultValue = value;
// 过一个生命周期再发送事件给u-form-item,否则this.$emit('input')更新了父组件的值,但是微信小程序上
// 尚未更新到u-form-item,导致获取的值为空,从而校验混论
// 这里不能延时时间太短,或者使用this.$nextTick,否则在头条上,会造成混乱
setTimeout(() => {
// 头条小程序由于自身bug,导致中文下,每按下一个键(尚未完成输入),都会触发一次@input,导致错误,这里进行判断处理
// #ifdef MP-TOUTIAO
if(this.$u.trim(value) == this.lastValue) return ;
this.lastValue = value;
// #endif
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-change', value);
}, 40)
},
/**
* blur 事件
* @param event
*/
handleBlur(event) {
// 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错
// 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时
setTimeout(() => {
this.focused = false;
}, 100)
// vue 原生的方法 return 出去
this.$emit('blur', event.detail.value);
setTimeout(() => {
// 头条小程序由于自身bug,导致中文下,每按下一个键(尚未完成输入),都会触发一次@input,导致错误,这里进行判断处理
// #ifdef MP-TOUTIAO
if(this.$u.trim(value) == this.lastValue) return ;
this.lastValue = value;
// #endif
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-blur', event.detail.value);
}, 40)
},
onFormItemError(status) {
this.validateState = status;
},
onFocus(event) {
this.focused = true;
this.$emit('focus');
},
onConfirm(e) {
this.$emit('confirm', e.detail.value);
},
onClear(event) {
this.$emit('input', '');
},
inputClick() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-input {
position: relative;
flex: 1;
@include vue-flex;
&__input {
//height: $u-form-item-height;
font-size: 28rpx;
color: $u-main-color;
flex: 1;
}
&__textarea {
width: auto;
font-size: 28rpx;
color: $u-main-color;
padding: 10rpx 0;
line-height: normal;
flex: 1;
}
&--border {
border-radius: 6rpx;
border-radius: 4px;
border: 1px solid $u-form-item-border-color;
}
&--error {
border-color: $u-type-error!important;
}
&__right-icon {
&__item {
margin-left: 10rpx;
}
&--select {
transition: transform .4s;
&--reverse {
transform: rotate(-180deg);
}
}
}
}
</style>
@@ -1,217 +0,0 @@
<template>
<u-popup class="" :mask="mask" :maskCloseAble="maskCloseAble" mode="bottom" :popup="false" v-model="value" length="auto"
:safeAreaInsetBottom="safeAreaInsetBottom" @close="popupClose" :zIndex="uZIndex">
<slot />
<view class="u-tooltip" v-if="tooltip">
<view class="u-tooltip-item u-tooltip-cancel" hover-class="u-tooltip-cancel-hover" @tap="onCancel">
{{cancelBtn ? cancelText : ''}}
</view>
<view v-if="showTips" class="u-tooltip-item u-tooltip-tips">
{{tips ? tips : mode == 'number' ? '数字键盘' : mode == 'card' ? '身份证键盘' : '车牌号键盘'}}
</view>
<view v-if="confirmBtn" @tap="onConfirm" class="u-tooltip-item u-tooltips-submit" hover-class="u-tooltips-submit-hover">
{{confirmBtn ? confirmText : ''}}
</view>
</view>
<block v-if="mode == 'number' || mode == 'card'">
<u-number-keyboard :random="random" @backspace="backspace" @change="change" :mode="mode" :dotEnabled="dotEnabled"></u-number-keyboard>
</block>
<block v-else>
<u-car-keyboard :random="random" @backspace="backspace" @change="change"></u-car-keyboard>
</block>
</u-popup>
</template>
<script>
/**
* keyboard 键盘
* @description 此为uViw自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3中模式,都有可以打乱按键顺序的选项。
* @tutorial https://www.uviewui.com/components/keyboard.html
* @property {String} mode 键盘类型,见官网基本使用的说明(默认number)
* @property {Boolean} dot-enabled 是否显示"."按键,只在mode=number时有效(默认true)
* @property {Boolean} tooltip 是否显示键盘顶部工具条(默认true)
* @property {String} tips 工具条中间的提示文字,见上方基本使用的说明,如不需要,请传""空字符
* @property {Boolean} cancel-btn 是否显示工具条左边的"取消"按钮(默认true)
* @property {Boolean} confirm-btn 是否显示工具条右边的"完成"按钮(默认true)
* @property {Boolean} mask 是否显示遮罩(默认true)
* @property {String} confirm-text 确认按钮的文字
* @property {String} cancel-text 取消按钮的文字
* @property {Number String} z-index 弹出键盘的z-index值(默认1075)
* @property {Boolean} random 是否打乱键盘按键的顺序(默认false)
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
* @property {Boolean} mask-close-able 是否允许点击遮罩收起键盘(默认true)
* @event {Function} change 按键被点击(不包含退格键被点击)
* @event {Function} cancel 键盘顶部工具条左边的"取消"按钮被点击
* @event {Function} confirm 键盘顶部工具条右边的"完成"按钮被点击
* @event {Function} backspace 键盘退格键被点击
* @example <u-keyboard mode="number" v-model="show"></u-keyboard>
*/
export default {
name: "u-keyboard",
props: {
// 键盘的类型,number-数字键盘,card-身份证键盘,car-车牌号键盘
mode: {
type: String,
default: 'number'
},
// 是否显示键盘的"."符号
dotEnabled: {
type: Boolean,
default: true
},
// 是否显示顶部工具条
tooltip: {
type: Boolean,
default: true
},
// 是否显示工具条中间的提示
showTips: {
type: Boolean,
default: true
},
// 工具条中间的提示文字
tips: {
type: String,
default: ''
},
// 是否显示工具条左边的"取消"按钮
cancelBtn: {
type: Boolean,
default: true
},
// 是否显示工具条右边的"完成"按钮
confirmBtn: {
type: Boolean,
default: true
},
// 是否打乱键盘按键的顺序
random: {
type: Boolean,
default: false
},
// 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距
safeAreaInsetBottom: {
type: Boolean,
default: false
},
// 是否允许通过点击遮罩关闭键盘
maskCloseAble: {
type: Boolean,
default: true
},
// 通过双向绑定控制键盘的弹出与收起
value: {
type: Boolean,
default: false
},
// 是否显示遮罩,某些时候数字键盘时,用户希望看到自己的数值,所以可能不想要遮罩
mask: {
type: Boolean,
default: true
},
// z-index值
zIndex: {
type: [Number, String],
default: ''
},
// 取消按钮的文字
cancelText: {
type: String,
default: '取消'
},
// 确认按钮的文字
confirmText: {
type: String,
default: '确认'
}
},
data() {
return {
//show: false
}
},
computed: {
uZIndex() {
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
}
},
methods: {
change(e) {
this.$emit('change', e);
},
// 键盘关闭
popupClose() {
// 通过发送input这个特殊的事件名,可以修改父组件传给props的value的变量,也即双向绑定
this.$emit('input', false);
},
// 输入完成
onConfirm() {
this.popupClose();
this.$emit('confirm');
},
// 取消输入
onCancel() {
this.popupClose();
this.$emit('cancel');
},
// 退格键
backspace() {
this.$emit('backspace');
},
// 关闭键盘
// close() {
// this.show = false;
// },
// // 打开键盘
// open() {
// this.show = true;
// }
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-keyboard {
position: relative;
z-index: 1003;
}
.u-tooltip {
@include vue-flex;
justify-content: space-between;
}
.u-tooltip-item {
color: #333333;
flex: 0 0 33.333333%;
text-align: center;
padding: 20rpx 10rpx;
font-size: 28rpx;
}
.u-tooltips-submit {
text-align: right;
flex-grow: 1;
flex-wrap: 0;
padding-right: 40rpx;
color: $u-type-primary;
}
.u-tooltip-cancel {
text-align: left;
flex-grow: 1;
flex-wrap: 0;
padding-left: 40rpx;
color: #888888;
}
.u-tooltips-submit-hover {
color: $u-type-success;
}
.u-tooltip-cancel-hover {
color: #333333;
}
</style>
@@ -1,244 +0,0 @@
<template>
<view class="u-wrap" :style="{
opacity: Number(opacity),
borderRadius: borderRadius + 'rpx',
// 因为time值需要改变,所以不直接用duration值(不能改变父组件prop传过来的值)
transition: `opacity ${time / 1000}s ease-in-out`
}"
:class="'u-lazy-item-' + elIndex">
<view :class="'u-lazy-item-' + elIndex">
<image :style="{borderRadius: borderRadius + 'rpx', height: imgHeight}" v-if="!isError" class="u-lazy-item"
:src="isShow ? image : loadingImg" :mode="imgMode" @load="imgLoaded" @error="loadError" @tap="clickImg"></image>
<image :style="{borderRadius: borderRadius + 'rpx', height: imgHeight}" class="u-lazy-item error" v-else :src="errorImg"
:mode="imgMode" @load="errorImgLoaded" @tap="clickImg"></image>
</view>
</view>
</template>
<script>
/**
* lazyLoad 懒加载
* @description 懒加载使用的场景为:页面有很多图片时,APP会同时加载所有的图片,导致页面卡顿,各个位置的图片出现前后不一致等.
* @tutorial https://www.uviewui.com/components/lazyLoad.html
* @property {String Number} index 用户自定义值,在事件触发时回调,用以区分是哪个图片
* @property {String} image 图片路径
* @property {String} loading-img 预加载时的占位图
* @property {String} error-img 图片加载出错时的占位图
* @property {String} threshold 触发加载时的位置,见上方说明,单位 rpx(默认300)
* @property {String Number} duration 图片加载成功时,淡入淡出时间,单位ms(默认)
* @property {String} effect 图片加载成功时,淡入淡出的css动画效果(默认ease-in-out)
* @property {Boolean} is-effect 图片加载成功时,是否启用淡入淡出效果(默认true)
* @property {String Number} border-radius 图片圆角值,单位rpx(默认0)
* @property {String Number} height 图片高度,注意:实际高度可能受img-mode参数影响(默认450)
* @property {String Number} mg-mode 图片的裁剪模式,详见image组件裁剪模式(默认widthFix)
* @event {Function} click 点击图片时触发
* @event {Function} load 图片加载成功时触发
* @event {Function} error 图片加载失败时触发
* @example <u-lazy-load :image="image" :loading-img="loadingImg" :error-img="errorImg"></u-lazy-load>
*/
export default {
name: 'u-lazy-load',
props: {
index: {
type: [Number, String]
},
// 要显示的图片
image: {
type: String,
default: ''
},
// 图片裁剪模式
imgMode: {
type: String,
default: 'widthFix'
},
// 占位图片路径
loadingImg: {
type: String,
default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAMAAAC3Ycb+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUM0QjNBQjkyQUQ2MTFFQTlCNUQ4RTIzNDE5RUIxNjciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUM0QjNBQkEyQUQ2MTFFQTlCNUQ4RTIzNDE5RUIxNjciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QzRCM0FCNzJBRDYxMUVBOUI1RDhFMjM0MTlFQjE2NyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QzRCM0FCODJBRDYxMUVBOUI1RDhFMjM0MTlFQjE2NyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtRHfPcAAAAzUExURZWVldfX18PDw62trZubm9zc3Li4uKGhoebm5tLS0uHh4c3Nzaenp729vcjIyLKysuvr6141L40AAAcXSURBVHja7NzZlqpGAEBR5lG0//9rIw7IJKJi4or7PGTdtN10wr5SVAEGf/qqArsAiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAg+nmQFMi5Jis+sIniED23jSzIgLTtg2D//iYme/8QBM/9lQ+CAEhbNLM3N9hEHAThX7GPCiBfAxK1b51kD+R7QMLjXg7iCsgWIPUh7pfVozG791oeBPngm48G583uW5GkBvI+SBaM2xXDn1oqum423bX/mgF5FySc2cv93Voug9TdZotsggnkBZB2NzbhrSY5HnoG07jei8dvzsJB/c3W60SALILE46+WCztsbhPR7R2VJq0ukEcT49nyy8QhaKcRa3fYHZD4+ufqOJAcgDz8/59vtw1I3QP5K6JsOG0vm3hce4I8LQp/BaRZGJC3AAn7IKOKXbC+7EdA5vdmmVwOLksgRThqOqiH4XEGsht+peoPUE8U/jJIO5OLH4GEwUslV5G0PTBG5Uiw/Y2jyigO3l9HAHKv9PYb82LloH74dZBoBUgar+l48NsNvtD0fkez9iwrAvIYZDRCl+Xs149Hm/KZmQ+QjUCiO1ei4ru7EsgnQYrkznlQb7thCuRfAzlOAPN72427P4VA/i2Q/DKT/Ls/VR8fvIBsDZIuz7TPF6TCbnk4GJkB2RokejTjuE7/unlgCuSTIO0Cy+Plp6vDfnQlBchy8QtjSHVd3EgmK1bHLm+H6+nXYbz2DuQRSPnqoL7vvq0u70on4zvxgCyWD3b9UyDVdW24PaWaiGTnFZJwPIQAebDpIKheBIm7n124ZthMJipAlkqHO+IZkP1tbfzOJark/A7MgKyvvl60fRqkvXfhuow+t9+q00+0/yyBrK8ZngOtBzldhw2X9tvpNGty0gvkmbPeJ0Cy/r09s/stbmfo0yMWkEdjevgKyOn2t2pxv7UXoibTdCDLje9/Ww1ymqzn87dbp92242ZmMRjI8hASvwKSLq4udqN6ksw8nxXN3tszD9L8Gkg+2mFrQYql5az4tvFj5xOx4VwnSdeBtGdyPwUytxK77pBVlNHdO7OK3rh/eTPUvdutT3fO52tuHMqD4N7llv8pyOQQ//w19YVDfX27+Sfuby9/6nau4pdA8vEdOZuChEH/quHt0Jg+IRJ/5+PrHwKZXfjbDiS73Zo7mu5UkzX7uTsXe0e/7nC3ePf1O69+BUg2XDfZCqSqOu7rGVf8cHBe8zhC2b61dtUHXv0OkGo6ZL4JkpbRYXdUaFevivx2M/1GIOctNh949TtAoumQ+TpIHMX54CJu+8BDd8FkE5BqcZh/59XvAClmTvKfB0nDqIlHo3T70SftyW1eX9dXtgQJqs1f/Q6QaOa/7wmQKtxH8eiGoCRuovODIO3VxOMmruZbHrLyD7z6DSDtGyT7ew1kf9hNn07c986JTovzzem0Id9wUG+Vk/IDr34DSNR7huZJkMFT6vEhqrPx/j5cnlZML8N6/PAzh9Y99Flm5Yde/c9BquDOkvkKkMP58dA4qi9vivE8JOvGz/j8FokfPpr288+pH2ZPOZrLmeGD+7KOh6dqYWJ48ki7yUg0tz0go/fv/LLddfV3sgOLJyaGPY/zrSlh1a36Arkzoue9CyG35ze6E6/dzO2Ga0EGHqdRJIkfn9/8OEjTW8Vq91ZWh39FeehWA7Nu9ft8CpUEk1WWOyDF0OPyEU2Pnzf/bZC0P6IPzmAvu7KauQBVrgKpJ0tG2arHzX8e5Pb3PezNs/PrX+3JMyCLn9XXf37tPFHvt09WfCDDjx+yyn1/p1V11j7GnB/q3leLuVva79S/tzed+db08YpF4uOZtmz/9oXWMq6BCAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiAALELvqt/BBgACqVeUBXxcCkAAAAASUVORK5CYII='
},
// 加载失败的错误占位图
errorImg: {
type: String,
default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAMAAAC3Ycb+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODdDMjhENDYyQUQ2MTFFQTlDQ0VBODgxQjFFOEEyMEMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODdDMjhENDcyQUQ2MTFFQTlDQ0VBODgxQjFFOEEyMEMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4N0MyOEQ0NDJBRDYxMUVBOUNDRUE4ODFCMUU4QTIwQyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4N0MyOEQ0NTJBRDYxMUVBOUNDRUE4ODFCMUU4QTIwQyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PhLwhikAAAAzUExURZWVldfX162trcPDw5ubm7i4uNzc3Obm5s3NzaGhoeHh4cjIyKenp9LS0r29vbKysuvr67sDMEkAAAlpSURBVHja7NzpYqMgAIVRUVHc8/5PO66R1WAbOzX97q+ZtDEpR0AWTR7kVyWhCAAhgABCAAGEAAIIAQQQAggBBBACCCAEEEAIIIAQQAgggBBAACGAAEIAAYQAQgABhAACCAEEEAIIIAQQAgggBBBACCCAEEAAIYAQQAAhgABCAAGEAAIIAYQAAggBBBACCCAEEEAIIAQQQAgggBBAACGAAEIAIYAAQgABhAACCAEEEAIIAQQQAgggBBBACCCAEEAAIYAQQAAhgABCAAGEAAIIAYQAAggBBBACCCAEEEAIIAQQQAgggBBAACGAAEIAIYAAQgABhAACCAEEEAIIAQQQAgggBBBACCCAEEAIIIAQQAAhgABCAAGEAEIAAYQAAggBBBACCCAEEAIIIAQQQAgggBBAACGAEEAAIYAAsqeX5QWHKIcs/Ptl03lfL4zDFPWfBGmSpPn+IZzSH5KkCL5B+n+oklwz6Iz//R2QzFOabzhEmiRirAmZt/bl0w/dpMbLqeeo4wEdpC7zR5WAPKziHKtO7ql+ReKvIa9BxgNaL5ZtEkpeAGIVp5jKJa09xVo9vgSSzQcszdYvmOqjQNSQ6pHK6rO1n1Xj32788miwHLaZz1Tl9i/yayDlYJ/60/+lp8GSY7OY1B8E4p55bWmfquFk22GLuUUxi78cX+m+BjL2GLkhMrV+/muS6Sfic0CEp5T1Yu2OQdTzsKV0MJV73KVjroyTffxfuv5Tf3fd6iLT9wz8YdVHgUzF2Is9/Xhi5sYJqP1w/GUpjOiHVbaI0w2L+pg3GZzvtokcgHxWDXHaiy78l3sPke01qphamT5c+dqyeAGSumdL/mkggauTam0e3L/mPEiqtzKDbl0Z1Wn8xOa4ySo8X/7TQIJnY/seEKWf12UmC72CKP9xYjr19RPT7NNA+oMO+R0gwmlotAry+C6I0f59ch8yXVQOr0BKYcXt1IUYRyCt+Ur9HGsrQKI79WY9sY9ARPKlzFOFdb41ioD8b5Bp+mqeeRKAxINkESBFGpOpKhgv9OuYpH8A8l4Qa3qp60Kl2/k+rG2sWafuuyCBafb2j4JkgZUob3nWcmicpkxEgmTLLGejTxnWSWCi8lPmsk6DlIHFJv24ojiYyYoGacwL8zXTLEAVaDI/Ybb3NIgKDSv2oXpmHkvNs+PTpMASEdlk7fOZeRk37fwJ6yGnQarQsGIfqqcvx43rTOXY6jf7uKXdRzdLDRPbjIrx1cIj3Kr4KyBFezzgUGuR5893qkOQ19fR2uVBaU+r16LphJNOiatK7PeBZK/Kb+tUn71rcQjSvARpghfH/yG/D2RetTuI3N5QrMWdP46brP7FmKZ//CGQ9At9SL01DLkzY/Vs8Z97fQZ7gelw7jHqCz+/Wile5J4g3Vc79eb5a6oLSue+Ve83gaSv2jp5PxCzjzwFUm9zw9MllSMil1kS4d2E9SaQ1xNo9wMxx0+nQNLnew/WDHvveMAHYm08mofl3TFI/8pD3Q6kMAv6DIi2jTCwRJUvNdDYrrJum9oHhusCbWALonwxBRk1vXMnEGWuT5wAmfYuVGUYpJ7fUZujCN92hvzwWlrFgxSfANKb10DxIMbShnfrynyZZV30imA7P43ArXXHbvBVkTCIuGy25AdBrHmNeBCpL214QdLp9LZarG3IMWrmW0ehtuO7F2PS09UcgqS3B7FKPhpknrStD0HGF/vQRne37LwLG8EbHT4WxN7/Fg0yD9Yr/3br4nnstA+0Il6QxzdBmg8A6a2/IRbkcK9h/uzV8zywF/oSkOyageCPglRWgcWClHnEzs9q/t/SENVXgFijlsq3VtXdCsRp4qObrLLLgjuzSq3fX89ZZW6AfxNIzF6X9FYgThN/fk093KkvHX/hbWd+DqS/FUhlf+G3gohEXzVs3g9iDluWoaW8fL73QhB34u+tIHIf19nLuF4Q98a09Eynnl56q+ePgEhnX+dbQOp6H5XnJ0ACd8dFgkwf12nTOTcEqd2pom+CFF02TIPw6dKmrLS5qOtBpo8b5quUtrwrSGbuqPkeSJqllTFHO02NPxdMrm+y5LKdWyWXjw4vA5nGEtnjuyCFyHqNYvEolzmASm3zK1Eg5zr13lhqV1tlksnVw8Pkwgri7O07AVKLJkutRYw87bPlRpBpNXE8xGb+fhBlvEGrGPLqViu5sILIx9dAmqF1705sxF4M8+R8P5dOdQwi12fMnATpjJ2JSt/POIvU9wPJEs/jduJAjLvU0yFT0i64Yb1bsVi79dA4pEy3TzoHMq2O7Re4vXm5O9+l290NpE4CU+YRIMNye2iaqbVS2AUnn2fsekthYKReVNutVedA5juttyIXrT38mOds+ps9DWhwL7GWc61/DVKPzVN9UHDarf1icU98IOU8tm6L031Iq63t1tKzj3fe/FCpO4F0/i0Z2+yvA1KeGBjqj1qYx8/zoxpKZ1Yl367I1k+sfcft/QPy9csXy/32qX1qLZsrryG5BGQaRj0vc/b7N54XXq293TCLB5HO42Fy517obW19b+qjl3CHp0fdLJcWvmdy1etESi/uAdJrs1hTaUklHuW8qSDdC3UfXVR5cnD3rAFSSqtFb7z7eapErx7rC739jCXfbK3aWiipjXo8UbmxXPa7QQq9R289j2Gr88N7Ag5AlHPRKc37pNZv0CZtX1tVMG6rm8qW1/KlCgQvcMss933ybwXZz3dReW5yce4ByZtHFIhwT9kmjxg8BzbKDUe1PB9edBJqSN7/KM1LmqyuMZ5BpeTUw1aD/uDI0relPfSHa/Wn8Pxq1BNfxy/h3IdwOJqIKumb9CHvTqMefyY82RoQAgggBBBACCCAEEAAIYAQQAAhgABCAAGEAAIIAYQAAggBBBACCCAEEEAIIAQQQAgggBBAACGAAEIAIYAAQgABhAACCAEEEAIIAQQQAgggBBBACCCAEEAIIIAQQAAhgABCAAGEAEIAAYQAAggBBBACCCAEEAIIIAQQQAgggBBAACGAEEAAIYAAQgABhAACCAEEEAIIAQQQAgggBBBACCCAEEAIIIAQQAAhgABCAAGEAEIAAYQAAggBBBACCCAEEAIIIAQQQAgggBBAACGAEEAAIYAAQgABhAACCAGEAAIIAQQQAgggBBBACCAEEEAIIIAQQAAhgABCACGAAEIAAYQAAggBBBACCAEEEAIIIAQQQAggfyL/BBgA8PgLdH0TBtkAAAAASUVORK5CYII='
},
// 图片进入可见区域前多少像素时,单位rpx,开始加载图片
// 负数为图片超出屏幕底部多少距离后触发懒加载,正数为图片顶部距离屏幕底部多少距离时触发(图片还没出现在屏幕上)
threshold: {
type: [Number, String],
default: 100
},
// 淡入淡出动画的过渡时间
duration: {
type: [Number, String],
default: 500
},
// 渡效果的速度曲线,各个之间差别不大,因为这是淡入淡出,且时间很短,不是那些变形或者移动的情况,会明显
// linear|ease|ease-in|ease-out|ease-in-out|cubic-bezier(n,n,n,n);
effect: {
type: String,
default: 'ease-in-out'
},
// 是否使用过渡效果
isEffect: {
type: Boolean,
default: true
},
// 圆角值
borderRadius: {
type: [Number, String],
default: 0
},
// 图片高度,单位rpx
height: {
type: [Number, String],
default: '450'
}
},
data() {
return {
isShow: false,
opacity: 1,
time: this.duration,
loadStatus: '', // 默认是懒加载中的状态
isError: false, // 图片加载失败
elIndex: this.$u.guid()
}
},
computed: {
// 将threshold从rpx转为px
getThreshold() {
// 先取绝对值,因为threshold可能是负数,最后根据this.threshold是正数或者负数,重新还原
let thresholdPx = uni.upx2px(Math.abs(this.threshold));
return this.threshold < 0 ? -thresholdPx : thresholdPx;
},
// 计算图片的高度,可能为auto,带%,或者直接数值
imgHeight() {
return this.$u.addUnit(this.height);
}
},
created() {
// 由于一些特殊原因,不能将此变量放到data中定义
this.observer = {};
},
watch: {
isShow(nVal) {
// 如果是不开启过渡效果,直接返回
if (!this.isEffect) return;
this.time = 0;
// 原来opacity为1(不透明,是为了显示占位图),改成0(透明,意味着该元素显示的是背景颜色,默认的白色),再改成1,是为了获得过渡效果
this.opacity = 0;
// 延时30ms,否则在浏览器H5,过渡效果无效
setTimeout(() => {
this.time = this.duration;
this.opacity = 1;
}, 30)
},
// 图片路径发生变化时,需要重新标记一些变量,否则会一直卡在某一个状态,比如isError
image(n) {
if(!n) {
// 如果传入null或者'',或者undefined,标记为错误状态
this.isError = true;
} else {
this.init();
this.isError = false;
}
}
},
methods: {
// 用于重新初始化
init() {
this.isError = false;
this.loadStatus = '';
},
// 点击图片触发的事件,loadlazy-还是懒加载中状态,loading-图片正在加载,loaded-图片加加载完成
clickImg() {
let whichImg = '';
// 如果isShow为false,意味着图片还没开始加载,点击的只能是最开始的占位图
if (this.isShow == false) whichImg = 'lazyImg';
// 如果isError为true,意味着图片加载失败,这是只剩下错误的占位图,所以点击的只能是错误占位图
// 当然,也可以给错误的占位图元素绑定点击事件,看你喜欢~
else if (this.isError == true) whichImg = 'errorImg';
// 总共三张图片,除了两个占位图,剩下的只能是正常的那张图片了
else whichImg = 'realImg';
// 只通知当前图片的index
this.$emit('click', this.index);
},
// 图片加载完成事件,可能是加载占位图时触发,也可能是加载真正的图片完成时触发,通过isShow区分
imgLoaded() {
// 占位图加载完成
if (this.loadStatus == '') {
this.loadStatus = 'lazyed';
}
// 真正的图片加载完成
else if (this.loadStatus == 'lazyed') {
this.loadStatus = 'loaded';
this.$emit('load', this.index);
}
},
// 错误的图片加载完成
errorImgLoaded() {
this.$emit('error', this.index);
},
// 图片加载失败
loadError() {
this.isError = true;
},
disconnectObserver(observerName) {
const observer = this[observerName];
observer && observer.disconnect();
},
},
beforeDestroy() {
// 销毁页面时,可能还没触发某张很底部的懒加载图片,所以把这个事件给去掉
//observer.disconnect();
},
mounted() {
// 此uOnReachBottom事件由mixin.js发出,目的是让页面到底时,保证所有图片都进行加载,做到绝对稳定且可靠
this.$nextTick(() => {
uni.$once('uOnReachBottom', () => {
if (!this.isShow) this.isShow = true;
});
})
// mounted的时候,不一定挂载了这个元素,延时30ms,否则会报错或者不报错,但是也没有效果
setTimeout(() => {
// 这里是组件内获取布局状态,不能用uni.createIntersectionObserver,而必须用this.createIntersectionObserver
this.disconnectObserver('contentObserver');
const contentObserver = uni.createIntersectionObserver(this);
// 要理解这里怎么计算的,请看这个:
// https://blog.csdn.net/qq_25324335/article/details/83687695
contentObserver.relativeToViewport({
bottom: this.getThreshold,
}).observe('.u-lazy-item-' + this.elIndex, (res) => {
if (res.intersectionRatio > 0) {
// 懒加载状态改变
this.isShow = true;
// 如果图片已经加载,去掉监听,减少性能的消耗
this.disconnectObserver('contentObserver');
}
})
this.contentObserver = contentObserver;
}, 30)
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-wrap {
background-color: #eee;
overflow: hidden;
}
.u-lazy-item {
width: 100%;
// 骗系统开启硬件加速
transform: transition3d(0, 0, 0);
// 防止图片加载“闪一下”
will-change: transform;
/* #ifndef APP-NVUE */
display: block;
/* #endif */
}
</style>
@@ -1,147 +0,0 @@
<template>
<view class="u-progress" :style="{
borderRadius: round ? '100rpx' : 0,
height: height + 'rpx',
backgroundColor: inactiveColor
}">
<view :class="[
type ? `u-type-${type}-bg` : '',
striped ? 'u-striped' : '',
striped && stripedActive ? 'u-striped-active' : ''
]" class="u-active" :style="[progressStyle]">
<slot v-if="$slots.default || $slots.$default" />
<block v-else-if="showPercent">
{{percent + '%'}}
</block>
</view>
</view>
</template>
<script>
/**
* lineProgress 线型进度条
* @description 展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。
* @tutorial https://www.uviewui.com/components/lineProgress.html
* @property {String Number} percent 进度条百分比值,为数值类型,0-100
* @property {Boolean} round 进度条两端是否为半圆(默认true)
* @property {String} type 如设置,active-color值将会失效
* @property {String} active-color 进度条激活部分的颜色(默认#19be6b)
* @property {String} inactive-color 进度条的底色(默认#ececec)
* @property {Boolean} show-percent 是否在进度条内部显示当前的百分比值数值(默认true)
* @property {String Number} height 进度条的高度,单位rpx(默认28)
* @property {Boolean} striped 是否显示进度条激活部分的条纹(默认false)
* @property {Boolean} striped-active 条纹是否具有动态效果(默认false)
* @example <u-line-progress :percent="70" :show-percent="true"></u-line-progress>
*/
export default {
name: "u-line-progress",
props: {
// 两端是否显示半圆形
round: {
type: Boolean,
default: true
},
// 主题颜色
type: {
type: String,
default: ''
},
// 激活部分的颜色
activeColor: {
type: String,
default: '#19be6b'
},
inactiveColor: {
type: String,
default: '#ececec'
},
// 进度百分比,数值
percent: {
type: Number,
default: 0
},
// 是否在进度条内部显示百分比的值
showPercent: {
type: Boolean,
default: true
},
// 进度条的高度,单位rpx
height: {
type: [Number, String],
default: 28
},
// 是否显示条纹
striped: {
type: Boolean,
default: false
},
// 条纹是否显示活动状态
stripedActive: {
type: Boolean,
default: false
}
},
data() {
return {
}
},
computed: {
progressStyle() {
let style = {};
style.width = this.percent + '%';
if(this.activeColor) style.backgroundColor = this.activeColor;
return style;
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-progress {
overflow: hidden;
height: 15px;
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
align-items: center;
width: 100%;
border-radius: 100rpx;
}
.u-active {
width: 0;
height: 100%;
align-items: center;
@include vue-flex;
justify-items: flex-end;
justify-content: space-around;
font-size: 20rpx;
color: #ffffff;
transition: all 0.4s ease;
}
.u-striped {
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-size: 39px 39px;
}
.u-striped-active {
animation: progress-stripes 2s linear infinite;
}
@keyframes progress-stripes {
0% {
background-position: 0 0;
}
100% {
background-position: 39px 0;
}
}
</style>
-84
View File
@@ -1,84 +0,0 @@
<template>
<view class="u-line" :style="[lineStyle]">
</view>
</template>
<script>
/**
* line 线条
* @description 此组件一般用于显示一根线条,用于分隔内容块,有横向和竖向两种模式,且能设置0.5px线条,使用也很简单
* @tutorial https://www.uviewui.com/components/line.html
* @property {String} color 线条的颜色(默认#e4e7ed)
* @property {String} length 长度,竖向时表现为高度,横向时表现为长度,可以为百分比,带rpx单位的值等
* @property {String} direction 线条的方向,row-横向,col-竖向(默认row)
* @property {String} border-style 线条的类型,solid-实线,dashed-方形虚线,dotted-圆点虚线(默认solid)
* @property {Boolean} hair-line 是否显示细线条(默认true)
* @property {String} margin 线条与上下左右元素的间距,字符串形式,如"30rpx"
* @example <u-line color="red"></u-line>
*/
export default {
name: 'u-line',
props: {
color: {
type: String,
default: '#e4e7ed'
},
// 长度,竖向时表现为高度,横向时表现为长度,可以为百分比,带rpx单位的值等
length: {
type: String,
default: '100%'
},
// 线条方向,col-竖向,row-横向
direction: {
type: String,
default: 'row'
},
// 是否显示细边框
hairLine: {
type: Boolean,
default: true
},
// 线条与上下左右元素的间距,字符串形式,如"30rpx"、"20rpx 30rpx"
margin: {
type: String,
default: '0'
},
// 线条的类型,solid-实线,dashed-方形虚线,dotted-圆点虚线
borderStyle: {
type: String,
default: 'solid'
}
},
computed: {
lineStyle() {
let style = {};
style.margin = this.margin;
// 如果是水平线条,边框高度为1px,再通过transform缩小一半,就是0.5px了
if(this.direction == 'row') {
// 此处采用兼容分开写,兼容nvue的写法
style.borderBottomWidth = '1px';
style.borderBottomStyle = this.borderStyle;
style.width = this.$u.addUnit(this.length);
if(this.hairLine) style.transform = 'scaleY(0.5)';
} else {
// 如果是竖向线条,边框宽度为1px,再通过transform缩小一半,就是0.5px了
style.borderLeftWidth = '1px';
style.borderLeftStyle = this.borderStyle;
style.height = this.$u.addUnit(this.length);
if(this.hairLine) style.transform = 'scaleX(0.5)';
}
style.borderColor = this.color;
return style;
}
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-line {
vertical-align: middle;
}
</style>
-89
View File
@@ -1,89 +0,0 @@
<template>
<text class="u-link" @tap.stop="openLink" :style="{
color: color,
fontSize: fontSize + 'rpx',
borderBottom: underLine ? `1px solid ${lineColor ? lineColor : color}` : 'none',
paddingBottom: underLine ? '0rpx' : '0'
}">
<slot></slot>
</text>
</template>
<script>
/**
* link 超链接
* @description 该组件为超链接组件,在不同平台有不同表现形式:在APP平台会通过plus环境打开内置浏览器,在小程序中把链接复制到粘贴板,同时提示信息,在H5中通过window.open打开链接。
* @tutorial https://www.uviewui.com/components/link.html
* @property {String} color 文字颜色(默认#606266)
* @property {String Number} font-size 字体大小,单位rpx(默认28)
* @property {Boolean} under-line 是否显示下划线(默认false)
* @property {String} href 跳转的链接,要带上http(s)
* @property {String} line-color 下划线颜色,默认同color参数颜色
* @property {String} mp-tips 各个小程序平台把链接复制到粘贴板后的提示语(默认“链接已复制,请在浏览器打开”)
* @example <u-link href="http://www.uviewui.com">蜀道难,难于上青天</u-link>
*/
export default {
name: "u-link",
props: {
// 文字颜色
color: {
type: String,
default: '#2979ff'
},
// 字体大小,单位rpx
fontSize: {
type: [String, Number],
default: 28
},
// 是否显示下划线
underLine: {
type: Boolean,
default: false
},
// 要跳转的链接
href: {
type: String,
default: ''
},
// 小程序中复制到粘贴板的提示语
mpTips: {
type: String,
default: '链接已复制,请在浏览器打开'
},
// 下划线颜色
lineColor: {
type: String,
default: ''
}
},
methods: {
openLink() {
// #ifdef APP-PLUS
plus.runtime.openURL(this.href)
// #endif
// #ifdef H5
window.open(this.href)
// #endif
// #ifdef MP
uni.setClipboardData({
data: this.href,
success: () => {
uni.hideToast();
this.$nextTick(() => {
this.$u.toast(this.mpTips);
})
}
});
// #endif
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-link {
line-height: 1;
}
</style>
@@ -1,25 +0,0 @@
<template>
<view class="u-loading-page">
</view>
</template>
<script>
export default {
props: {
},
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
</style>
@@ -1,103 +0,0 @@
<template>
<view v-if="show" class="u-loading" :class="mode == 'circle' ? 'u-loading-circle' : 'u-loading-flower'" :style="[cricleStyle]">
</view>
</template>
<script>
/**
* loading 加载动画
* @description 警此组件为一个小动画,目前用在uView的loadmore加载更多和switch开关等组件的正在加载状态场景。
* @tutorial https://www.uviewui.com/components/loading.html
* @property {String} mode 模式选择,见官网说明(默认circle)
* @property {String} color 动画活动区域的颜色,只对 mode = flower 模式有效(默认#c7c7c7)
* @property {String Number} size 加载图标的大小,单位rpx(默认34)
* @property {Boolean} show 是否显示动画(默认true)
* @example <u-loading mode="circle"></u-loading>
*/
export default {
name: "u-loading",
props: {
// 动画的类型
mode: {
type: String,
default: 'circle'
},
// 动画的颜色
color: {
type: String,
default: '#c7c7c7'
},
// 加载图标的大小,单位rpx
size: {
type: [String, Number],
default: '34'
},
// 是否显示动画
show: {
type: Boolean,
default: true
}
},
computed: {
// 加载中圆圈动画的样式
cricleStyle() {
let style = {};
style.width = this.size + 'rpx';
style.height = this.size + 'rpx';
if (this.mode == 'circle') style.borderColor = `#e4e4e4 #e4e4e4 #e4e4e4 ${this.color ? this.color : '#c7c7c7'}`;
return style;
},
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-loading-circle {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
vertical-align: middle;
width: 28rpx;
height: 28rpx;
background: 0 0;
border-radius: 50%;
border: 2px solid;
border-color: #e5e5e5 #e5e5e5 #e5e5e5 #8f8d8e;
animation: u-circle 1s linear infinite;
}
.u-loading-flower {
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
-webkit-animation: a 1s steps(12) infinite;
animation: u-flower 1s steps(12) infinite;
background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat;
background-size: 100%;
}
@keyframes u-flower {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(1turn);
transform: rotate(1turn);
}
}
@-webkit-keyframes u-circle {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
</style>
@@ -1,203 +0,0 @@
<template>
<view class="u-load-more-wrap" :style="{
backgroundColor: bgColor,
marginBottom: marginBottom + 'rpx',
marginTop: marginTop + 'rpx',
height: $u.addUnit(height)
}">
<u-line color="#d4d4d4" length="50"></u-line>
<!-- 加载中和没有更多的状态才显示两边的横线 -->
<view :class="status == 'loadmore' || status == 'nomore' ? 'u-more' : ''" class="u-load-more-inner">
<view class="u-loadmore-icon-wrap">
<u-loading class="u-loadmore-icon" :color="iconColor" :mode="iconType == 'circle' ? 'circle' : 'flower'" :show="status == 'loading' && icon"></u-loading>
</view>
<!-- 如果没有更多的状态下,显示内容为dot(粗点),加载特定样式 -->
<view class="u-line-1" :style="[loadTextStyle]" :class="[(status == 'nomore' && isDot == true) ? 'u-dot-text' : 'u-more-text']" @tap="loadMore">
{{ showText }}
</view>
</view>
<u-line color="#d4d4d4" length="50"></u-line>
</view>
</template>
<script>
/**
* loadmore 加载更多
* @description 此组件一般用于标识页面底部加载数据时的状态。
* @tutorial https://www.uviewui.com/components/loadMore.html
* @property {String} status 组件状态(默认loadmore)
* @property {String} bg-color 组件背景颜色,在页面是非白色时会用到(默认#ffffff)
* @property {Boolean} icon 加载中时是否显示图标(默认true)
* @property {String} icon-type 加载中时的图标类型(默认circle)
* @property {String} icon-color icon-type为circle时有效,加载中的动画图标的颜色(默认#b7b7b7)
* @property {Boolean} is-dot status为nomore时,内容显示为一个"●"(默认false)
* @property {String} color 字体颜色(默认#606266)
* @property {String Number} margin-top 到上一个相邻元素的距离
* @property {String Number} margin-bottom 到下一个相邻元素的距离
* @property {Object} load-text 自定义显示的文字,见上方说明示例
* @event {Function} loadmore status为loadmore时,点击组件会发出此事件
* @example <u-loadmore :status="status" icon-type="iconType" load-text="loadText" />
*/
export default {
name: "u-loadmore",
props: {
// 组件背景色
bgColor: {
type: String,
default: 'transparent'
},
// 是否显示加载中的图标
icon: {
type: Boolean,
default: true
},
// 字体大小
fontSize: {
type: String,
default: '28'
},
// 字体颜色
color: {
type: String,
default: '#606266'
},
// 组件状态,loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
status: {
type: String,
default: 'loadmore'
},
// 加载中状态的图标,flower-花朵状图标,circle-圆圈状图标
iconType: {
type: String,
default: 'circle'
},
// 显示的文字
loadText: {
type: Object,
default () {
return {
loadmore: '加载更多',
loading: '正在加载...',
nomore: '没有更多了'
}
}
},
// 在“没有更多”状态下,是否显示粗点
isDot: {
type: Boolean,
default: false
},
// 加载中显示圆圈动画时,动画的颜色
iconColor: {
type: String,
default: '#b7b7b7'
},
// 上边距
marginTop: {
type: [String, Number],
default: 0
},
// 下边距
marginBottom: {
type: [String, Number],
default: 0
},
// 高度,单位rpx
height: {
type: [String, Number],
default: 'auto'
}
},
data() {
return {
// 粗点
dotText: "●"
}
},
computed: {
// 加载的文字显示的样式
loadTextStyle() {
return {
color: this.color,
fontSize: this.fontSize + 'rpx',
position: 'relative',
zIndex: 1,
backgroundColor: this.bgColor,
// 如果是加载中状态,动画和文字需要距离近一点
}
},
// 加载中圆圈动画的样式
cricleStyle() {
return {
borderColor: `#e5e5e5 #e5e5e5 #e5e5e5 ${this.circleColor}`
}
},
// 加载中花朵动画形式
// 动画由base64图片生成,暂不支持修改
flowerStyle() {
return {
}
},
// 显示的提示文字
showText() {
let text = '';
if(this.status == 'loadmore') text = this.loadText.loadmore;
else if(this.status == 'loading') text = this.loadText.loading;
else if(this.status == 'nomore' && this.isDot) text = this.dotText;
else text = this.loadText.nomore;
return text;
}
},
methods: {
loadMore() {
// 只有在“加载更多”的状态下才发送点击事件,内容不满一屏时无法触发底部上拉事件,所以需要点击来触发
if(this.status == 'loadmore') this.$emit('loadmore');
}
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
/* #ifdef MP */
// 在mp.scss中,赋予了u-line为flex: 1,这里需要一个明确的长度,所以重置掉它
// 在组件内部,把组件名(u-line)当做选择器,在微信开发工具会提示不合法,但不影响使用
u-line {
flex: none;
}
/* #endif */
.u-load-more-wrap {
@include vue-flex;
justify-content: center;
align-items: center;
}
.u-load-more-inner {
@include vue-flex;
justify-content: center;
align-items: center;
padding: 0 12rpx;
}
.u-more {
position: relative;
@include vue-flex;
justify-content: center;
}
.u-dot-text {
font-size: 28rpx;
}
.u-loadmore-icon-wrap {
margin-right: 8rpx;
}
.u-loadmore-icon {
@include vue-flex;
align-items: center;
justify-content: center;
}
</style>
-123
View File
@@ -1,123 +0,0 @@
<template>
<view class="u-mask" hover-stop-propagation :style="[maskStyle, zoomStyle]" @tap="click" @touchmove.stop.prevent="() => {}" :class="{
'u-mask-zoom': zoom,
'u-mask-show': show
}">
<slot />
</view>
</template>
<script>
/**
* mask 遮罩
* @description 创建一个遮罩层,用于强调特定的页面元素,并阻止用户对遮罩下层的内容进行操作,一般用于弹窗场景
* @tutorial https://www.uviewui.com/components/mask.html
* @property {Boolean} show 是否显示遮罩(默认false)
* @property {String Number} z-index z-index 层级(默认1070)
* @property {Object} custom-style 自定义样式对象,见上方说明
* @property {String Number} duration 动画时长,单位毫秒(默认300)
* @property {Boolean} zoom 是否使用scale对遮罩进行缩放(默认true)
* @property {Boolean} mask-click-able 遮罩是否可点击,为false时点击不会发送click事件(默认true)
* @event {Function} click mask-click-able为true时,点击遮罩发送此事件
* @example <u-mask :show="show" @click="show = false"></u-mask>
*/
export default {
name: "u-mask",
props: {
// 是否显示遮罩
show: {
type: Boolean,
default: false
},
// 层级z-index
zIndex: {
type: [Number, String],
default: ''
},
// 用户自定义样式
customStyle: {
type: Object,
default () {
return {}
}
},
// 遮罩的动画样式, 是否使用使用zoom进行scale进行缩放
zoom: {
type: Boolean,
default: true
},
// 遮罩的过渡时间,单位为ms
duration: {
type: [Number, String],
default: 300
},
// 是否可以通过点击遮罩进行关闭
maskClickAble: {
type: Boolean,
default: true
}
},
data() {
return {
zoomStyle: {
transform: ''
},
scale: 'scale(1.2, 1.2)'
}
},
watch: {
show(n) {
if(n && this.zoom) {
// 当展示遮罩的时候,设置scale为1,达到缩小(原来为1.2)的效果
this.zoomStyle.transform = 'scale(1, 1)';
} else if(!n && this.zoom) {
// 当隐藏遮罩的时候,设置scale为1.2,达到放大(因为显示遮罩时已重置为1)的效果
this.zoomStyle.transform = this.scale;
}
}
},
computed: {
maskStyle() {
let style = {};
style.backgroundColor = "rgba(0, 0, 0, 0.6)";
if(this.show) style.zIndex = this.zIndex ? this.zIndex : this.$u.zIndex.mask;
else style.zIndex = -1;
style.transition = `all ${this.duration / 1000}s ease-in-out`;
// 判断用户传递的对象是否为空,不为空就进行合并
if (Object.keys(this.customStyle).length) style = {
...style,
...this.customStyle
};
return style;
}
},
methods: {
click() {
if (!this.maskClickAble) return;
this.$emit('click');
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.u-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
transition: transform 0.3s;
}
.u-mask-show {
opacity: 1;
}
.u-mask-zoom {
transform: scale(1.2, 1.2);
}
</style>

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