提交 a35010df 编写于 作者: DCloud_JSON's avatar DCloud_JSON

- 更新 文件路径:内uni-push客户端推送标识获取失败的提示

上级 98eeea10
{ {
"name" : "uni-starter", "name": "",
"appid" : "__UNI__07F7150", "appid": "",
"description" : "云端一体应用快速开发基本项目模版", "description": "云端一体应用快速开发基本项目模版",
"versionName" : "1.0.0", "versionName": "",
"versionCode" : "100", "versionCode": "100",
"transformPx" : false, "transformPx": false,
"app-plus" : { "app-plus": {
"usingComponents" : true, "usingComponents": true,
"nvueStyleCompiler" : "uni-app", "nvueStyleCompiler": "uni-app",
"compilerVersion" : 3, "compilerVersion": 3,
"splashscreen" : { "splashscreen": {
"alwaysShowBeforeRender" : true, "alwaysShowBeforeRender": true,
"waiting" : true, "waiting": true,
"autoclose" : true, "autoclose": true,
"delay" : 0 "delay": 0
},
"modules" : {},
"distribute" : {
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
"ios" : {},
"sdkConfigs" : {
"push" : {
"unipush" : null
}
}
}
},
"quickapp" : {},
"mp-weixin" : {
"appid" : "wx999bf02c8e05dfc9",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
}, },
"mp-baidu" : { "modules": {
"usingComponents" : true
}, },
"mp-toutiao" : { "distribute": {
"usingComponents" : true "android": {
}, "permissions": [
"uniStatistics" : { "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"enable" : false "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
"ios": {
},
"sdkConfigs": {
"push": {
"unipush": null
}
}
}
},
"quickapp": {
},
"mp-weixin": {
"appid": "",
"setting": {
"urlCheck": false
}, },
"vueVersion" : "2" "usingComponents": true
} },
"mp-alipay": {
"usingComponents": true
},
"mp-baidu": {
"usingComponents": true
},
"mp-toutiao": {
"usingComponents": true
},
"uniStatistics": {
"enable": false
},
"vueVersion": "2"
}
\ No newline at end of file
...@@ -191,13 +191,12 @@ ...@@ -191,13 +191,12 @@
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
overflow: hidden; overflow: hidden;
box-sizing: border-box; box-sizing: border-box;
min-width: 20px;
font-feature-settings: "tnum";
/* #endif */ /* #endif */
justify-content: center; justify-content: center;
flex-direction: row; flex-direction: row;
height: 20px; height: 20px;
min-width: 20px;
padding: 0 4px; padding: 0 4px;
line-height: 18px; line-height: 18px;
color: #fff; color: #fff;
...@@ -207,6 +206,7 @@ ...@@ -207,6 +206,7 @@
border: 1px solid #fff; border: 1px solid #fff;
text-align: center; text-align: center;
font-family: 'Helvetica Neue', Helvetica, sans-serif; font-family: 'Helvetica Neue', Helvetica, sans-serif;
font-feature-settings: "tnum";
font-size: $bage-size; font-size: $bage-size;
/* #ifdef H5 */ /* #ifdef H5 */
z-index: 999; z-index: 999;
......
## 1.0.41(2023-01-16)
- 优化 压缩依赖的文件资源大小
## 1.0.40(2023-01-16)
- 更新依赖的 验证码插件`uni-captcha`版本的版本为 0.6.4 修复 部分情况下APP端无法获取验证码的问题 [详情参考](https://ext.dcloud.net.cn/plugin?id=4048)
- 修复 客户端token过期后,点击退出登录按钮报错的问题
- uni-id-co 修复 updateUser 接口`手机号``邮箱`参数值为空字符串时,修改无效的问题
## 1.0.39(2022-12-28) ## 1.0.39(2022-12-28)
- uni-id-co 修复 URL化时第三方登录无法获取 uniPlatform 参数 - uni-id-co 修复 URL化时第三方登录无法获取 uniPlatform 参数
- uni-id-co 修复 validator error - uni-id-co 修复 validator error
## 1.0.38(2022-12-26) ## 1.0.38(2022-12-26)
- uni-id-co 优化 手机号与邮箱验证规则为空字符串时不校验 - uni-id-co 优化 手机号与邮箱验证规则为空字符串时不校验
## 1.0.37(2022-12-09) ## 1.0.37(2022-12-09)
- 优化admin端样式 - 优化admin端样式
## 1.0.36(2022-12-08) ## 1.0.36(2022-12-08)
- uni-id-co 修复 `updateUser` 接口部分参数为空时数据修改异常 - uni-id-co 修复 `updateUser` 接口部分参数为空时数据修改异常
## 1.0.35(2022-11-30) ## 1.0.35(2022-11-30)
- uni-id-co 新增 匹配到的用户不可在当前应用登录时的错误码 `uni-id-account-not-exists-in-current-app` [错误码说明](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#errcode) - uni-id-co 新增 匹配到的用户不可在当前应用登录时的错误码 `uni-id-account-not-exists-in-current-app` [错误码说明](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#errcode)
## 1.0.34(2022-11-29) ## 1.0.34(2022-11-29)
- 优化 toast 错误提示时间为3秒 - 优化 toast 错误提示时间为3秒
- uni-id-co 修复 无法从 clientInfo 中获取 uniIdToken - uni-id-co 修复 无法从 clientInfo 中获取 uniIdToken
## 1.0.33(2022-11-25) ## 1.0.33(2022-11-25)
- uni-id-co 新增 外部系统联登接口,可为外部系统创建与uni-id相对应的账号,使该账号可以使用依赖uniId的系统及功能 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external) - uni-id-co 新增 外部系统联登接口,可为外部系统创建与uni-id相对应的账号,使该账号可以使用依赖uniId的系统及功能 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external)
- uni-id-co 新增 URL化请求时鉴权签名验证 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#http-reqeust-auth) - uni-id-co 新增 URL化请求时鉴权签名验证 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#http-reqeust-auth)
- uni-id-co 修复 微信登录时用户未设置头像的报错问题 - uni-id-co 修复 微信登录时用户未设置头像的报错问题
## 1.0.32(2022-11-21) ## 1.0.32(2022-11-21)
- 新增 设置密码页面 - 新增 设置密码页面
- 新增 登录后跳转设置密码页面配置项`setPasswordAfterLogin` [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-pwd-after-login) - 新增 登录后跳转设置密码页面配置项`setPasswordAfterLogin` [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-pwd-after-login)
- uni-id-co 新增 设置密码接口 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-pwd) - uni-id-co 新增 设置密码接口 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-pwd)
## 1.0.31(2022-11-16) ## 1.0.31(2022-11-16)
- uni-id-co 修复 验证码可能无法收到的bug - uni-id-co 修复 验证码可能无法收到的bug
## 1.0.30(2022-11-11) ## 1.0.30(2022-11-11)
- uni-id-co 修复 用户只有openid时绑定微信/QQ报错 - uni-id-co 修复 用户只有openid时绑定微信/QQ报错
## 1.0.29(2022-11-10) ## 1.0.29(2022-11-10)
- uni-id-co 支持URL化方式请求 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#adapter-http) - uni-id-co 支持URL化方式请求 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#adapter-http)
## 1.0.28(2022-11-09) ## 1.0.28(2022-11-09)
- uni-id-co 升级密码加密算法,支持hmac-sha256加密 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#password-safe) - uni-id-co 升级密码加密算法,支持hmac-sha256加密 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#password-safe)
- uni-id-co 新增 开发者可以自定义密码加密规则 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#custom-password-encrypt) - uni-id-co 新增 开发者可以自定义密码加密规则 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#custom-password-encrypt)
- uni-id-co 新增 支持将其他系统用户迁移至uni-id [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#move-users-to-uni-id) - uni-id-co 新增 支持将其他系统用户迁移至uni-id [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#move-users-to-uni-id)
## 1.0.27(2022-10-26) ## 1.0.27(2022-10-26)
- uni-id-co 新增 secureNetworkHandshakeByWeixin 接口,用于建立和微信小程序的安全网络连接 - uni-id-co 新增 secureNetworkHandshakeByWeixin 接口,用于建立和微信小程序的安全网络连接
## 1.0.26(2022-10-18) ## 1.0.26(2022-10-18)
- 修复 uni-id-pages 导入时异常的Bug - 修复 uni-id-pages 导入时异常的Bug
## 1.0.25(2022-10-14) ## 1.0.25(2022-10-14)
- uni-id-co 增加 微信授权手机号登录方式 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin-mobile) - uni-id-co 增加 微信授权手机号登录方式 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin-mobile)
- uni-id-co 增加 解绑第三方平台账号 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-third-account) - uni-id-co 增加 解绑第三方平台账号 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-third-account)
- uni-id-co 微信绑定手机号支持通过`getPhoneNumber`事件回调的`code`绑定 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-mp-weixin) - uni-id-co 微信绑定手机号支持通过`getPhoneNumber`事件回调的`code`绑定 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-mp-weixin)
- 修复 sendSmsCode 接口未在参数内传递 templateId 时 未能从配置文件读取 templateId 的Bug - 修复 sendSmsCode 接口未在参数内传递 templateId 时 未能从配置文件读取 templateId 的Bug
## 1.0.24(2022-10-08) ## 1.0.24(2022-10-08)
- 修复 报uni-id-users表schema内错误的bug - 修复 报uni-id-users表schema内错误的bug
## 1.0.23(2022-10-08) ## 1.0.23(2022-10-08)
- 修复 vue3下vite编译发行打包失败 - 修复 vue3下vite编译发行打包失败
- 修复 某些情况下注册账号,报TypeErroe:Cannot read properties of undefined (reading ’showToast‘)的错误 - 修复 某些情况下注册账号,报TypeErroe:Cannot read properties of undefined (reading ’showToast‘)的错误
## 1.0.22(2022-09-23) ## 1.0.22(2022-09-23)
- 修复 某些情况下,修改密码报“两次输入密码不一致”的bug - 修复 某些情况下,修改密码报“两次输入密码不一致”的bug
## 1.0.21(2022-09-21) ## 1.0.21(2022-09-21)
- 修复 store.hasLogin的值在某些情况下会出错的bug - 修复 store.hasLogin的值在某些情况下会出错的bug
## 1.0.20(2022-09-21) ## 1.0.20(2022-09-21)
- 新增 store 账号信息状态管理,详情:用户中心页面 路径:`/uni_modules/uni-id-pages/pages/userinfo/userinfo` - 新增 store 账号信息状态管理,详情:用户中心页面 路径:`/uni_modules/uni-id-pages/pages/userinfo/userinfo`
## 1.0.19(2022-09-20) ## 1.0.19(2022-09-20)
- 修复 小程序端,使用将自定义节点设置成[虚拟节点](https://uniapp.dcloud.net.cn/tutorial/vue-api.html#%E5%85%B6%E4%BB%96%E9%85%8D%E7%BD%AE)的uni-ui组件,样式错乱的问题 - 修复 小程序端,使用将自定义节点设置成[虚拟节点](https://uniapp.dcloud.net.cn/tutorial/vue-api.html#%E5%85%B6%E4%BB%96%E9%85%8D%E7%BD%AE)的uni-ui组件,样式错乱的问题
## 1.0.18(2022-09-20) ## 1.0.18(2022-09-20)
- 修复 微信小程序端 WXSS 编译报错的bug - 修复 微信小程序端 WXSS 编译报错的bug
## 1.0.17(2022.09-19) ## 1.0.17(2022.09-19)
- 修复 无法退出登录的bug - 修复 无法退出登录的bug
## 1.0.16(2022-09-19) ## 1.0.16(2022-09-19)
- 修复 在 Edge 浏览器下 input[type="password"] 会出现浏览器自带的密码查看按钮 - 修复 在 Edge 浏览器下 input[type="password"] 会出现浏览器自带的密码查看按钮
- 优化 退出登录重定向页面为 uniIdRouter.loginPage - 优化 退出登录重定向页面为 uniIdRouter.loginPage
- 新增 注册账号页面支持返回登录页面 - 新增 注册账号页面支持返回登录页面
## 1.0.15(2022-09-19) ## 1.0.15(2022-09-19)
- 更新表结构,解决在uni-admin中部分clientDB操作没有权限的问题 - 更新表结构,解决在uni-admin中部分clientDB操作没有权限的问题
## 1.0.14(2022-09-16) ## 1.0.14(2022-09-16)
- 修改 配置项`isAdmin`默认值为`false` - 修改 配置项`isAdmin`默认值为`false`
## 1.0.13(2022-09-16) ## 1.0.13(2022-09-16)
- 新增 管理员注册页面 - 新增 管理员注册页面
- 新增 配置项`isAdmin`区分是否为管理端 - 新增 配置项`isAdmin`区分是否为管理端
- 新增 登录成功后自动跳转;跳转优先级:路由携带(`uniIdRedirectUrl`参数) > 返回上一路由 > 跳转首页 - 新增 登录成功后自动跳转;跳转优先级:路由携带(`uniIdRedirectUrl`参数) > 返回上一路由 > 跳转首页
- uni-id-co 优化 注册管理员时管理员存在提示文案 - uni-id-co 优化 注册管理员时管理员存在提示文案
## 1.0.12(2022-09-07) ## 1.0.12(2022-09-07)
- 修复 getSupportedLoginType判断是否支持微信公众号、PC网页微信扫码登录方式报错的Bug - 修复 getSupportedLoginType判断是否支持微信公众号、PC网页微信扫码登录方式报错的Bug
- 优化 适配pc端样式 - 优化 适配pc端样式
- 新增 邮箱验证码注册 - 新增 邮箱验证码注册
- 新增 邮箱验证码找回密码 - 新增 邮箱验证码找回密码
- 新增 退出登录(全局)回调事件:`uni-id-pages-logout`,支持通过[uni.$on](https://uniapp.dcloud.net.cn/api/window/communication.html#on)监听; - 新增 退出登录(全局)回调事件:`uni-id-pages-logout`,支持通过[uni.$on](https://uniapp.dcloud.net.cn/api/window/communication.html#on)监听;
- 调整 抽离退出登录方法至`/uni_modules/uni-id-pages/common/common.js`中,方便在项目其他页面中调用 - 调整 抽离退出登录方法至`/uni_modules/uni-id-pages/common/common.js`中,方便在项目其他页面中调用
- 调整 用户中心(路径:`/uni_modules/uni-id-pages/pages/userinfo/userinfo`)默认不再显示退出登录按钮。支持页面传参数`showLoginManage=true`恢复显示 - 调整 用户中心(路径:`/uni_modules/uni-id-pages/pages/userinfo/userinfo`)默认不再显示退出登录按钮。支持页面传参数`showLoginManage=true`恢复显示
## 1.0.11(2022-09-01) ## 1.0.11(2022-09-01)
- 修复 iOS端,一键登录功能卡在showLoading的问题 - 修复 iOS端,一键登录功能卡在showLoading的问题
- 更新 合并密码强度与长度配置 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#config) - 更新 合并密码强度与长度配置 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#config)
- uni-id-co 修复 调用 removeAuthorizedApp 接口报错的Bug - uni-id-co 修复 调用 removeAuthorizedApp 接口报错的Bug
- uni-id-co 新增 管理端接口 updateUser [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-user) - uni-id-co 新增 管理端接口 updateUser [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-user)
- uni-id-co 调整 为兼容旧版本,未配置密码强度时提供最简单的密码规则校验(长度大于6即可) - uni-id-co 调整 为兼容旧版本,未配置密码强度时提供最简单的密码规则校验(长度大于6即可)
- uni-id-co 调整 注册、登录时如果携带了token则尝试对此token进行登出操作 - uni-id-co 调整 注册、登录时如果携带了token则尝试对此token进行登出操作
- uni-id-co 调整 管理端接口 addUser 增加 mobile、email等参数 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#add-user) - uni-id-co 调整 管理端接口 addUser 增加 mobile、email等参数 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#add-user)
## 1.0.10(2022-08-25) ## 1.0.10(2022-08-25)
- 修复 导入uni-id-pages插件时未自动导入uni-open-bridge-common的Bug - 修复 导入uni-id-pages插件时未自动导入uni-open-bridge-common的Bug
## 1.0.9(2022-08-23) ## 1.0.9(2022-08-23)
- 修复 uni-id-co 缺失uni-open-bridge-common依赖的Bug - 修复 uni-id-co 缺失uni-open-bridge-common依赖的Bug
## 1.0.8(2022-08-23) ## 1.0.8(2022-08-23)
- 新增 H5端支持微信登录(含微信公众号内的网页授权登录 和 普通浏览器内网页生成二维码,实现手机微信扫码登录)[详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#weixinlogin) - 新增 H5端支持微信登录(含微信公众号内的网页授权登录 和 普通浏览器内网页生成二维码,实现手机微信扫码登录)[详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#weixinlogin)
- 新增 登录成功(全局)回调事件:`uni-id-pages-login-success`,支持通过[uni.$on](https://uniapp.dcloud.net.cn/api/window/communication.html#on)监听; - 新增 登录成功(全局)回调事件:`uni-id-pages-login-success`,支持通过[uni.$on](https://uniapp.dcloud.net.cn/api/window/communication.html#on)监听;
- 新增 密码强度(是否必须包含大小写字母、数字和特殊符号以及长度)配置 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#config) - 新增 密码强度(是否必须包含大小写字母、数字和特殊符号以及长度)配置 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#config)
- 调整 uni-id-co 密码规则调整,废除之前的简单校验,允许配置密码强度 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#password-strength) - 调整 uni-id-co 密码规则调整,废除之前的简单校验,允许配置密码强度 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#password-strength)
- 调整 uni-id-co 存储用户 openid 时同时以客户端 AppId 为 Key 的副本,参考:[微信登录](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin)[QQ登录](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-qq) - 调整 uni-id-co 存储用户 openid 时同时以客户端 AppId 为 Key 的副本,参考:[微信登录](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin)[QQ登录](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-qq)
- 调整 uni-id-co 依赖 uni-open-bridge-common 存储用户 session_key、access_token 等信息 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#save-user-token) - 调整 uni-id-co 依赖 uni-open-bridge-common 存储用户 session_key、access_token 等信息 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#save-user-token)
- 新增 uni-id-co 增加 beforeRegister 钩子用户在注册前向用户记录内添加一些数据 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#before-register) - 新增 uni-id-co 增加 beforeRegister 钩子用户在注册前向用户记录内添加一些数据 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#before-register)
## 1.0.7(2022-07-19) ## 1.0.7(2022-07-19)
- 修复 uni-id-co接口 logout时没有删除token的Bug - 修复 uni-id-co接口 logout时没有删除token的Bug
## 1.0.6(2022-07-13) ## 1.0.6(2022-07-13)
- 新增 允许覆盖内置校验规则 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#custom-validator) - 新增 允许覆盖内置校验规则 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#custom-validator)
- 修复 app端clientInfo.appVersionCode为数字导致校验无法通过的Bug - 修复 app端clientInfo.appVersionCode为数字导致校验无法通过的Bug
## 1.0.5(2022-07-11) ## 1.0.5(2022-07-11)
修复 微信小程序调用uni-id-co接口报错的Bug [详情](https://ask.dcloud.net.cn/question/148877) 修复 微信小程序调用uni-id-co接口报错的Bug [详情](https://ask.dcloud.net.cn/question/148877)
## 1.0.4(2022-07-06) ## 1.0.4(2022-07-06)
- uni-id-co增加clientInfo字段类型校验 - uni-id-co增加clientInfo字段类型校验
- 监听token更新时机,同步客户端push_clientid至uni-id-device表,改为:同步客户端push_clientid至uni-id-device表和opendb-device表 - 监听token更新时机,同步客户端push_clientid至uni-id-device表,改为:同步客户端push_clientid至uni-id-device表和opendb-device表
## 1.0.3(2022-07-05) ## 1.0.3(2022-07-05)
新增监听token更新时机,同步客户端push_clientid至uni-id-device表 新增监听token更新时机,同步客户端push_clientid至uni-id-device表
## 1.0.2(2022-07-04) ## 1.0.2(2022-07-04)
修复微信小程序登录时无unionid报错的Bug [详情](https://ask.dcloud.net.cn/question/148016) 修复微信小程序登录时无unionid报错的Bug [详情](https://ask.dcloud.net.cn/question/148016)
## 1.0.1(2022-06-28) ## 1.0.1(2022-06-28)
添加相关uni-id表 添加相关uni-id表
## 1.0.0(2022-06-23) ## 1.0.0(2022-06-23)
正式版 正式版
import pagesJson from '@/pages.json' import pagesJson from '@/pages.json'
import config from '@/uni_modules/uni-id-pages/config.js' import config from '@/uni_modules/uni-id-pages/config.js'
const uniIdCo = uniCloud.importObject("uni-id-co") const uniIdCo = uniCloud.importObject("uni-id-co")
const db = uniCloud.database(); const db = uniCloud.database();
const usersTable = db.collection('uni-id-users') const usersTable = db.collection('uni-id-users')
let hostUserInfo = uni.getStorageSync('uni-id-pages-userInfo')||{} let hostUserInfo = uni.getStorageSync('uni-id-pages-userInfo')||{}
// console.log( hostUserInfo); // console.log( hostUserInfo);
const data = { const data = {
userInfo: hostUserInfo, userInfo: hostUserInfo,
hasLogin: Object.keys(hostUserInfo).length != 0 hasLogin: Object.keys(hostUserInfo).length != 0
} }
// console.log('data', data); // console.log('data', data);
// 定义 mutations, 修改属性 // 定义 mutations, 修改属性
export const mutations = { export const mutations = {
// data不为空,表示传递要更新的值(注意不是覆盖是合并),什么也不传时,直接查库获取更新 // data不为空,表示传递要更新的值(注意不是覆盖是合并),什么也不传时,直接查库获取更新
async updateUserInfo(data = false) { async updateUserInfo(data = false) {
if (data) { if (data) {
usersTable.where('_id==$env.uid').update(data).then(e => { usersTable.where('_id==$env.uid').update(data).then(e => {
// console.log(e); // console.log(e);
if (e.result.updated) { if (e.result.updated) {
uni.showToast({ uni.showToast({
title: "更新成功", title: "更新成功",
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
this.setUserInfo(data) this.setUserInfo(data)
} else { } else {
uni.showToast({ uni.showToast({
title: "没有改变", title: "没有改变",
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
} }
}) })
} else { } else {
try { try {
let res = await usersTable.where("'_id' == $cloudEnv_uid") let res = await usersTable.where("'_id' == $cloudEnv_uid")
.field('mobile,nickname,username,email,avatar_file') .field('mobile,nickname,username,email,avatar_file')
.get() .get()
// console.log('fromDbData',res.result.data); // console.log('fromDbData',res.result.data);
this.setUserInfo(res.result.data[0]) this.setUserInfo(res.result.data[0])
} catch (e) { } catch (e) {
this.setUserInfo({},{cover:true}) this.setUserInfo({},{cover:true})
console.error(e.message, e.errCode); console.error(e.message, e.errCode);
} }
} }
}, },
async setUserInfo(data, {cover}={cover:false}) { async setUserInfo(data, {cover}={cover:false}) {
// console.log('set-userInfo', data); // console.log('set-userInfo', data);
let userInfo = cover?data:Object.assign(store.userInfo,data) let userInfo = cover?data:Object.assign(store.userInfo,data)
store.userInfo = Object.assign({},userInfo) store.userInfo = Object.assign({},userInfo)
store.hasLogin = Object.keys(store.userInfo).length != 0 store.hasLogin = Object.keys(store.userInfo).length != 0
// console.log('store.userInfo', store.userInfo); // console.log('store.userInfo', store.userInfo);
uni.setStorage({ uni.setStorage({
key: "uni-id-pages-userInfo", key: "uni-id-pages-userInfo",
data:store.userInfo data:store.userInfo
}) })
return data return data
}, },
async logout() { async logout() {
await uniIdCo.logout() // 1. 已经过期就不需要调用服务端的注销接口 2.即使调用注销接口失败,不能阻塞客户端
uni.removeStorageSync('uni_id_token'); if(uniCloud.getCurrentUserInfo().tokenExpired > Date.now()){
uni.setStorageSync('uni_id_token_expired', 0) try{
uni.redirectTo({ await uniIdCo.logout()
url: `/${pagesJson.uniIdRouter?.loginPage ?? 'uni_modules/uni-id-pages/pages/login/login-withoutpwd'}`, }catch(e){
}); console.error(e);
uni.$emit('uni-id-pages-logout') }
this.setUserInfo({},{cover:true}) }
}, uni.removeStorageSync('uni_id_token');
uni.setStorageSync('uni_id_token_expired', 0)
loginBack (e = {}) { uni.redirectTo({
const {uniIdRedirectUrl = ''} = e url: `/${pagesJson.uniIdRouter?.loginPage ?? 'uni_modules/uni-id-pages/pages/login/login-withoutpwd'}`,
let delta = 0; //判断需要返回几层 });
let pages = getCurrentPages(); uni.$emit('uni-id-pages-logout')
// console.log(pages); this.setUserInfo({},{cover:true})
pages.forEach((page, index) => { },
if (pages[pages.length - index - 1].route.split('/')[3] == 'login') {
delta++ loginBack (e = {}) {
} const {uniIdRedirectUrl = ''} = e
}) let delta = 0; //判断需要返回几层
// console.log('判断需要返回几层:', delta); let pages = getCurrentPages();
if (uniIdRedirectUrl) { // console.log(pages);
return uni.reLaunch({ pages.forEach((page, index) => {
url: uniIdRedirectUrl if (pages[pages.length - index - 1].route.split('/')[3] == 'login') {
}) delta++
} }
// #ifdef H5 })
if (e.loginType == 'weixin') { // console.log('判断需要返回几层:', delta);
// console.log('window.history', window.history); if (uniIdRedirectUrl) {
return window.history.go(-3) return uni.reLaunch({
} url: uniIdRedirectUrl
// #endif })
}
if (delta) { // #ifdef H5
const page = pagesJson.pages[0] if (e.loginType == 'weixin') {
return uni.reLaunch({ // console.log('window.history', window.history);
url: `/${page.path}` return window.history.go(-3)
}) }
} // #endif
uni.navigateBack({ if (delta) {
delta const page = pagesJson.pages[0]
}) return uni.reLaunch({
}, url: `/${page.path}`
loginSuccess(e = {}){ })
const { }
showToast = true, toastText = '登录成功', autoBack = true, uniIdRedirectUrl = '', passwordConfirmed
} = e uni.navigateBack({
// console.log({toastText,autoBack}); delta
if (showToast) { })
uni.showToast({ },
title: toastText, loginSuccess(e = {}){
icon: 'none', const {
duration: 3000 showToast = true, toastText = '登录成功', autoBack = true, uniIdRedirectUrl = '', passwordConfirmed
}); } = e
} // console.log({toastText,autoBack});
this.updateUserInfo() if (showToast) {
uni.showToast({
uni.$emit('uni-id-pages-login-success') title: toastText,
icon: 'none',
if (config.setPasswordAfterLogin && !passwordConfirmed) { duration: 3000
return uni.redirectTo({ });
url: uniIdRedirectUrl ? `/uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd?uniIdRedirectUrl=${uniIdRedirectUrl}&loginType=${e.loginType}`: `/uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd?loginType=${e.loginType}`, }
fail: (err) => { this.updateUserInfo()
console.log(err)
} uni.$emit('uni-id-pages-login-success')
})
} if (config.setPasswordAfterLogin && !passwordConfirmed) {
return uni.redirectTo({
if (autoBack) { url: uniIdRedirectUrl ? `/uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd?uniIdRedirectUrl=${uniIdRedirectUrl}&loginType=${e.loginType}`: `/uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd?loginType=${e.loginType}`,
this.loginBack(uniIdRedirectUrl) fail: (err) => {
} console.log(err)
} }
})
} }
// #ifdef VUE2 if (autoBack) {
import Vue from 'vue' this.loginBack(uniIdRedirectUrl)
// 通过Vue.observable创建一个可响应的对象 }
export const store = Vue.observable(data) }
// #endif
}
// #ifdef VUE3
import { // #ifdef VUE2
reactive import Vue from 'vue'
} from 'vue' // 通过Vue.observable创建一个可响应的对象
// 通过Vue.observable创建一个可响应的对象 export const store = Vue.observable(data)
export const store = reactive(data) // #endif
// #endif
// #ifdef VUE3
import {
reactive
} from 'vue'
// 通过Vue.observable创建一个可响应的对象
export const store = reactive(data)
// #endif
<template> <template>
<view> <view>
<uni-captcha :focus="focusCaptchaInput" ref="captcha" scene="send-email-code" v-model="captcha" /> <uni-captcha :focus="focusCaptchaInput" ref="captcha" scene="send-email-code" v-model="captcha" />
<view class="box"> <view class="box">
<uni-easyinput :focus="focusEmailCodeInput" @blur="focusEmailCodeInput = false" type="number" class="input-box" :inputBorder="false" v-model="modelValue" maxlength="6" <uni-easyinput :focus="focusEmailCodeInput" @blur="focusEmailCodeInput = false" type="number" class="input-box" :inputBorder="false" v-model="modelValue" maxlength="6"
placeholder="请输入邮箱验证码"> placeholder="请输入邮箱验证码">
</uni-easyinput> </uni-easyinput>
<view class="short-code-btn" hover-class="hover" @click="start"> <view class="short-code-btn" hover-class="hover" @click="start">
<text class="inner-text" :class="reverseNumber==0?'inner-text-active':''">{{innerText}}</text> <text class="inner-text" :class="reverseNumber==0?'inner-text-active':''">{{innerText}}</text>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
function debounce(func, wait) { function debounce(func, wait) {
let timer; let timer;
wait = wait || 500; wait = wait || 500;
return function() { return function() {
let context = this; let context = this;
let args = arguments; let args = arguments;
if (timer) clearTimeout(timer); if (timer) clearTimeout(timer);
let callNow = !timer; let callNow = !timer;
timer = setTimeout(() => { timer = setTimeout(() => {
timer = null; timer = null;
}, wait) }, wait)
if (callNow) func.apply(context, args); if (callNow) func.apply(context, args);
} }
} }
/** /**
* email-code-form * email-code-form
* @description 获取邮箱验证码组件 * @description 获取邮箱验证码组件
* @tutorial https://ext.dcloud.net.cn/plugin?id= * @tutorial https://ext.dcloud.net.cn/plugin?id=
* @property {Number} count 倒计时时长 s * @property {Number} count 倒计时时长 s
* @property {String} email 邮箱 * @property {String} email 邮箱
* @property {String} type = [login-by-email-code|reset-pwd-by-email-code|bind-email] 验证码类型,用于防止不同功能的验证码混用,目前支持的类型login登录、register注册、bind绑定邮箱、unbind解绑邮箱 * @property {String} type = [login-by-email-code|reset-pwd-by-email-code|bind-email] 验证码类型,用于防止不同功能的验证码混用,目前支持的类型login登录、register注册、bind绑定邮箱、unbind解绑邮箱
* @property {false} focusCaptchaInput = [true|false] 验证码输入框是否默认获取焦点 * @property {false} focusCaptchaInput = [true|false] 验证码输入框是否默认获取焦点
*/ */
export default { export default {
name: "uni-email-code-form", name: "uni-email-code-form",
model: { model: {
prop: 'modelValue', prop: 'modelValue',
event: 'update:modelValue' event: 'update:modelValue'
}, },
props: { props: {
event: ['update:modelValue'], event: ['update:modelValue'],
/** /**
* 倒计时时长 s * 倒计时时长 s
*/ */
count: { count: {
type: [String, Number], type: [String, Number],
default: 60 default: 60
}, },
/** /**
* 邮箱 * 邮箱
*/ */
email: { email: {
type: [String], type: [String],
default: '' default: ''
}, },
/* /*
验证码类型,用于防止不同功能的验证码混用,目前支持的类型login登录、register注册、bind绑定邮箱、unbind解绑邮箱 验证码类型,用于防止不同功能的验证码混用,目前支持的类型login登录、register注册、bind绑定邮箱、unbind解绑邮箱
*/ */
type: { type: {
type: String, type: String,
default () { default () {
return 'register' return 'register'
} }
}, },
/* /*
验证码输入框是否默认获取焦点 验证码输入框是否默认获取焦点
*/ */
focusCaptchaInput: { focusCaptchaInput: {
type: Boolean, type: Boolean,
default () { default () {
return false return false
} }
}, },
}, },
data() { data() {
return { return {
captcha: "", captcha: "",
reverseNumber: 0, reverseNumber: 0,
reverseTimer: null, reverseTimer: null,
modelValue: "", modelValue: "",
focusEmailCodeInput:false focusEmailCodeInput:false
}; };
}, },
watch: { watch: {
captcha(value, oldValue) { captcha(value, oldValue) {
if (value.length == 4 && oldValue.length != 4) { if (value.length == 4 && oldValue.length != 4) {
this.start() this.start()
} }
}, },
modelValue(value) { modelValue(value) {
// TODO 兼容 vue2 // TODO 兼容 vue2
this.$emit('input', value); this.$emit('input', value);
// TODO 兼容 vue3 // TODO 兼容 vue3
this.$emit('update:modelValue', value) this.$emit('update:modelValue', value)
} }
}, },
computed: { computed: {
innerText() { innerText() {
if (this.reverseNumber == 0) return "获取邮箱验证码"; if (this.reverseNumber == 0) return "获取邮箱验证码";
return "重新发送" + '(' + this.reverseNumber + 's)'; return "重新发送" + '(' + this.reverseNumber + 's)';
} }
}, },
created() { created() {
this.initClick(); this.initClick();
}, },
methods: { methods: {
getImageCaptcha(focus) { getImageCaptcha(focus) {
this.$refs.captcha.getImageCaptcha(focus) this.$refs.captcha.getImageCaptcha(focus)
}, },
initClick() { initClick() {
this.start = debounce(() => { this.start = debounce(() => {
if (this.reverseNumber != 0) return; if (this.reverseNumber != 0) return;
this.sendMsg(); this.sendMsg();
}) })
}, },
sendMsg() { sendMsg() {
if (this.captcha.length != 4) { if (this.captcha.length != 4) {
this.$refs.captcha.focusCaptchaInput = true this.$refs.captcha.focusCaptchaInput = true
return uni.showToast({ return uni.showToast({
title: '请先输入图形验证码', title: '请先输入图形验证码',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
} }
if(!this.email) return uni.showToast({ if(!this.email) return uni.showToast({
title: "请输入邮箱", title: "请输入邮箱",
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
let reg_email = /@/; let reg_email = /@/;
if (!reg_email.test(this.email)) return uni.showToast({ if (!reg_email.test(this.email)) return uni.showToast({
title: "邮箱格式错误", title: "邮箱格式错误",
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
const uniIdCo = uniCloud.importObject("uni-id-co", { const uniIdCo = uniCloud.importObject("uni-id-co", {
customUI: true customUI: true
}) })
// console.log('uniIdCo', uniIdCo) // console.log('uniIdCo', uniIdCo)
console.log('sendEmailCode',{ console.log('sendEmailCode',{
"email": this.email, "email": this.email,
"scene": this.type, "scene": this.type,
"captcha": this.captcha "captcha": this.captcha
}); });
uniIdCo.sendEmailCode({ uniIdCo.sendEmailCode({
"email": this.email, "email": this.email,
"scene": this.type, "scene": this.type,
"captcha": this.captcha "captcha": this.captcha
}).then(result => { }).then(result => {
// console.log(result.code); // console.log(result.code);
uni.showToast({ uni.showToast({
title: "邮箱验证码发送成功", title: "邮箱验证码发送成功",
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
this.reverseNumber = Number(this.count); this.reverseNumber = Number(this.count);
this.getCode(); this.getCode();
}).catch(e => { }).catch(e => {
// console.log(JSON.stringify(e)); // console.log(JSON.stringify(e));
if (e.code == "uni-id-invalid-mail-template") { if (e.code == "uni-id-invalid-mail-template") {
this.modelValue = "123456" this.modelValue = "123456"
uni.showToast({ uni.showToast({
title: '已启动测试模式,详情【控制台信息】', title: '已启动测试模式,详情【控制台信息】',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
console.warn(e.message); console.warn(e.message);
} else { } else {
this.getImageCaptcha() this.getImageCaptcha()
this.captcha = "" this.captcha = ""
uni.showToast({ uni.showToast({
title: e.message, title: e.message,
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
} }
}) })
}, },
getCode() { getCode() {
if (this.reverseNumber == 0) { if (this.reverseNumber == 0) {
clearTimeout(this.reverseTimer); clearTimeout(this.reverseTimer);
this.reverseTimer = null; this.reverseTimer = null;
return; return;
} }
this.reverseNumber--; this.reverseNumber--;
this.reverseTimer = setTimeout(() => { this.reverseTimer = setTimeout(() => {
this.getCode(); this.getCode();
}, 1000) }, 1000)
} }
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.box { .box {
position: relative; position: relative;
margin-top: 10px; margin-top: 10px;
} }
.short-code-btn { .short-code-btn {
padding: 0; padding: 0;
position: absolute; position: absolute;
top: 0; top: 0;
right: 8px; right: 8px;
width: 260rpx; width: 260rpx;
max-width: 130px; max-width: 130px;
height: 44px; height: 44px;
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.inner-text { .inner-text {
font-size: 14px; font-size: 14px;
color: #AAAAAA; color: #AAAAAA;
} }
.inner-text-active { .inner-text-active {
color: #04498c; color: #04498c;
} }
.captcha { .captcha {
width: 350rpx; width: 350rpx;
} }
.input-box { .input-box {
margin: 0; margin: 0;
padding: 4px; padding: 4px;
background-color: #F8F8F8; background-color: #F8F8F8;
font-size: 14px; font-size: 14px;
} }
.box ::v-deep .content-clear-icon { .box ::v-deep .content-clear-icon {
margin-right: 100px; margin-right: 100px;
} }
.box { .box {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
flex-direction: row; flex-direction: row;
} }
</style> </style>
<template> <template>
<uni-popup ref="popup" type="bottom"> <uni-popup ref="popup" type="bottom">
<view class="box"> <view class="box">
<text class="headBox">绑定资料</text> <text class="headBox">绑定资料</text>
<text class="tip">获取你的微信头像和昵称,完善你的个人资料</text> <text class="tip">获取你的微信头像和昵称,完善你的个人资料</text>
<view class="btnBox"> <view class="btnBox">
<text @click="closeMe" class="close">关闭</text> <text @click="closeMe" class="close">关闭</text>
<button class="agree uni-btn" type="primary" @click="getUserProfile">确定</button> <button class="agree uni-btn" type="primary" @click="getUserProfile">确定</button>
</view> </view>
</view> </view>
</uni-popup> </uni-popup>
</template> </template>
<script> <script>
const db = uniCloud.database(); const db = uniCloud.database();
const usersTable = db.collection('uni-id-users') const usersTable = db.collection('uni-id-users')
let userId = '' let userId = ''
export default { export default {
emits:['next'], emits:['next'],
data() { data() {
return {} return {}
}, },
methods: { methods: {
async open(uid){ async open(uid){
userId = uid userId = uid
this.$refs.popup.open() this.$refs.popup.open()
}, },
async getUserProfile(){ async getUserProfile(){
uni.showLoading(); uni.showLoading();
let res = await new Promise((callBack) => { let res = await new Promise((callBack) => {
uni.getUserProfile({ uni.getUserProfile({
desc: "用于设置账户昵称和头像", desc: "用于设置账户昵称和头像",
complete: (e) => { complete: (e) => {
// console.log("getUserProfile:", e); // console.log("getUserProfile:", e);
callBack(e) callBack(e)
} }
}) })
}) })
// console.log("userInfo", res.userInfo); // console.log("userInfo", res.userInfo);
if(res.errMsg != "getUserProfile:ok"){ if(res.errMsg != "getUserProfile:ok"){
return this.closeMe() return this.closeMe()
} }
let {avatarUrl,nickName} = res.userInfo; let {avatarUrl,nickName} = res.userInfo;
let tempFilePath = await new Promise((callBack)=>{ let tempFilePath = await new Promise((callBack)=>{
uni.downloadFile({ uni.downloadFile({
url: avatarUrl, url: avatarUrl,
success: (res) => { success: (res) => {
if (res.statusCode === 200) { if (res.statusCode === 200) {
// console.log('下载成功'); // console.log('下载成功');
callBack(res.tempFilePath) callBack(res.tempFilePath)
} }
callBack() callBack()
}, },
fail: (err) => { fail: (err) => {
console.error(err) console.error(err)
}, },
complete: (e) => { complete: (e) => {
// console.log("downloadFile",e); // console.log("downloadFile",e);
} }
}); });
}) })
const extName = tempFilePath.split('.').pop() || 'jpg' const extName = tempFilePath.split('.').pop() || 'jpg'
const cloudPath = 'user/avatar/'+ userId+'/'+Date.now()+'-avatar.'+extName; const cloudPath = 'user/avatar/'+ userId+'/'+Date.now()+'-avatar.'+extName;
// console.log(tempFilePath); // console.log(tempFilePath);
const result = await uniCloud.uploadFile({ const result = await uniCloud.uploadFile({
filePath: tempFilePath, filePath: tempFilePath,
cloudPath, cloudPath,
fileType:'image' fileType:'image'
}); });
// console.log("上传成功",{result}); // console.log("上传成功",{result});
let userInfo = { let userInfo = {
"nickname":nickName, "nickname":nickName,
"avatar_file":{ "avatar_file":{
name:cloudPath, name:cloudPath,
extname:"jpg", extname:"jpg",
url:result.fileID url:result.fileID
} }
} }
this.doUpdate(userInfo,()=>{ this.doUpdate(userInfo,()=>{
this.$refs.popup.close() this.$refs.popup.close()
}) })
}, },
closeMe(e){ closeMe(e){
uni.showLoading(); uni.showLoading();
this.doUpdate({nickname:"匿名微信用户"},()=>{ this.doUpdate({nickname:"匿名微信用户"},()=>{
uni.hideLoading() uni.hideLoading()
this.$refs.popup.close() this.$refs.popup.close()
}) })
}, },
doUpdate(data,callback){ doUpdate(data,callback){
// console.log('dododo',data); // console.log('dododo',data);
// 使用 clientDB 提交数据 // 使用 clientDB 提交数据
usersTable.where('_id==$env.uid').update(data).then((res) => { usersTable.where('_id==$env.uid').update(data).then((res) => {
callback(res) callback(res)
}).catch((err) => { }).catch((err) => {
uni.showModal({ uni.showModal({
content: err.message || '请求服务失败', content: err.message || '请求服务失败',
showCancel: false showCancel: false
}) })
callback(err) callback(err)
}).finally(() => { }).finally(() => {
this.$emit('next') this.$emit('next')
uni.hideLoading() uni.hideLoading()
}) })
} }
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
view{ view{
display: flex; display: flex;
} }
.box{ .box{
background-color: #FFFFFF; background-color: #FFFFFF;
height:200px; height:200px;
width: 750rpx; width: 750rpx;
flex-direction: column; flex-direction: column;
border-top-left-radius: 15px; border-top-left-radius: 15px;
border-top-right-radius: 15px; border-top-right-radius: 15px;
} }
.headBox{ .headBox{
padding:20rpx; padding:20rpx;
height:80rpx; height:80rpx;
line-height:80rpx; line-height:80rpx;
text-align: left; text-align: left;
font-size:16px; font-size:16px;
color:#333333; color:#333333;
margin-left: 15rpx; margin-left: 15rpx;
} }
.tip{ .tip{
color:#666666; color:#666666;
text-align: left; text-align: left;
justify-content: center; justify-content: center;
margin:10rpx 30rpx; margin:10rpx 30rpx;
font-size:18px; font-size:18px;
} }
.btnBox{ .btnBox{
margin-top:45rpx; margin-top:45rpx;
justify-content: center; justify-content: center;
flex-direction: row; flex-direction: row;
} }
.close,.agree{ .close,.agree{
text-align: center; text-align: center;
width:200rpx; width:200rpx;
height:80upx; height:80upx;
line-height:80upx; line-height:80upx;
border-radius:5px; border-radius:5px;
margin:0 20rpx; margin:0 20rpx;
font-size:14px; font-size:14px;
} }
.close{ .close{
color:#999999; color:#999999;
border-color: #EEEEEE; border-color: #EEEEEE;
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
background-color:#FFFFFF; background-color:#FFFFFF;
} }
.close:active{ .close:active{
color:#989898; color:#989898;
background-color:#E2E2E2; background-color:#E2E2E2;
} }
.agree{ .agree{
color:#FFFFFF; color:#FFFFFF;
} }
/* #ifdef MP */ /* #ifdef MP */
.agree::after { .agree::after {
border: none; border: none;
} }
/* #endif */ /* #endif */
.agree:active{ .agree:active{
background-color:#F5F5F6; background-color:#F5F5F6;
} }
</style> </style>
{ {
"id": "uni-id-pages", "id": "uni-id-pages",
"displayName": "uni-id-pages", "displayName": "uni-id-pages",
"version": "1.0.39", "version": "1.0.41",
"description": "云端一体简单、统一、可扩展的用户中心页面模版", "description": "云端一体简单、统一、可扩展的用户中心页面模版",
"keywords": [ "keywords": [
"用户管理", "用户管理",
......
<!-- 网络链接内容展示页(uni-id-pages中用于展示隐私政策协议内容) --> <!-- 网络链接内容展示页(uni-id-pages中用于展示隐私政策协议内容) -->
<template> <template>
<view> <view>
<web-view v-if="url" :src="url"></web-view> <web-view v-if="url" :src="url"></web-view>
</view> </view>
</template> </template>
<script> <script>
export default { export default {
onLoad({url,title}) { onLoad({url,title}) {
if(url.substring(0, 4) != 'http'){ if(url.substring(0, 4) != 'http'){
uni.showModal({ uni.showModal({
title:"错误", title:"错误",
content: '不是一个有效的网站链接,'+'"'+url+'"', content: '不是一个有效的网站链接,'+'"'+url+'"',
showCancel: false, showCancel: false,
confirmText:"知道了", confirmText:"知道了",
complete: () => { complete: () => {
uni.navigateBack() uni.navigateBack()
} }
}); });
title = "页面路径错误" title = "页面路径错误"
}else{ }else{
// console.log(url,title); // console.log(url,title);
this.url = url; this.url = url;
} }
if(title){ if(title){
uni.setNavigationBarTitle({title}); uni.setNavigationBarTitle({title});
} }
}, },
data() { data() {
return { return {
url:null url:null
}; };
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
</style> </style>
<!-- 短信验证码登录页 --> <!-- 短信验证码登录页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<view class="login-logo"> <view class="login-logo">
<image :src="logo"></image> <image :src="logo"></image>
</view> </view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title">请输入验证码</text> <text class="title">请输入验证码</text>
<text class="tip">先输入图形验证码,再获取短信验证码</text> <text class="tip">先输入图形验证码,再获取短信验证码</text>
<uni-forms> <uni-forms>
<uni-id-pages-sms-form focusCaptchaInput v-model="code" type="login-by-sms" ref="smsCode" :phone="phone"> <uni-id-pages-sms-form focusCaptchaInput v-model="code" type="login-by-sms" ref="smsCode" :phone="phone">
</uni-id-pages-sms-form> </uni-id-pages-sms-form>
<button class="uni-btn send-btn" type="primary" @click="submit">登录</button> <button class="uni-btn send-btn" type="primary" @click="submit">登录</button>
</uni-forms> </uni-forms>
<uni-popup-captcha @confirm="submit" v-model="captcha" scene="login-by-sms" ref="popup"></uni-popup-captcha> <uni-popup-captcha @confirm="submit" v-model="captcha" scene="login-by-sms" ref="popup"></uni-popup-captcha>
</view> </view>
</template> </template>
<script> <script>
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js'; import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
export default { export default {
mixins: [mixin], mixins: [mixin],
data() { data() {
return { return {
"code": "", "code": "",
"phone": "", "phone": "",
"captcha": "", "captcha": "",
"logo": "/static/logo.png" "logo": "/static/logo.png"
} }
}, },
computed: { computed: {
tipText() { tipText() {
return '验证码已通过短信发送至' + this.phone; return '验证码已通过短信发送至' + this.phone;
}, },
}, },
onLoad({ onLoad({
phoneNumber phoneNumber
}) { }) {
this.phone = phoneNumber; this.phone = phoneNumber;
}, },
onShow() { onShow() {
// console.log('onShow'); // console.log('onShow');
// #ifdef H5 // #ifdef H5
document.onkeydown = event => { document.onkeydown = event => {
var e = event || window.event; var e = event || window.event;
if (e && e.keyCode == 13) { //回车键的键值为13 if (e && e.keyCode == 13) { //回车键的键值为13
this.submit() this.submit()
} }
}; };
// #endif // #endif
}, },
methods: { methods: {
submit() { //完成并提交 submit() { //完成并提交
const uniIdCo = uniCloud.importObject("uni-id-co", { const uniIdCo = uniCloud.importObject("uni-id-co", {
errorOptions: { errorOptions: {
type: 'toast' type: 'toast'
} }
}) })
if (this.code.length != 6) { if (this.code.length != 6) {
this.$refs.smsCode.focusSmsCodeInput = true this.$refs.smsCode.focusSmsCodeInput = true
return uni.showToast({ return uni.showToast({
title: '验证码不能为空', title: '验证码不能为空',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
} }
uniIdCo.loginBySms({ uniIdCo.loginBySms({
"mobile": this.phone, "mobile": this.phone,
"code": this.code, "code": this.code,
"captcha": this.captcha "captcha": this.captcha
}).then(e => { }).then(e => {
// console.log(e); // console.log(e);
this.loginSuccess(e) this.loginSuccess(e)
}).catch(e => { }).catch(e => {
if (e.errCode == 'uni-id-captcha-required') { if (e.errCode == 'uni-id-captcha-required') {
this.$refs.popup.open() this.$refs.popup.open()
} else { } else {
console.log(e.errMsg); console.log(e.errMsg);
// console.log(e.errCode); // console.log(e.errCode);
} }
}).finally(e => { }).finally(e => {
this.captcha = '' this.captcha = ''
}) })
} }
} }
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
.tip { .tip {
margin-top: -15px; margin-top: -15px;
margin-bottom: 15px; margin-bottom: 15px;
} }
.popup-captcha { .popup-captcha {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
padding: 20rpx; padding: 20rpx;
background-color: #FFF; background-color: #FFF;
border-radius: 2px; border-radius: 2px;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
} }
.popup-captcha .title { .popup-captcha .title {
font-weight: normal; font-weight: normal;
padding: 0; padding: 0;
padding-bottom: 15px; padding-bottom: 15px;
color: #666; color: #666;
} }
.popup-captcha .close { .popup-captcha .close {
position: absolute; position: absolute;
bottom: -40px; bottom: -40px;
margin-left: -13px; margin-left: -13px;
left: 50%; left: 50%;
} }
.popup-captcha .uni-btn { .popup-captcha .uni-btn {
margin: 0; margin: 0;
} }
</style> </style>
<!-- 免密登录页 --> <!-- 免密登录页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<view class="login-logo"> <view class="login-logo">
<image :src="logo"></image> <image :src="logo"></image>
</view> </view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title">请选择登录方式</text> <text class="title">请选择登录方式</text>
<!-- 快捷登录框 当url带参数时有效 --> <!-- 快捷登录框 当url带参数时有效 -->
<template v-if="['apple','weixin', 'weixinMobile'].includes(type)"> <template v-if="['apple','weixin', 'weixinMobile'].includes(type)">
<text class="tip">将根据第三方账号服务平台的授权范围获取你的信息</text> <text class="tip">将根据第三方账号服务平台的授权范围获取你的信息</text>
<view class="quickLogin"> <view class="quickLogin">
<image v-if="type !== 'weixinMobile'" @click="quickLogin" :src="imgSrc" mode="widthFix" class="quickLoginBtn"></image> <image v-if="type !== 'weixinMobile'" @click="quickLogin" :src="imgSrc" mode="widthFix" class="quickLoginBtn"></image>
<button v-else type="primary" open-type="getPhoneNumber" @getphonenumber="quickLogin" class="uni-btn">微信授权手机号登录</button> <button v-else type="primary" open-type="getPhoneNumber" @getphonenumber="quickLogin" class="uni-btn">微信授权手机号登录</button>
<uni-id-pages-agreements scope="register" ref="agreements"></uni-id-pages-agreements> <uni-id-pages-agreements scope="register" ref="agreements"></uni-id-pages-agreements>
</view> </view>
</template> </template>
<template v-else> <template v-else>
<text class="tip">未注册的账号验证通过后将自动注册</text> <text class="tip">未注册的账号验证通过后将自动注册</text>
<view class="phone-box"> <view class="phone-box">
<view @click="chooseArea" class="area">+86</view> <view @click="chooseArea" class="area">+86</view>
<uni-easyinput :focus="focusPhone" @blur="focusPhone = false" class="input-box" type="number" :inputBorder="false" <uni-easyinput :focus="focusPhone" @blur="focusPhone = false" class="input-box" type="number" :inputBorder="false"
v-model="phone" maxlength="11" placeholder="请输入手机号" /> v-model="phone" maxlength="11" placeholder="请输入手机号" />
</view> </view>
<uni-id-pages-agreements scope="register" ref="agreements"></uni-id-pages-agreements> <uni-id-pages-agreements scope="register" ref="agreements"></uni-id-pages-agreements>
<button class="uni-btn" type="primary" @click="toSmsPage">获取验证码</button> <button class="uni-btn" type="primary" @click="toSmsPage">获取验证码</button>
</template> </template>
<!-- 固定定位的快捷登录按钮 --> <!-- 固定定位的快捷登录按钮 -->
<uni-id-pages-fab-login ref="uniFabLogin"></uni-id-pages-fab-login> <uni-id-pages-fab-login ref="uniFabLogin"></uni-id-pages-fab-login>
</view> </view>
</template> </template>
<script> <script>
let currentWebview; //当前窗口对象 let currentWebview; //当前窗口对象
import config from '@/uni_modules/uni-id-pages/config.js' import config from '@/uni_modules/uni-id-pages/config.js'
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js'; import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
export default { export default {
mixins: [mixin], mixins: [mixin],
data() { data() {
return { return {
type: "", //快捷登录方式 type: "", //快捷登录方式
phone: "", //手机号码 phone: "", //手机号码
focusPhone:false, focusPhone:false,
logo: "/static/logo.png" logo: "/static/logo.png"
} }
}, },
computed: { computed: {
async loginTypes() { //读取配置的登录优先级 async loginTypes() { //读取配置的登录优先级
return config.loginTypes return config.loginTypes
}, },
isPhone() { //手机号码校验正则 isPhone() { //手机号码校验正则
return /^1\d{10}$/.test(this.phone); return /^1\d{10}$/.test(this.phone);
}, },
imgSrc() { //大快捷登录按钮图 imgSrc() { //大快捷登录按钮图
return '/uni_modules/uni-id-pages/static/login/' + this.type + '.png' return '/uni_modules/uni-id-pages/static/login/' + this.type + '.png'
} }
}, },
async onLoad(e) { async onLoad(e) {
// console.log(e); // console.log(e);
//获取通过url传递的参数type设置当前登录方式,如果没传递直接默认以配置的登录 //获取通过url传递的参数type设置当前登录方式,如果没传递直接默认以配置的登录
let type = e.type || config.loginTypes[0] let type = e.type || config.loginTypes[0]
this.type = type this.type = type
if(type != 'univerify'){ if(type != 'univerify'){
this.focusPhone = true this.focusPhone = true
} }
this.$nextTick(() => { this.$nextTick(() => {
//关闭重复显示的登录快捷方式 //关闭重复显示的登录快捷方式
if (['weixin', 'apple'].includes(type)) { if (['weixin', 'apple'].includes(type)) {
this.$refs.uniFabLogin.servicesList = this.$refs.uniFabLogin.servicesList.filter(item =>item.id != type) this.$refs.uniFabLogin.servicesList = this.$refs.uniFabLogin.servicesList.filter(item =>item.id != type)
} }
}) })
uni.$on('uni-id-pages-set-login-type', type => { uni.$on('uni-id-pages-set-login-type', type => {
this.type = type this.type = type
}) })
}, },
onShow() { onShow() {
// #ifdef H5 // #ifdef H5
document.onkeydown = event => { document.onkeydown = event => {
var e = event || window.event; var e = event || window.event;
if (e && e.keyCode == 13) { //回车键的键值为13 if (e && e.keyCode == 13) { //回车键的键值为13
this.toSmsPage() this.toSmsPage()
} }
}; };
// #endif // #endif
}, },
onUnload() { onUnload() {
uni.$off('uni-id-pages-set-login-type') uni.$off('uni-id-pages-set-login-type')
}, },
onReady() { onReady() {
//是否优先启动一键登录。即:页面一加载就启动一键登录 //是否优先启动一键登录。即:页面一加载就启动一键登录
//#ifdef APP-PLUS //#ifdef APP-PLUS
if (this.type == "univerify") { if (this.type == "univerify") {
this.type == this.loginTypes[1] this.type == this.loginTypes[1]
// console.log('开始一键登录'); // console.log('开始一键登录');
setTimeout(() => { setTimeout(() => {
this.$refs.uniFabLogin.login_before('univerify') this.$refs.uniFabLogin.login_before('univerify')
}, 100) }, 100)
} }
//#endif //#endif
}, },
methods: { methods: {
quickLogin(e) { quickLogin(e) {
let options = {} let options = {}
if (e.detail?.code) { if (e.detail?.code) {
options.phoneNumberCode = e.detail.code options.phoneNumberCode = e.detail.code
} }
if (this.type === 'weixinMobile' && !e.detail?.code) return if (this.type === 'weixinMobile' && !e.detail?.code) return
this.$refs.uniFabLogin.login_before(this.type, true, options) this.$refs.uniFabLogin.login_before(this.type, true, options)
}, },
toSmsPage() { toSmsPage() {
// console.log('toSmsPage',this.agree); // console.log('toSmsPage',this.agree);
if (!this.isPhone) { if (!this.isPhone) {
this.focusPhone = true this.focusPhone = true
return uni.showToast({ return uni.showToast({
title: "手机号码格式不正确", title: "手机号码格式不正确",
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
} }
if (this.needAgreements && !this.agree) { if (this.needAgreements && !this.agree) {
return this.$refs.agreements.popup(this.toSmsPage) return this.$refs.agreements.popup(this.toSmsPage)
} }
// 发送验证吗 // 发送验证吗
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-smscode?phoneNumber=' + this.phone url: '/uni_modules/uni-id-pages/pages/login/login-smscode?phoneNumber=' + this.phone
}); });
}, },
//去密码登录页 //去密码登录页
toPwdLogin() { toPwdLogin() {
uni.navigateTo({ uni.navigateTo({
url: '../login/password' url: '../login/password'
}) })
}, },
chooseArea() { chooseArea() {
uni.showToast({ uni.showToast({
title: '暂不支持其他国家', title: '暂不支持其他国家',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
}, },
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
.uni-content{ .uni-content{
height: 350px; height: 350px;
} }
} }
.uni-content, .uni-content,
.quickLogin { .quickLogin {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
/* #endif */ /* #endif */
} }
.phone-box { .phone-box {
position: relative; position: relative;
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
} }
.area { .area {
position: absolute; position: absolute;
left: 10px; left: 10px;
z-index: 9; z-index: 9;
top: 12px; top: 12px;
font-size: 14px; font-size: 14px;
} }
.area::after { .area::after {
content: ""; content: "";
border: 3px solid transparent; border: 3px solid transparent;
border-top-color: #000; border-top-color: #000;
top: 12px; top: 12px;
left: 3px; left: 3px;
position: relative; position: relative;
} }
/* #ifdef MP */ /* #ifdef MP */
// 解决小程序端开启虚拟节点virtualHost引起的 class = input-box丢失的问题 [详情参考](https://uniapp.dcloud.net.cn/matter.html#%E5%90%84%E5%AE%B6%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6%E4%B8%8D%E5%90%8C-%E5%8F%AF%E8%83%BD%E5%AD%98%E5%9C%A8%E7%9A%84%E5%B9%B3%E5%8F%B0%E5%85%BC%E5%AE%B9%E9%97%AE%E9%A2%98) // 解决小程序端开启虚拟节点virtualHost引起的 class = input-box丢失的问题 [详情参考](https://uniapp.dcloud.net.cn/matter.html#%E5%90%84%E5%AE%B6%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6%E4%B8%8D%E5%90%8C-%E5%8F%AF%E8%83%BD%E5%AD%98%E5%9C%A8%E7%9A%84%E5%B9%B3%E5%8F%B0%E5%85%BC%E5%AE%B9%E9%97%AE%E9%A2%98)
.phone-box ::v-deep .uni-easyinput__content, .phone-box ::v-deep .uni-easyinput__content,
/* #endif */ /* #endif */
.input-box { .input-box {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
box-sizing: border-box; box-sizing: border-box;
/* #endif */ /* #endif */
flex: 1; flex: 1;
padding-left: 45px; padding-left: 45px;
margin-bottom: 10px; margin-bottom: 10px;
border-radius: 0; border-radius: 0;
} }
.quickLogin { .quickLogin {
// width: 650rpx; // width: 650rpx;
height: 350px; height: 350px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.quickLoginBtn { .quickLoginBtn {
margin: 20px 0; margin: 20px 0;
width: 450rpx; width: 450rpx;
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
max-width: 230px; max-width: 230px;
/* #endif */ /* #endif */
height: 82rpx; height: 82rpx;
} }
.tip { .tip {
margin-top: -15px; margin-top: -15px;
margin-bottom: 20px; margin-bottom: 20px;
} }
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
.quickLogin{ .quickLogin{
height: auto; height: auto;
} }
} }
</style> </style>
<!-- 创建超级管理员 --> <!-- 创建超级管理员 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<match-media :min-width="690"> <match-media :min-width="690">
<view class="login-logo"> <view class="login-logo">
<image :src="logo"></image> <image :src="logo"></image>
</view> </view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title title-box">创建超级管理员</text> <text class="title title-box">创建超级管理员</text>
</match-media> </match-media>
<uni-forms ref="form" :value="formData" :rules="rules" validate-trigger="submit" err-show-type="toast"> <uni-forms ref="form" :value="formData" :rules="rules" validate-trigger="submit" err-show-type="toast">
<uni-forms-item name="username" required> <uni-forms-item name="username" required>
<uni-easyinput :inputBorder="false" :focus="focusUsername" @blur="focusUsername = false" <uni-easyinput :inputBorder="false" :focus="focusUsername" @blur="focusUsername = false"
class="input-box" placeholder="请输入用户名" v-model="formData.username" trim="both" /> class="input-box" placeholder="请输入用户名" v-model="formData.username" trim="both" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="nickname"> <uni-forms-item name="nickname">
<uni-easyinput :inputBorder="false" :focus="focusNickname" @blur="focusNickname = false" class="input-box" placeholder="请输入用户昵称" v-model="formData.nickname" <uni-easyinput :inputBorder="false" :focus="focusNickname" @blur="focusNickname = false" class="input-box" placeholder="请输入用户昵称" v-model="formData.nickname"
trim="both" /> trim="both" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password" v-model="formData.password" required> <uni-forms-item name="password" v-model="formData.password" required>
<uni-easyinput :inputBorder="false" :focus="focusPassword" @blur="focusPassword = false" <uni-easyinput :inputBorder="false" :focus="focusPassword" @blur="focusPassword = false"
class="input-box" maxlength="20" :placeholder="'请输入' + (config.passwordStrength == 'weak'?'6':'8') + '-16位密码'" type="password" class="input-box" maxlength="20" :placeholder="'请输入' + (config.passwordStrength == 'weak'?'6':'8') + '-16位密码'" type="password"
v-model="formData.password" trim="both" /> v-model="formData.password" trim="both" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password2" v-model="formData.password2" required> <uni-forms-item name="password2" v-model="formData.password2" required>
<uni-easyinput :inputBorder="false" :focus="focusPassword2" @blur="focusPassword2 =false" <uni-easyinput :inputBorder="false" :focus="focusPassword2" @blur="focusPassword2 =false"
class="input-box" placeholder="再次输入密码" maxlength="20" type="password" v-model="formData.password2" class="input-box" placeholder="再次输入密码" maxlength="20" type="password" v-model="formData.password2"
trim="both" /> trim="both" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item> <uni-forms-item>
<uni-captcha ref="captcha" scene="register" v-model="formData.captcha" /> <uni-captcha ref="captcha" scene="register" v-model="formData.captcha" />
</uni-forms-item> </uni-forms-item>
<uni-id-pages-agreements scope="register" ref="agreements" ></uni-id-pages-agreements> <uni-id-pages-agreements scope="register" ref="agreements" ></uni-id-pages-agreements>
<button class="uni-btn" type="primary" @click="submit">注册</button> <button class="uni-btn" type="primary" @click="submit">注册</button>
<button @click="navigateBack" class="register-back">返回</button> <button @click="navigateBack" class="register-back">返回</button>
<match-media :min-width="690"> <match-media :min-width="690">
<view class="link-box"> <view class="link-box">
<text class="link" @click="toLogin">已有账号?点此登录</text> <text class="link" @click="toLogin">已有账号?点此登录</text>
</view> </view>
</match-media> </match-media>
</uni-forms> </uni-forms>
</view> </view>
</template> </template>
<script> <script>
import rules from './validator.js'; import rules from './validator.js';
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js'; import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
import config from '@/uni_modules/uni-id-pages/config.js' import config from '@/uni_modules/uni-id-pages/config.js'
const uniIdCo = uniCloud.importObject("uni-id-co", {customUI: true}) const uniIdCo = uniCloud.importObject("uni-id-co", {customUI: true})
export default { export default {
mixins: [mixin], mixins: [mixin],
data() { data() {
return { return {
formData: { formData: {
username: "", username: "",
nickname: "", nickname: "",
password: "", password: "",
password2: "", password2: "",
captcha: "" captcha: ""
}, },
rules, rules,
focusUsername:false, focusUsername:false,
focusNickname:false, focusNickname:false,
focusPassword:false, focusPassword:false,
focusPassword2:false, focusPassword2:false,
logo: "/static/logo.png" logo: "/static/logo.png"
} }
}, },
onReady() { onReady() {
this.$refs.form.setRules(this.rules) this.$refs.form.setRules(this.rules)
}, },
onShow() { onShow() {
// #ifdef H5 // #ifdef H5
document.onkeydown = event => { document.onkeydown = event => {
var e = event || window.event; var e = event || window.event;
if (e && e.keyCode == 13) { //回车键的键值为13 if (e && e.keyCode == 13) { //回车键的键值为13
this.submit() this.submit()
} }
}; };
// #endif // #endif
}, },
methods: { methods: {
/** /**
* 触发表单提交 * 触发表单提交
*/ */
submit() { submit() {
this.$refs.form.validate().then((res) => { this.$refs.form.validate().then((res) => {
if(this.formData.captcha.length != 4){ if(this.formData.captcha.length != 4){
this.$refs.captcha.focusCaptchaInput = true this.$refs.captcha.focusCaptchaInput = true
return uni.showToast({ return uni.showToast({
title: '请输入验证码', title: '请输入验证码',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
} }
if (this.needAgreements && !this.agree) { if (this.needAgreements && !this.agree) {
return this.$refs.agreements.popup(()=>{ return this.$refs.agreements.popup(()=>{
this.submitForm(res) this.submitForm(res)
}) })
} }
this.submitForm(res) this.submitForm(res)
}).catch((errors) => { }).catch((errors) => {
let key = errors[0].key let key = errors[0].key
key = key.replace(key[0], key[0].toUpperCase()) key = key.replace(key[0], key[0].toUpperCase())
// console.log(key); // console.log(key);
this['focus'+key] = true this['focus'+key] = true
}) })
}, },
submitForm(params) { submitForm(params) {
uniIdCo.registerAdmin(this.formData).then(e => { uniIdCo.registerAdmin(this.formData).then(e => {
// console.log(e); // console.log(e);
uni.navigateBack() uni.navigateBack()
}) })
.catch(e => { .catch(e => {
// console.log(e); // console.log(e);
// console.log(e.message); // console.log(e.message);
//更好的体验:登录错误,直接刷新验证码 //更好的体验:登录错误,直接刷新验证码
this.$refs.captcha.getImageCaptcha() this.$refs.captcha.getImageCaptcha()
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: e.errMsg || `创建失败: ${e.errCode}`, content: e.errMsg || `创建失败: ${e.errCode}`,
showCancel: false showCancel: false
}) })
}) })
}, },
navigateBack() { navigateBack() {
uni.navigateBack() uni.navigateBack()
}, },
toLogin() { toLogin() {
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withpwd' url: '/uni_modules/uni-id-pages/pages/login/login-withpwd'
}) })
}, },
registerByEmail() { registerByEmail() {
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/register/register-by-email' url: '/uni_modules/uni-id-pages/pages/register/register-by-email'
}) })
} }
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
@media screen and (max-width: 690px) { @media screen and (max-width: 690px) {
.uni-content{ .uni-content{
margin-top: 15px; margin-top: 15px;
height: 100%; height: 100%;
background-color: #fff; background-color: #fff;
} }
} }
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
.uni-content{ .uni-content{
padding: 30px 40px 60px; padding: 30px 40px 60px;
max-height: 520px; max-height: 520px;
} }
.link-box { .link-box {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
margin-top: 10px; margin-top: 10px;
} }
.link { .link {
font-size: 12px; font-size: 12px;
} }
} }
.uni-content ::v-deep .uni-forms-item__label { .uni-content ::v-deep .uni-forms-item__label {
position: absolute; position: absolute;
left: -15px; left: -15px;
} }
button { button {
margin-top: 15px; margin-top: 15px;
} }
</style> </style>
<!-- 邮箱验证码注册 --> <!-- 邮箱验证码注册 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<match-media :min-width="690"> <match-media :min-width="690">
<view class="login-logo"> <view class="login-logo">
<image :src="logo"></image> <image :src="logo"></image>
</view> </view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title title-box">邮箱验证码注册</text> <text class="title title-box">邮箱验证码注册</text>
</match-media> </match-media>
<uni-forms ref="form" :value="formData" :rules="rules" validate-trigger="submit" err-show-type="toast"> <uni-forms ref="form" :value="formData" :rules="rules" validate-trigger="submit" err-show-type="toast">
<uni-forms-item name="email" required> <uni-forms-item name="email" required>
<uni-easyinput :inputBorder="false" :focus="focusEmail" @blur="focusEmail = false" <uni-easyinput :inputBorder="false" :focus="focusEmail" @blur="focusEmail = false"
class="input-box" placeholder="请输入邮箱" v-model="formData.email" trim="both" /> class="input-box" placeholder="请输入邮箱" v-model="formData.email" trim="both" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="nickname"> <uni-forms-item name="nickname">
<uni-easyinput :inputBorder="false" :focus="focusNickname" @blur="focusNickname = false" class="input-box" placeholder="请输入用户昵称" <uni-easyinput :inputBorder="false" :focus="focusNickname" @blur="focusNickname = false" class="input-box" placeholder="请输入用户昵称"
v-model="formData.nickname" trim="both" /> v-model="formData.nickname" trim="both" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password" v-model="formData.password" required> <uni-forms-item name="password" v-model="formData.password" required>
<uni-easyinput :inputBorder="false" :focus="focusPassword" @blur="focusPassword = false" <uni-easyinput :inputBorder="false" :focus="focusPassword" @blur="focusPassword = false"
class="input-box" maxlength="20" :placeholder="'请输入' + (config.passwordStrength == 'weak'?'6':'8') + '-16位密码'" type="password" class="input-box" maxlength="20" :placeholder="'请输入' + (config.passwordStrength == 'weak'?'6':'8') + '-16位密码'" type="password"
v-model="formData.password" trim="both" /> v-model="formData.password" trim="both" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password2" v-model="formData.password2" required> <uni-forms-item name="password2" v-model="formData.password2" required>
<uni-easyinput :inputBorder="false" :focus="focusPassword2" @blur="focusPassword2 =false" <uni-easyinput :inputBorder="false" :focus="focusPassword2" @blur="focusPassword2 =false"
class="input-box" placeholder="再次输入密码" maxlength="20" type="password" v-model="formData.password2" class="input-box" placeholder="再次输入密码" maxlength="20" type="password" v-model="formData.password2"
trim="both" /> trim="both" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="code" > <uni-forms-item name="code" >
<uni-id-pages-email-form ref="shortCode" :email="formData.email" type="register" v-model="formData.code"> <uni-id-pages-email-form ref="shortCode" :email="formData.email" type="register" v-model="formData.code">
</uni-id-pages-email-form> </uni-id-pages-email-form>
</uni-forms-item> </uni-forms-item>
<uni-id-pages-agreements scope="register" ref="agreements" ></uni-id-pages-agreements> <uni-id-pages-agreements scope="register" ref="agreements" ></uni-id-pages-agreements>
<button class="uni-btn" type="primary" @click="submit">注册</button> <button class="uni-btn" type="primary" @click="submit">注册</button>
<button @click="navigateBack" class="register-back">返回</button> <button @click="navigateBack" class="register-back">返回</button>
<match-media :min-width="690"> <match-media :min-width="690">
<view class="link-box"> <view class="link-box">
<text class="link" @click="registerByUserName">用户名密码注册</text> <text class="link" @click="registerByUserName">用户名密码注册</text>
<text class="link" @click="toLogin">已有账号?点此登录</text> <text class="link" @click="toLogin">已有账号?点此登录</text>
</view> </view>
</match-media> </match-media>
</uni-forms> </uni-forms>
</view> </view>
</template> </template>
<script> <script>
import rules from './validator.js'; import rules from './validator.js';
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js'; import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
import config from '@/uni_modules/uni-id-pages/config.js' import config from '@/uni_modules/uni-id-pages/config.js'
import passwordMod from '@/uni_modules/uni-id-pages/common/password.js' import passwordMod from '@/uni_modules/uni-id-pages/common/password.js'
const uniIdCo = uniCloud.importObject("uni-id-co") const uniIdCo = uniCloud.importObject("uni-id-co")
export default { export default {
mixins: [mixin], mixins: [mixin],
data() { data() {
return { return {
formData: { formData: {
email: "", email: "",
nickname: "", nickname: "",
password: "", password: "",
password2: "", password2: "",
code: "" code: ""
}, },
rules: { rules: {
email: { email: {
rules: [{ rules: [{
required: true, required: true,
errorMessage: '请输入邮箱', errorMessage: '请输入邮箱',
},{ },{
format:'email', format:'email',
errorMessage: '邮箱格式不正确', errorMessage: '邮箱格式不正确',
} }
] ]
}, },
nickname: { nickname: {
rules: [{ rules: [{
minLength: 3, minLength: 3,
maxLength: 32, maxLength: 32,
errorMessage: '昵称长度在 {minLength} 到 {maxLength} 个字符', errorMessage: '昵称长度在 {minLength} 到 {maxLength} 个字符',
}, },
{ {
validateFunction: function(rule, value, data, callback) { validateFunction: function(rule, value, data, callback) {
// console.log(value); // console.log(value);
if (/^1\d{10}$/.test(value) || /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(value)) { if (/^1\d{10}$/.test(value) || /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(value)) {
callback('昵称不能是:手机号或邮箱') callback('昵称不能是:手机号或邮箱')
}; };
if (/^\d+$/.test(value)) { if (/^\d+$/.test(value)) {
callback('昵称不能为纯数字') callback('昵称不能为纯数字')
}; };
if(/[\u4E00-\u9FA5\uF900-\uFA2D]{1,}/.test(value)){ if(/[\u4E00-\u9FA5\uF900-\uFA2D]{1,}/.test(value)){
callback('昵称不能包含中文') callback('昵称不能包含中文')
} }
return true return true
} }
} }
], ],
label: "昵称" label: "昵称"
}, },
...passwordMod.getPwdRules(), ...passwordMod.getPwdRules(),
code: { code: {
rules: [{ rules: [{
required: true, required: true,
errorMessage: '请输入邮箱验证码', errorMessage: '请输入邮箱验证码',
}, },
{ {
pattern: /^.{6}$/, pattern: /^.{6}$/,
errorMessage: '邮箱验证码不正确', errorMessage: '邮箱验证码不正确',
} }
] ]
} }
}, },
focusEmail:false, focusEmail:false,
focusNickname:false, focusNickname:false,
focusPassword:false, focusPassword:false,
focusPassword2:false, focusPassword2:false,
logo: "/static/logo.png" logo: "/static/logo.png"
} }
}, },
onReady() { onReady() {
this.$refs.form.setRules(this.rules) this.$refs.form.setRules(this.rules)
}, },
onShow() { onShow() {
// #ifdef H5 // #ifdef H5
document.onkeydown = event => { document.onkeydown = event => {
var e = event || window.event; var e = event || window.event;
if (e && e.keyCode == 13) { //回车键的键值为13 if (e && e.keyCode == 13) { //回车键的键值为13
this.submit() this.submit()
} }
}; };
// #endif // #endif
}, },
methods: { methods: {
/** /**
* 触发表单提交 * 触发表单提交
*/ */
submit() { submit() {
this.$refs.form.validate().then((res) => { this.$refs.form.validate().then((res) => {
if (this.needAgreements && !this.agree) { if (this.needAgreements && !this.agree) {
return this.$refs.agreements.popup(()=>{ return this.$refs.agreements.popup(()=>{
this.submitForm(res) this.submitForm(res)
}) })
} }
this.submitForm(res) this.submitForm(res)
}).catch((errors) => { }).catch((errors) => {
let key = errors[0].key let key = errors[0].key
key = key.replace(key[0], key[0].toUpperCase()) key = key.replace(key[0], key[0].toUpperCase())
// console.log(key); // console.log(key);
this['focus'+key] = true this['focus'+key] = true
}) })
}, },
submitForm(params) { submitForm(params) {
uniIdCo.registerUserByEmail(this.formData).then(e => { uniIdCo.registerUserByEmail(this.formData).then(e => {
// console.log(e); // console.log(e);
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withpwd', url: '/uni_modules/uni-id-pages/pages/login/login-withpwd',
complete: (e) => { complete: (e) => {
// console.log(e); // console.log(e);
} }
}) })
}) })
.catch(e => { .catch(e => {
// console.log(e); // console.log(e);
console.log(e.message); console.log(e.message);
}) })
}, },
navigateBack() { navigateBack() {
uni.navigateBack() uni.navigateBack()
}, },
toLogin() { toLogin() {
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withpwd' url: '/uni_modules/uni-id-pages/pages/login/login-withpwd'
}) })
}, },
registerByUserName() { registerByUserName() {
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/register/register' url: '/uni_modules/uni-id-pages/pages/register/register'
}) })
} }
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
@media screen and (max-width: 690px) { @media screen and (max-width: 690px) {
.uni-content{ .uni-content{
margin-top: 15px; margin-top: 15px;
} }
} }
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
.uni-content{ .uni-content{
padding: 30px 40px; padding: 30px 40px;
max-height: 650px; max-height: 650px;
} }
.link-box { .link-box {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
margin-top: 10px; margin-top: 10px;
} }
.link { .link {
font-size: 12px; font-size: 12px;
} }
} }
.uni-content ::v-deep .uni-forms-item__label { .uni-content ::v-deep .uni-forms-item__label {
position: absolute; position: absolute;
left: -15px; left: -15px;
} }
button { button {
margin-top: 15px; margin-top: 15px;
} }
</style> </style>
<!-- 找回密码页 --> <!-- 找回密码页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<match-media :min-width="690"> <match-media :min-width="690">
<view class="login-logo"> <view class="login-logo">
<image :src="logo"></image> <image :src="logo"></image>
</view> </view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title title-box">通过邮箱验证码找回密码</text> <text class="title title-box">通过邮箱验证码找回密码</text>
</match-media> </match-media>
<uni-forms ref="form" :value="formData" err-show-type="toast"> <uni-forms ref="form" :value="formData" err-show-type="toast">
<uni-forms-item name="email"> <uni-forms-item name="email">
<uni-easyinput :focus="focusEmail" @blur="focusEmail = false" class="input-box" :disabled="lock" :inputBorder="false" <uni-easyinput :focus="focusEmail" @blur="focusEmail = false" class="input-box" :disabled="lock" :inputBorder="false"
v-model="formData.email" placeholder="请输入邮箱"> v-model="formData.email" placeholder="请输入邮箱">
</uni-easyinput> </uni-easyinput>
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="code"> <uni-forms-item name="code">
<uni-id-pages-email-form ref="shortCode" :email="formData.email" type="reset-pwd-by-email" v-model="formData.code"> <uni-id-pages-email-form ref="shortCode" :email="formData.email" type="reset-pwd-by-email" v-model="formData.code">
</uni-id-pages-email-form> </uni-id-pages-email-form>
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password"> <uni-forms-item name="password">
<uni-easyinput :focus="focusPassword" @blur="focusPassword = false" class="input-box" type="password" :inputBorder="false" v-model="formData.password" <uni-easyinput :focus="focusPassword" @blur="focusPassword = false" class="input-box" type="password" :inputBorder="false" v-model="formData.password"
placeholder="请输入新密码"></uni-easyinput> placeholder="请输入新密码"></uni-easyinput>
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password2"> <uni-forms-item name="password2">
<uni-easyinput :focus="focusPassword2" @blur="focusPassword2 = false" class="input-box" type="password" :inputBorder="false" v-model="formData.password2" <uni-easyinput :focus="focusPassword2" @blur="focusPassword2 = false" class="input-box" type="password" :inputBorder="false" v-model="formData.password2"
placeholder="请再次输入新密码"></uni-easyinput> placeholder="请再次输入新密码"></uni-easyinput>
</uni-forms-item> </uni-forms-item>
<button class="uni-btn send-btn-box" type="primary" @click="submit">提交</button> <button class="uni-btn send-btn-box" type="primary" @click="submit">提交</button>
<match-media :min-width="690"> <match-media :min-width="690">
<view class="link-box"> <view class="link-box">
<text class="link" @click="retrieveByPhone">通过手机验证码找回密码</text> <text class="link" @click="retrieveByPhone">通过手机验证码找回密码</text>
<view></view> <view></view>
<text class="link" @click="backLogin">返回登录</text> <text class="link" @click="backLogin">返回登录</text>
</view> </view>
</match-media> </match-media>
</uni-forms> </uni-forms>
<uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="reset-pwd-by-sms" ref="popup"></uni-popup-captcha> <uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="reset-pwd-by-sms" ref="popup"></uni-popup-captcha>
</view> </view>
</template> </template>
<script> <script>
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js'; import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
import passwordMod from '@/uni_modules/uni-id-pages/common/password.js' import passwordMod from '@/uni_modules/uni-id-pages/common/password.js'
const uniIdCo = uniCloud.importObject("uni-id-co",{ const uniIdCo = uniCloud.importObject("uni-id-co",{
errorOptions:{ errorOptions:{
type:'toast' type:'toast'
} }
}) })
export default { export default {
mixins: [mixin], mixins: [mixin],
data() { data() {
return { return {
lock: false, lock: false,
focusEmail:true, focusEmail:true,
focusPassword:false, focusPassword:false,
focusPassword2:false, focusPassword2:false,
formData: { formData: {
"email": "", "email": "",
"code": "", "code": "",
'password': '', 'password': '',
'password2': '', 'password2': '',
"captcha": "" "captcha": ""
}, },
rules: { rules: {
email: { email: {
rules: [{ rules: [{
required: true, required: true,
errorMessage: '请输入邮箱', errorMessage: '请输入邮箱',
}, },
{ {
format:'email', format:'email',
errorMessage: '邮箱格式不正确', errorMessage: '邮箱格式不正确',
} }
] ]
}, },
code: { code: {
rules: [{ rules: [{
required: true, required: true,
errorMessage: '请输入邮箱验证码', errorMessage: '请输入邮箱验证码',
}, },
{ {
pattern: /^.{6}$/, pattern: /^.{6}$/,
errorMessage: '请输入6位验证码', errorMessage: '请输入6位验证码',
} }
] ]
}, },
...passwordMod.getPwdRules() ...passwordMod.getPwdRules()
}, },
logo: "/static/logo.png" logo: "/static/logo.png"
} }
}, },
computed: { computed: {
isEmail() { isEmail() {
let reg_email = /@/; let reg_email = /@/;
let isEmail = reg_email.test(this.formData.email); let isEmail = reg_email.test(this.formData.email);
return isEmail; return isEmail;
}, },
isPwd() { isPwd() {
let reg_pwd = /^.{6,20}$/; let reg_pwd = /^.{6,20}$/;
let isPwd = reg_pwd.test(this.formData.password); let isPwd = reg_pwd.test(this.formData.password);
return isPwd; return isPwd;
}, },
isCode() { isCode() {
let reg_code = /^\d{6}$/; let reg_code = /^\d{6}$/;
let isCode = reg_code.test(this.formData.code); let isCode = reg_code.test(this.formData.code);
return isCode; return isCode;
} }
}, },
onLoad(event) { onLoad(event) {
if (event && event.emailNumber) { if (event && event.emailNumber) {
this.formData.email = event.emailNumber; this.formData.email = event.emailNumber;
if(event.lock){ if(event.lock){
this.lock = event.lock //如果是已经登录的账号,点击找回密码就锁定指定的账号绑定的邮箱码 this.lock = event.lock //如果是已经登录的账号,点击找回密码就锁定指定的账号绑定的邮箱码
this.focusEmail = true this.focusEmail = true
} }
} }
}, },
onReady() { onReady() {
if (this.formData.email) { if (this.formData.email) {
this.$refs.shortCode.start(); this.$refs.shortCode.start();
} }
this.$refs.form.setRules(this.rules) this.$refs.form.setRules(this.rules)
}, },
onShow() { onShow() {
// #ifdef H5 // #ifdef H5
document.onkeydown = event => { document.onkeydown = event => {
var e = event || window.event; var e = event || window.event;
if (e && e.keyCode == 13) { //回车键的键值为13 if (e && e.keyCode == 13) { //回车键的键值为13
this.submit() this.submit()
} }
}; };
// #endif // #endif
}, },
methods: { methods: {
/** /**
* 完成并提交 * 完成并提交
*/ */
submit() { submit() {
// console.log("formData", this.formData); // console.log("formData", this.formData);
// console.log('rules', this.rules); // console.log('rules', this.rules);
this.$refs.form.validate() this.$refs.form.validate()
.then(res => { .then(res => {
let { let {
email, email,
password: password, password: password,
captcha, captcha,
code code
} = this.formData } = this.formData
uniIdCo.resetPwdByEmail({ uniIdCo.resetPwdByEmail({
email, email,
code, code,
password, password,
captcha captcha
}).then(e => { }).then(e => {
// console.log(e); // console.log(e);
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withpwd', url: '/uni_modules/uni-id-pages/pages/login/login-withpwd',
complete: (e) => { complete: (e) => {
// console.log(e); // console.log(e);
} }
}) })
}) })
.catch(e => { .catch(e => {
if (e.errCode == 'uni-id-captcha-required') { if (e.errCode == 'uni-id-captcha-required') {
this.$refs.popup.open() this.$refs.popup.open()
} }
}).finally(e => { }).finally(e => {
this.formData.captcha = "" this.formData.captcha = ""
}) })
}).catch(errors=>{ }).catch(errors=>{
let key = errors[0].key let key = errors[0].key
if(key == 'code'){ if(key == 'code'){
// console.log(this.$refs.shortCode); // console.log(this.$refs.shortCode);
return this.$refs.shortCode.focusSmsCodeInput = true return this.$refs.shortCode.focusSmsCodeInput = true
} }
key = key.replace(key[0], key[0].toUpperCase()) key = key.replace(key[0], key[0].toUpperCase())
// console.log(key,'focus'+key); // console.log(key,'focus'+key);
this['focus'+key] = true this['focus'+key] = true
}) })
}, },
retrieveByPhone() { retrieveByPhone() {
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/retrieve/retrieve' url: '/uni_modules/uni-id-pages/pages/retrieve/retrieve'
}) })
}, },
backLogin () { backLogin () {
uni.redirectTo({ uni.redirectTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withpwd' url: '/uni_modules/uni-id-pages/pages/login/login-withpwd'
}) })
} }
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
@media screen and (max-width: 690px) { @media screen and (max-width: 690px) {
.uni-content{ .uni-content{
margin-top: 15px; margin-top: 15px;
} }
} }
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
.uni-content{ .uni-content{
padding: 30px 40px 40px; padding: 30px 40px 40px;
max-height: 650px; max-height: 650px;
} }
.link-box { .link-box {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
margin-top: 10px; margin-top: 10px;
} }
.link { .link {
font-size: 12px; font-size: 12px;
} }
} }
</style> </style>
<!-- 找回密码页 --> <!-- 找回密码页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<match-media :min-width="690"> <match-media :min-width="690">
<view class="login-logo"> <view class="login-logo">
<image :src="logo"></image> <image :src="logo"></image>
</view> </view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title title-box">通过手机验证码找回密码</text> <text class="title title-box">通过手机验证码找回密码</text>
</match-media> </match-media>
<uni-forms ref="form" :value="formData" err-show-type="toast"> <uni-forms ref="form" :value="formData" err-show-type="toast">
<uni-forms-item name="phone"> <uni-forms-item name="phone">
<uni-easyinput :focus="focusPhone" @blur="focusPhone = false" class="input-box" :disabled="lock" type="number" :inputBorder="false" <uni-easyinput :focus="focusPhone" @blur="focusPhone = false" class="input-box" :disabled="lock" type="number" :inputBorder="false"
v-model="formData.phone" maxlength="11" placeholder="请输入手机号"> v-model="formData.phone" maxlength="11" placeholder="请输入手机号">
</uni-easyinput> </uni-easyinput>
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="code"> <uni-forms-item name="code">
<uni-id-pages-sms-form ref="shortCode" :phone="formData.phone" type="reset-pwd-by-sms" v-model="formData.code"> <uni-id-pages-sms-form ref="shortCode" :phone="formData.phone" type="reset-pwd-by-sms" v-model="formData.code">
</uni-id-pages-sms-form> </uni-id-pages-sms-form>
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password"> <uni-forms-item name="password">
<uni-easyinput :focus="focusPassword" @blur="focusPassword = false" class="input-box" type="password" :inputBorder="false" v-model="formData.password" <uni-easyinput :focus="focusPassword" @blur="focusPassword = false" class="input-box" type="password" :inputBorder="false" v-model="formData.password"
placeholder="请输入新密码"></uni-easyinput> placeholder="请输入新密码"></uni-easyinput>
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password2"> <uni-forms-item name="password2">
<uni-easyinput :focus="focusPassword2" @blur="focusPassword2 = false" class="input-box" type="password" :inputBorder="false" v-model="formData.password2" <uni-easyinput :focus="focusPassword2" @blur="focusPassword2 = false" class="input-box" type="password" :inputBorder="false" v-model="formData.password2"
placeholder="请再次输入新密码"></uni-easyinput> placeholder="请再次输入新密码"></uni-easyinput>
</uni-forms-item> </uni-forms-item>
<button class="uni-btn send-btn-box" type="primary" @click="submit">提交</button> <button class="uni-btn send-btn-box" type="primary" @click="submit">提交</button>
<match-media :min-width="690"> <match-media :min-width="690">
<view class="link-box"> <view class="link-box">
<text class="link" @click="retrieveByEmail">通过邮箱验证码找回密码</text> <text class="link" @click="retrieveByEmail">通过邮箱验证码找回密码</text>
<view></view> <view></view>
<text class="link" @click="backLogin">返回登录</text> <text class="link" @click="backLogin">返回登录</text>
</view> </view>
</match-media> </match-media>
</uni-forms> </uni-forms>
<uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="reset-pwd-by-sms" ref="popup"></uni-popup-captcha> <uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="reset-pwd-by-sms" ref="popup"></uni-popup-captcha>
</view> </view>
</template> </template>
<script> <script>
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js'; import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
const uniIdCo = uniCloud.importObject("uni-id-co",{ const uniIdCo = uniCloud.importObject("uni-id-co",{
errorOptions:{ errorOptions:{
type:'toast' type:'toast'
} }
}) })
export default { export default {
mixins: [mixin], mixins: [mixin],
data() { data() {
return { return {
lock: false, lock: false,
focusPhone:true, focusPhone:true,
focusPassword:false, focusPassword:false,
focusPassword2:false, focusPassword2:false,
formData: { formData: {
"phone": "", "phone": "",
"code": "", "code": "",
'password': '', 'password': '',
'password2': '', 'password2': '',
"captcha": "" "captcha": ""
}, },
rules: { rules: {
phone: { phone: {
rules: [{ rules: [{
required: true, required: true,
errorMessage: '请输入手机号', errorMessage: '请输入手机号',
}, },
{ {
pattern: /^1\d{10}$/, pattern: /^1\d{10}$/,
errorMessage: '手机号码格式不正确', errorMessage: '手机号码格式不正确',
} }
] ]
}, },
code: { code: {
rules: [{ rules: [{
required: true, required: true,
errorMessage: '请输入短信验证码', errorMessage: '请输入短信验证码',
}, },
{ {
pattern: /^.{6}$/, pattern: /^.{6}$/,
errorMessage: '请输入6位验证码', errorMessage: '请输入6位验证码',
} }
] ]
}, },
password: { password: {
rules: [{ rules: [{
required: true, required: true,
errorMessage: '请输入新密码', errorMessage: '请输入新密码',
}, },
{ {
pattern: /^.{6,20}$/, pattern: /^.{6,20}$/,
errorMessage: '密码为6 - 20位', errorMessage: '密码为6 - 20位',
} }
] ]
}, },
password2: { password2: {
rules: [{ rules: [{
required: true, required: true,
errorMessage: '请确认密码', errorMessage: '请确认密码',
}, },
{ {
pattern: /^.{6,20}$/, pattern: /^.{6,20}$/,
errorMessage: '密码为6 - 20位', errorMessage: '密码为6 - 20位',
}, },
{ {
validateFunction: function(rule, value, data, callback) { validateFunction: function(rule, value, data, callback) {
// console.log(value); // console.log(value);
if (value != data.password) { if (value != data.password) {
callback('两次输入密码不一致') callback('两次输入密码不一致')
}; };
return true return true
} }
} }
] ]
} }
}, },
logo: "/static/logo.png" logo: "/static/logo.png"
} }
}, },
computed: { computed: {
isPhone() { isPhone() {
let reg_phone = /^1\d{10}$/; let reg_phone = /^1\d{10}$/;
let isPhone = reg_phone.test(this.formData.phone); let isPhone = reg_phone.test(this.formData.phone);
return isPhone; return isPhone;
}, },
isPwd() { isPwd() {
let reg_pwd = /^.{6,20}$/; let reg_pwd = /^.{6,20}$/;
let isPwd = reg_pwd.test(this.formData.password); let isPwd = reg_pwd.test(this.formData.password);
return isPwd; return isPwd;
}, },
isCode() { isCode() {
let reg_code = /^\d{6}$/; let reg_code = /^\d{6}$/;
let isCode = reg_code.test(this.formData.code); let isCode = reg_code.test(this.formData.code);
return isCode; return isCode;
} }
}, },
onLoad(event) { onLoad(event) {
if (event && event.phoneNumber) { if (event && event.phoneNumber) {
this.formData.phone = event.phoneNumber; this.formData.phone = event.phoneNumber;
if(event.lock){ if(event.lock){
this.lock = event.lock //如果是已经登录的账号,点击找回密码就锁定指定的账号绑定的手机号码 this.lock = event.lock //如果是已经登录的账号,点击找回密码就锁定指定的账号绑定的手机号码
this.focusPhone = true this.focusPhone = true
} }
} }
}, },
onReady() { onReady() {
if (this.formData.phone) { if (this.formData.phone) {
this.$refs.shortCode.start(); this.$refs.shortCode.start();
} }
this.$refs.form.setRules(this.rules) this.$refs.form.setRules(this.rules)
}, },
onShow() { onShow() {
// #ifdef H5 // #ifdef H5
document.onkeydown = event => { document.onkeydown = event => {
var e = event || window.event; var e = event || window.event;
if (e && e.keyCode == 13) { //回车键的键值为13 if (e && e.keyCode == 13) { //回车键的键值为13
this.submit() this.submit()
} }
}; };
// #endif // #endif
}, },
methods: { methods: {
/** /**
* 完成并提交 * 完成并提交
*/ */
submit() { submit() {
// console.log("formData", this.formData); // console.log("formData", this.formData);
// console.log('rules', this.rules); // console.log('rules', this.rules);
this.$refs.form.validate() this.$refs.form.validate()
.then(res => { .then(res => {
let { let {
"phone": mobile, "phone": mobile,
"password": password, "password": password,
captcha, captcha,
code code
} = this.formData } = this.formData
uniIdCo.resetPwdBySms({ uniIdCo.resetPwdBySms({
mobile, mobile,
code, code,
password, password,
captcha captcha
}).then(e => { }).then(e => {
// console.log(e); // console.log(e);
uni.navigateBack() uni.navigateBack()
}) })
.catch(e => { .catch(e => {
if (e.errCode == 'uni-id-captcha-required') { if (e.errCode == 'uni-id-captcha-required') {
this.$refs.popup.open() this.$refs.popup.open()
} }
}).finally(e => { }).finally(e => {
this.formData.captcha = "" this.formData.captcha = ""
}) })
}).catch(errors=>{ }).catch(errors=>{
let key = errors[0].key let key = errors[0].key
if(key == 'code'){ if(key == 'code'){
// console.log(this.$refs.shortCode); // console.log(this.$refs.shortCode);
return this.$refs.shortCode.focusSmsCodeInput = true return this.$refs.shortCode.focusSmsCodeInput = true
} }
key = key.replace(key[0], key[0].toUpperCase()) key = key.replace(key[0], key[0].toUpperCase())
// console.log(key,'focus'+key); // console.log(key,'focus'+key);
this['focus'+key] = true this['focus'+key] = true
}) })
}, },
retrieveByEmail() { retrieveByEmail() {
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email' url: '/uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email'
}) })
}, },
backLogin () { backLogin () {
uni.redirectTo({ uni.redirectTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withpwd' url: '/uni_modules/uni-id-pages/pages/login/login-withpwd'
}) })
} }
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
@media screen and (max-width: 690px) { @media screen and (max-width: 690px) {
.uni-content{ .uni-content{
margin-top: 15px; margin-top: 15px;
} }
} }
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
.uni-content{ .uni-content{
padding: 30px 40px 40px; padding: 30px 40px 40px;
max-height: 650px; max-height: 650px;
} }
.link-box { .link-box {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
margin-top: 10px; margin-top: 10px;
} }
.link { .link {
font-size: 12px; font-size: 12px;
} }
} }
</style> </style>
<!-- 绑定手机号码页 --> <!-- 绑定手机号码页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<match-media :min-width="690"> <match-media :min-width="690">
<view class="login-logo"> <view class="login-logo">
<image :src="logo"></image> <image :src="logo"></image>
</view> </view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title title-box">绑定手机号</text> <text class="title title-box">绑定手机号</text>
</match-media> </match-media>
<!-- 登录框 (选择手机号所属国家和地区需要另行实现) --> <!-- 登录框 (选择手机号所属国家和地区需要另行实现) -->
<uni-easyinput clearable :focus="focusMobile" @blur="focusMobile = false" type="number" class="input-box" :inputBorder="false" v-model="formData.mobile" <uni-easyinput clearable :focus="focusMobile" @blur="focusMobile = false" type="number" class="input-box" :inputBorder="false" v-model="formData.mobile"
maxlength="11" placeholder="请输入手机号"></uni-easyinput> maxlength="11" placeholder="请输入手机号"></uni-easyinput>
<uni-id-pages-sms-form ref="smsForm" type="bind-mobile-by-sms" v-model="formData.code" :phone="formData.mobile"> <uni-id-pages-sms-form ref="smsForm" type="bind-mobile-by-sms" v-model="formData.code" :phone="formData.mobile">
</uni-id-pages-sms-form> </uni-id-pages-sms-form>
<button class="uni-btn send-btn-box" type="primary" @click="submit">提交</button> <button class="uni-btn send-btn-box" type="primary" @click="submit">提交</button>
<uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="bind-mobile-by-sms" ref="popup"> <uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="bind-mobile-by-sms" ref="popup">
</uni-popup-captcha> </uni-popup-captcha>
</view> </view>
</template> </template>
<script> <script>
import { import {
store, store,
mutations mutations
} from '@/uni_modules/uni-id-pages/common/store.js' } from '@/uni_modules/uni-id-pages/common/store.js'
export default { export default {
data() { data() {
return { return {
formData: { formData: {
mobile: "", mobile: "",
code: "", code: "",
captcha: "" captcha: ""
}, },
focusMobile:true, focusMobile:true,
logo: "/static/logo.png" logo: "/static/logo.png"
} }
}, },
computed: { computed: {
tipText() { tipText() {
return `验证码已通过短信发送至 ${this.formData.mobile}。密码为6 - 20位` return `验证码已通过短信发送至 ${this.formData.mobile}。密码为6 - 20位`
} }
}, },
onLoad(event) {}, onLoad(event) {},
onReady() {}, onReady() {},
methods: { methods: {
/** /**
* 完成并提交 * 完成并提交
*/ */
submit() { submit() {
if(! /^1\d{10}$/.test(this.formData.mobile)){ if(! /^1\d{10}$/.test(this.formData.mobile)){
this.focusMobile = true this.focusMobile = true
return uni.showToast({ return uni.showToast({
title: '手机号码格式不正确', title: '手机号码格式不正确',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
} }
if(! /^\d{6}$/.test(this.formData.code)){ if(! /^\d{6}$/.test(this.formData.code)){
this.$refs.smsForm.focusSmsCodeInput = true this.$refs.smsForm.focusSmsCodeInput = true
return uni.showToast({ return uni.showToast({
title: '验证码格式不正确', title: '验证码格式不正确',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
} }
// console.log(this.formData); // console.log(this.formData);
const uniIdCo = uniCloud.importObject("uni-id-co") const uniIdCo = uniCloud.importObject("uni-id-co")
uniIdCo.bindMobileBySms(this.formData).then(e => { uniIdCo.bindMobileBySms(this.formData).then(e => {
// console.log(e); // console.log(e);
uni.showToast({ uni.showToast({
title: e.errMsg, title: e.errMsg,
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
// #ifdef APP-NVUE // #ifdef APP-NVUE
const eventChannel = this.$scope.eventChannel; // 兼容APP-NVUE const eventChannel = this.$scope.eventChannel; // 兼容APP-NVUE
// #endif // #endif
// #ifndef APP-NVUE // #ifndef APP-NVUE
const eventChannel = this.getOpenerEventChannel(); const eventChannel = this.getOpenerEventChannel();
// #endif // #endif
mutations.setUserInfo(this.formData) mutations.setUserInfo(this.formData)
uni.navigateBack() uni.navigateBack()
}).catch(e => { }).catch(e => {
console.log(e); console.log(e);
if (e.errCode == 'uni-id-captcha-required') { if (e.errCode == 'uni-id-captcha-required') {
this.$refs.popup.open() this.$refs.popup.open()
} }
}).finally(e => { }).finally(e => {
this.formData.captcha = "" this.formData.captcha = ""
}) })
} }
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
.uni-content { .uni-content {
padding: 0; padding: 0;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 50rpx; padding: 50rpx;
padding-top: 10px; padding-top: 10px;
} }
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
.uni-content{ .uni-content{
padding: 30px 40px 40px; padding: 30px 40px 40px;
} }
} }
/* #ifndef APP-NVUE || VUE3 */ /* #ifndef APP-NVUE || VUE3 */
.uni-content ::v-deep .uni-easyinput__content {} .uni-content ::v-deep .uni-easyinput__content {}
/* #endif */ /* #endif */
.input-box { .input-box {
width: 100%; width: 100%;
margin-top: 16px; margin-top: 16px;
background-color: #f9f9f9; background-color: #f9f9f9;
border-radius: 6rpx; border-radius: 6rpx;
flex-direction: row; flex-direction: row;
flex-wrap: nowrap; flex-wrap: nowrap;
margin-bottom: 10px; margin-bottom: 10px;
} }
.send-btn-box { .send-btn-box {
margin-top: 15px; margin-top: 15px;
} }
</style> </style>
<!-- 图片裁剪页 --> <!-- 图片裁剪页 -->
<template> <template>
<view class="content" > <view class="content" >
<limeClipper :width="options.width" :scale-ratio="2" :is-lock-width="false" :is-lock-height="false" :height="options.height" :image-url="path" <limeClipper :width="options.width" :scale-ratio="2" :is-lock-width="false" :is-lock-height="false" :height="options.height" :image-url="path"
@success="successFn" @cancel="cancel" /> @success="successFn" @cancel="cancel" />
</view> </view>
</template> </template>
<script> <script>
import limeClipper from './limeClipper/limeClipper.vue'; import limeClipper from './limeClipper/limeClipper.vue';
export default { export default {
components: {limeClipper}, components: {limeClipper},
data() {return {path: '',options:{"width":600,"height":600}}}, data() {return {path: '',options:{"width":600,"height":600}}},
onLoad({path,options}) { onLoad({path,options}) {
this.path = path this.path = path
// console.log('path-path-path-path',path); // console.log('path-path-path-path',path);
if(options){ if(options){
this.options = JSON.parse(options) this.options = JSON.parse(options)
} }
}, },
methods:{ methods:{
successFn(e){ successFn(e){
this.getOpenerEventChannel().emit('success',e.url) this.getOpenerEventChannel().emit('success',e.url)
uni.navigateBack() uni.navigateBack()
}, },
cancel(){ cancel(){
uni.navigateBack() uni.navigateBack()
} }
} }
} }
</script> </script>
<style> <style>
.box{ .box{
width: 400rpx; width: 400rpx;
} }
.mt{ .mt{
margin-top: -10px; margin-top: -10px;
} }
</style> </style>
\ No newline at end of file
> 插件来源:[https://ext.dcloud.net.cn/plugin?id=3594](https://ext.dcloud.net.cn/plugin?id=3594) > 插件来源:[https://ext.dcloud.net.cn/plugin?id=3594](https://ext.dcloud.net.cn/plugin?id=3594)
##### 以下是作者写的插件介绍: ##### 以下是作者写的插件介绍:
# Clipper 图片裁剪 # Clipper 图片裁剪
> uniapp 图片裁剪,可用于图片头像等裁剪处理 > uniapp 图片裁剪,可用于图片头像等裁剪处理
> [查看更多](http://liangei.gitee.io/limeui/#/clipper) <br> > [查看更多](http://liangei.gitee.io/limeui/#/clipper) <br>
> Q群:458377637 > Q群:458377637
## 平台兼容 ## 平台兼容
| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App | | H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App |
| --- | ---------- | ------------ | ---------- | ---------- | --------- | --- | | --- | ---------- | ------------ | ---------- | ---------- | --------- | --- |
| √ | √ | √ | 未测 | √ | √ | √ | | √ | √ | √ | 未测 | √ | √ | √ |
## 代码演示 ## 代码演示
### 基本用法 ### 基本用法
`@success` 事件点击 👉 **确定** 后会返回生成的图片信息,包含 `url``width``height` `@success` 事件点击 👉 **确定** 后会返回生成的图片信息,包含 `url``width``height`
```html ```html
<image :src="url" v-if="url" mode="widthFix"></image> <image :src="url" v-if="url" mode="widthFix"></image>
<l-clipper v-if="show" @success="url = $event.url; show = false" @cancel="show = false" ></l-clipper> <l-clipper v-if="show" @success="url = $event.url; show = false" @cancel="show = false" ></l-clipper>
<button @tap="show = true">裁剪</button> <button @tap="show = true">裁剪</button>
``` ```
```js ```js
// 非uni_modules引入 // 非uni_modules引入
import lClipper from '@/components/lime-clipper/' import lClipper from '@/components/lime-clipper/'
// uni_modules引入 // uni_modules引入
import lClipper from '@/uni_modules/lime-clipper/components/lime-clipper/' import lClipper from '@/uni_modules/lime-clipper/components/lime-clipper/'
export default { export default {
components: {lClipper}, components: {lClipper},
data() { data() {
return { return {
show: false, show: false,
url: '', url: '',
} }
} }
} }
``` ```
### 传入图片 ### 传入图片
`image-url`可传入**相对路径****临时路径****本地路径****网络图片**<br> `image-url`可传入**相对路径****临时路径****本地路径****网络图片**<br>
* **当为网络地址时** * **当为网络地址时**
* H5:👉 需要解决跨域问题。 <br> * H5:👉 需要解决跨域问题。 <br>
* 小程序:👉 需要配置 downloadFile 域名 <br> * 小程序:👉 需要配置 downloadFile 域名 <br>
```html ```html
<image :src="url" v-if="url" mode="widthFix"></image> <image :src="url" v-if="url" mode="widthFix"></image>
<l-clipper v-if="show" :image-url="imageUrl" @success="url = $event.url; show = false" @cancel="show = false" ></l-clipper> <l-clipper v-if="show" :image-url="imageUrl" @success="url = $event.url; show = false" @cancel="show = false" ></l-clipper>
<button @tap="show = true">裁剪</button> <button @tap="show = true">裁剪</button>
``` ```
```js ```js
export default { export default {
components: {lClipper}, components: {lClipper},
data() { data() {
return { return {
imageUrl: 'https://img12.360buyimg.com/pop/s1180x940_jfs/t1/97205/26/1142/87801/5dbac55aEf795d962/48a4d7a63ff80b8b.jpg', imageUrl: 'https://img12.360buyimg.com/pop/s1180x940_jfs/t1/97205/26/1142/87801/5dbac55aEf795d962/48a4d7a63ff80b8b.jpg',
show: false, show: false,
url: '', url: '',
} }
} }
} }
``` ```
### 确定按钮颜色 ### 确定按钮颜色
样式变量名:`--l-clipper-confirm-color` 样式变量名:`--l-clipper-confirm-color`
可放到全局样式的 `page` 里或节点的 `style` 可放到全局样式的 `page` 里或节点的 `style`
```html ```html
<l-clipper class="clipper" style="--l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24)" ></l-clipper> <l-clipper class="clipper" style="--l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24)" ></l-clipper>
``` ```
```css ```css
// css 中为组件设置 CSS 变量 // css 中为组件设置 CSS 变量
.clipper { .clipper {
--l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24) --l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24)
} }
// 全局 // 全局
page { page {
--l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24) --l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24)
} }
``` ```
### 使用插槽 ### 使用插槽
共五个插槽 `cancel` 取消按钮、 `photo` 选择图片按钮、 `rotate` 旋转按钮、 `confirm` 确定按钮和默认插槽。 共五个插槽 `cancel` 取消按钮、 `photo` 选择图片按钮、 `rotate` 旋转按钮、 `confirm` 确定按钮和默认插槽。
```html ```html
<image :src="url" v-if="url" mode="widthFix"></image> <image :src="url" v-if="url" mode="widthFix"></image>
<l-clipper <l-clipper
v-if="show" v-if="show"
:isLockWidth="isLockWidth" :isLockWidth="isLockWidth"
:isLockHeight="isLockHeight" :isLockHeight="isLockHeight"
:isLockRatio="isLockRatio" :isLockRatio="isLockRatio"
:isLimitMove="isLimitMove" :isLimitMove="isLimitMove"
:isDisableScale="isDisableScale" :isDisableScale="isDisableScale"
:isDisableRotate="isDisableRotate" :isDisableRotate="isDisableRotate"
:isShowCancelBtn="isShowCancelBtn" :isShowCancelBtn="isShowCancelBtn"
:isShowPhotoBtn="isShowPhotoBtn" :isShowPhotoBtn="isShowPhotoBtn"
:isShowRotateBtn="isShowRotateBtn" :isShowRotateBtn="isShowRotateBtn"
:isShowConfirmBtn="isShowConfirmBtn" :isShowConfirmBtn="isShowConfirmBtn"
@success="url = $event.url; show = false" @success="url = $event.url; show = false"
@cancel="show = false" > @cancel="show = false" >
<!-- 四个基本按钮插槽 --> <!-- 四个基本按钮插槽 -->
<view slot="cancel">取消</view> <view slot="cancel">取消</view>
<view slot="photo">选择图片</view> <view slot="photo">选择图片</view>
<view slot="rotate">旋转</view> <view slot="rotate">旋转</view>
<view slot="confirm">确定</view> <view slot="confirm">确定</view>
<!-- 默认插槽 --> <!-- 默认插槽 -->
<view class="tools"> <view class="tools">
<view>显示取消按钮 <view>显示取消按钮
<switch :checked="isShowCancelBtn" @change="isShowCancelBtn = $event.target.value" ></switch> <switch :checked="isShowCancelBtn" @change="isShowCancelBtn = $event.target.value" ></switch>
</view> </view>
<view>显示选择图片按钮 <view>显示选择图片按钮
<switch :checked="isShowPhotoBtn" @change="isShowPhotoBtn = $event.target.value" ></switch> <switch :checked="isShowPhotoBtn" @change="isShowPhotoBtn = $event.target.value" ></switch>
</view> </view>
<view>显示旋转按钮 <view>显示旋转按钮
<switch :checked="isShowRotateBtn" @change="isShowRotateBtn = $event.target.value" ></switch> <switch :checked="isShowRotateBtn" @change="isShowRotateBtn = $event.target.value" ></switch>
</view> </view>
<view>显示确定按钮 <view>显示确定按钮
<switch :checked="isShowConfirmBtn" @change="isShowConfirmBtn = $event.target.value" ></switch> <switch :checked="isShowConfirmBtn" @change="isShowConfirmBtn = $event.target.value" ></switch>
</view> </view>
<view>锁定裁剪框宽度 <view>锁定裁剪框宽度
<switch :checked="isLockWidth" @change="isLockWidth = $event.target.value" ></switch> <switch :checked="isLockWidth" @change="isLockWidth = $event.target.value" ></switch>
</view> </view>
<view>锁定裁剪框高度 <view>锁定裁剪框高度
<switch :checked="isLockHeight" @change="isLockHeight = $event.target.value" ></switch> <switch :checked="isLockHeight" @change="isLockHeight = $event.target.value" ></switch>
</view> </view>
<view>锁定裁剪框比例 <view>锁定裁剪框比例
<switch :checked="isLockRatio" @change="isLockRatio = $event.target.value" ></switch> <switch :checked="isLockRatio" @change="isLockRatio = $event.target.value" ></switch>
</view> </view>
<view>限制移动范围 <view>限制移动范围
<switch :checked="isLimitMove" @change="isLimitMove = $event.target.value" ></switch> <switch :checked="isLimitMove" @change="isLimitMove = $event.target.value" ></switch>
</view> </view>
<view>禁止缩放 <view>禁止缩放
<switch :checked="isDisableScale" @change="isDisableScale = $event.target.value" ></switch> <switch :checked="isDisableScale" @change="isDisableScale = $event.target.value" ></switch>
</view> </view>
<view>禁止旋转 <view>禁止旋转
<switch :checked="isDisableRotate" @change="isDisableRotate = $event.target.value" ></switch> <switch :checked="isDisableRotate" @change="isDisableRotate = $event.target.value" ></switch>
</view> </view>
</view> </view>
</l-clipper> </l-clipper>
<button @tap="show = true">裁剪</button> <button @tap="show = true">裁剪</button>
``` ```
```js ```js
export default { export default {
components: {lClipper}, components: {lClipper},
data() { data() {
return { return {
show: false, show: false,
url: '', url: '',
isLockWidth: false, isLockWidth: false,
isLockHeight: false, isLockHeight: false,
isLockRatio: true, isLockRatio: true,
isLimitMove: false, isLimitMove: false,
isDisableScale: false, isDisableScale: false,
isDisableRotate: false, isDisableRotate: false,
isShowCancelBtn: true, isShowCancelBtn: true,
isShowPhotoBtn: true, isShowPhotoBtn: true,
isShowRotateBtn: true, isShowRotateBtn: true,
isShowConfirmBtn: true isShowConfirmBtn: true
} }
} }
} }
``` ```
## API ## API
### Props ### Props
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 |
| ------------- | ------------ | ---------------- | ------------ | | ------------- | ------------ | ---------------- | ------------ |
| image-url | 图片路径 | <em>string</em> | | | image-url | 图片路径 | <em>string</em> | |
| quality | 图片的质量,取值范围为 [0, 1],不在范围内时当作1处理 | <em>number</em> | `1` | | quality | 图片的质量,取值范围为 [0, 1],不在范围内时当作1处理 | <em>number</em> | `1` |
| source | `{album: '从相册中选择'}`key为图片来源类型,value为选项说明 | <em>Object</em> | | | source | `{album: '从相册中选择'}`key为图片来源类型,value为选项说明 | <em>Object</em> | |
| width | 裁剪框宽度,单位为 `rpx` | <em>number</em> | `400` | | width | 裁剪框宽度,单位为 `rpx` | <em>number</em> | `400` |
| height | 裁剪框高度 | <em>number</em> | `400` | | height | 裁剪框高度 | <em>number</em> | `400` |
| min-width | 裁剪框最小宽度 | <em>number</em> | `200` | | min-width | 裁剪框最小宽度 | <em>number</em> | `200` |
| min-height |裁剪框最小高度 | <em>number</em> | `200` | | min-height |裁剪框最小高度 | <em>number</em> | `200` |
| max-width | 裁剪框最大宽度 | <em>number</em> | `600` | | max-width | 裁剪框最大宽度 | <em>number</em> | `600` |
| max-height | 裁剪框最大宽度 | <em>number</em> | `600` | | max-height | 裁剪框最大宽度 | <em>number</em> | `600` |
| min-ratio | 图片最小缩放比 | <em>number</em> | `0.5` | | min-ratio | 图片最小缩放比 | <em>number</em> | `0.5` |
| max-ratio | 图片最大缩放比 | <em>number</em> | `2` | | max-ratio | 图片最大缩放比 | <em>number</em> | `2` |
| rotate-angle | 旋转按钮每次旋转的角度 | <em>number</em> | `90` | | rotate-angle | 旋转按钮每次旋转的角度 | <em>number</em> | `90` |
| scale-ratio | 生成图片相对于裁剪框的比例, **比例越高生成图片越清晰** | <em>number</em> | `1` | | scale-ratio | 生成图片相对于裁剪框的比例, **比例越高生成图片越清晰** | <em>number</em> | `1` |
| is-lock-width | 是否锁定裁剪框宽度 | <em>boolean</em> | `false` | | is-lock-width | 是否锁定裁剪框宽度 | <em>boolean</em> | `false` |
| is-lock-height | 是否锁定裁剪框高度上 | <em>boolean</em> | `false` | | is-lock-height | 是否锁定裁剪框高度上 | <em>boolean</em> | `false` |
| is-lock-ratio | 是否锁定裁剪框比例 | <em>boolean</em> | `true` | | is-lock-ratio | 是否锁定裁剪框比例 | <em>boolean</em> | `true` |
| is-disable-scale | 是否禁止缩放 | <em>boolean</em> | `false` | | is-disable-scale | 是否禁止缩放 | <em>boolean</em> | `false` |
| is-disable-rotate | 是否禁止旋转 | <em>boolean</em> | `false` | | is-disable-rotate | 是否禁止旋转 | <em>boolean</em> | `false` |
| is-limit-move | 是否限制移动范围 | <em>boolean</em> | `false` | | is-limit-move | 是否限制移动范围 | <em>boolean</em> | `false` |
| is-show-photo-btn | 是否显示选择图片按钮 | <em>boolean</em> | `true` | | is-show-photo-btn | 是否显示选择图片按钮 | <em>boolean</em> | `true` |
| is-show-rotate-btn | 是否显示转按钮 | <em>boolean</em> | `true` | | is-show-rotate-btn | 是否显示转按钮 | <em>boolean</em> | `true` |
| is-show-confirm-btn | 是否显示确定按钮 | <em>boolean</em> | `true` | | is-show-confirm-btn | 是否显示确定按钮 | <em>boolean</em> | `true` |
| is-show-cancel-btn | 是否显示关闭按钮 | <em>boolean</em> | `true` | | is-show-cancel-btn | 是否显示关闭按钮 | <em>boolean</em> | `true` |
### 事件 Events ### 事件 Events
| 事件名 | 说明 | 回调 | | 事件名 | 说明 | 回调 |
| ------- | ------------ | -------------- | | ------- | ------------ | -------------- |
| success | 生成图片成功 | {`width`, `height`, `url`} | | success | 生成图片成功 | {`width`, `height`, `url`} |
| fail | 生成图片失败 | `error` | | fail | 生成图片失败 | `error` |
| cancel | 关闭 | `false` | | cancel | 关闭 | `false` |
| ready | 图片加载完成 | {`width`, `height`, `path`, `orientation`, `type`} | | ready | 图片加载完成 | {`width`, `height`, `path`, `orientation`, `type`} |
| change | 图片大小改变时触发 | {`width`, `height`} | | change | 图片大小改变时触发 | {`width`, `height`} |
| rotate | 图片旋转时触发 | `angle` | | rotate | 图片旋转时触发 | `angle` |
## 常见问题 ## 常见问题
> 1、H5端使用网络图片需要解决跨域问题。<br> > 1、H5端使用网络图片需要解决跨域问题。<br>
> 2、小程序使用网络图片需要去公众平台增加下载白名单!二级域名也需要配!<br> > 2、小程序使用网络图片需要去公众平台增加下载白名单!二级域名也需要配!<br>
> 3、H5端生成图片是base64,有时显示只有一半可以使用原生标签`<IMG/>`<br> > 3、H5端生成图片是base64,有时显示只有一半可以使用原生标签`<IMG/>`<br>
> 4、IOS APP 请勿使用HBX2.9.3.20201014的版本!这个版本无法生成图片。<br> > 4、IOS APP 请勿使用HBX2.9.3.20201014的版本!这个版本无法生成图片。<br>
> 5、APP端无成功反馈、也无失败反馈时,请更新基座和HBX。<br> > 5、APP端无成功反馈、也无失败反馈时,请更新基座和HBX。<br>
## 打赏 ## 打赏
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。<br> 如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。<br>
![输入图片说明](https://images.gitee.com/uploads/images/2020/1122/222521_bb543f96_518581.jpeg "微信图片编辑_20201122220352.jpg") ![输入图片说明](https://images.gitee.com/uploads/images/2020/1122/222521_bb543f96_518581.jpeg "微信图片编辑_20201122220352.jpg")
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve"> viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css"> <style type="text/css">
.st0{fill:#606060;} .st0{fill:#606060;}
.st1{fill:none;stroke:#FFFFFF;stroke-width:2.4306;stroke-miterlimit:10;} .st1{fill:none;stroke:#FFFFFF;stroke-width:2.4306;stroke-miterlimit:10;}
.st2{fill:#FFFFFF;} .st2{fill:#FFFFFF;}
</style> </style>
<g> <g>
<path class="st2" d="M11.6,11c0.4,0.4,0.6,0.9,0.6,1.5c0,0.6-0.2,1.1-0.6,1.4c-0.4,0.4-0.9,0.6-1.5,0.6c-0.6,0-1.1-0.2-1.5-0.6 <path class="st2" d="M11.6,11c0.4,0.4,0.6,0.9,0.6,1.5c0,0.6-0.2,1.1-0.6,1.4c-0.4,0.4-0.9,0.6-1.5,0.6c-0.6,0-1.1-0.2-1.5-0.6
c-0.4-0.4-0.6-0.9-0.6-1.4s0.2-1.1,0.6-1.5c0.4-0.4,0.9-0.6,1.5-0.6C10.8,10.4,11.2,10.6,11.6,11z M24.6,18.4V6.7H5.4v12l1.8-1.8 c-0.4-0.4-0.6-0.9-0.6-1.4s0.2-1.1,0.6-1.5c0.4-0.4,0.9-0.6,1.5-0.6C10.8,10.4,11.2,10.6,11.6,11z M24.6,18.4V6.7H5.4v12l1.8-1.8
c0.3-0.3,0.6-0.4,1-0.4c0.4,0,0.7,0.1,1,0.4l1.8,1.8l5.8-7c0.3-0.3,0.6-0.5,1.1-0.5c0.4,0,0.8,0.2,1.1,0.5 c0.3-0.3,0.6-0.4,1-0.4c0.4,0,0.7,0.1,1,0.4l1.8,1.8l5.8-7c0.3-0.3,0.6-0.5,1.1-0.5c0.4,0,0.8,0.2,1.1,0.5
C18.8,11.6,24.6,18.4,24.6,18.4z M25.6,5.7C25.9,6,26,6.3,26,6.7v16.1c0,0.4-0.1,0.7-0.4,1c-0.3,0.3-0.6,0.4-1,0.4H5.4 C18.8,11.6,24.6,18.4,24.6,18.4z M25.6,5.7C25.9,6,26,6.3,26,6.7v16.1c0,0.4-0.1,0.7-0.4,1c-0.3,0.3-0.6,0.4-1,0.4H5.4
c-0.4,0-0.7-0.1-1-0.4c-0.3-0.3-0.4-0.6-0.4-1V6.7c0-0.4,0.1-0.7,0.4-1c0.3-0.3,0.6-0.4,1-0.4h19.3C25,5.3,25.3,5.4,25.6,5.7z"/> c-0.4,0-0.7-0.1-1-0.4c-0.3-0.3-0.4-0.6-0.4-1V6.7c0-0.4,0.1-0.7,0.4-1c0.3-0.3,0.6-0.4,1-0.4h19.3C25,5.3,25.3,5.4,25.6,5.7z"/>
<path class="st1" d="M24.3,21.5H5.7c-0.2,0-0.3-0.2-0.3-0.3V7c0-0.2,0.2-0.3,0.3-0.3h18.6c0.2,0,0.3,0.2,0.3,0.3v14.2 <path class="st1" d="M24.3,21.5H5.7c-0.2,0-0.3-0.2-0.3-0.3V7c0-0.2,0.2-0.3,0.3-0.3h18.6c0.2,0,0.3,0.2,0.3,0.3v14.2
C24.6,21.3,24.5,21.5,24.3,21.5z"/> C24.6,21.3,24.5,21.5,24.3,21.5z"/>
</g> </g>
</svg> </svg>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="30px" height="30px" viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve"> width="30px" height="30px" viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css"> <style type="text/css">
.st0{fill:none;stroke:#FFFFFF;stroke-width:2.4306;stroke-miterlimit:10;} .st0{fill:none;stroke:#FFFFFF;stroke-width:2.4306;stroke-miterlimit:10;}
.st1{fill:#FFFFFF;} .st1{fill:#FFFFFF;}
</style> </style>
<g> <g>
<path class="st0" d="M17.1,24.2h-12c-0.2,0-0.3-0.2-0.3-0.3v-9.3c0-0.2,0.2-0.3,0.3-0.3h12c0.2,0,0.3,0.2,0.3,0.3v9.3 <path class="st0" d="M17.1,24.2h-12c-0.2,0-0.3-0.2-0.3-0.3v-9.3c0-0.2,0.2-0.3,0.3-0.3h12c0.2,0,0.3,0.2,0.3,0.3v9.3
C17.5,24.1,17.3,24.2,17.1,24.2z"/> C17.5,24.1,17.3,24.2,17.1,24.2z"/>
<path class="st0" d="M16.6,5.4c4.8,0,8.7,3.9,8.7,8.7"/> <path class="st0" d="M16.6,5.4c4.8,0,8.7,3.9,8.7,8.7"/>
<polyline class="st0" points="19.3,10.1 14.9,5.6 19.3,1.2 "/> <polyline class="st0" points="19.3,10.1 14.9,5.6 19.3,1.2 "/>
</g> </g>
</svg> </svg>
.flex-auto { .flex-auto {
flex: auto; flex: auto;
} }
.bg-transparent { .bg-transparent {
background-color: rgba(0,0,0,0.9); background-color: rgba(0,0,0,0.9);
transition-duration: 0.35s; transition-duration: 0.35s;
} }
.l-clipper { .l-clipper {
width: 100vw; width: 100vw;
height: calc(100vh - var(--window-top)); height: calc(100vh - var(--window-top));
background-color: rgba(0,0,0,0.9); background-color: rgba(0,0,0,0.9);
position: fixed; position: fixed;
top: var(--window-top); top: var(--window-top);
left: 0; left: 0;
z-index: 1; z-index: 1;
} }
.l-clipper-mask { .l-clipper-mask {
position: relative; position: relative;
z-index: 2; z-index: 2;
pointer-events: none; pointer-events: none;
} }
.l-clipper__content { .l-clipper__content {
pointer-events: none; pointer-events: none;
position: absolute; position: absolute;
border: 1rpx solid rgba(255,255,255,0.3); border: 1rpx solid rgba(255,255,255,0.3);
box-sizing: border-box; box-sizing: border-box;
box-shadow: rgba(0,0,0,0.5) 0 0 0 80vh; box-shadow: rgba(0,0,0,0.5) 0 0 0 80vh;
background: transparent; background: transparent;
} }
.l-clipper__content::before, .l-clipper__content::before,
.l-clipper__content::after { .l-clipper__content::after {
content: ''; content: '';
position: absolute; position: absolute;
border: 1rpx dashed rgba(255,255,255,0.3); border: 1rpx dashed rgba(255,255,255,0.3);
} }
.l-clipper__content::before { .l-clipper__content::before {
width: 100%; width: 100%;
top: 33.33%; top: 33.33%;
height: 33.33%; height: 33.33%;
border-left: none; border-left: none;
border-right: none; border-right: none;
} }
.l-clipper__content::after { .l-clipper__content::after {
width: 33.33%; width: 33.33%;
left: 33.33%; left: 33.33%;
height: 100%; height: 100%;
border-top: none; border-top: none;
border-bottom: none; border-bottom: none;
} }
.l-clipper__edge { .l-clipper__edge {
position: absolute; position: absolute;
width: 34rpx; width: 34rpx;
height: 34rpx; height: 34rpx;
border: 6rpx solid #fff; border: 6rpx solid #fff;
pointer-events: auto; pointer-events: auto;
} }
.l-clipper__edge::before { .l-clipper__edge::before {
content: ''; content: '';
position: absolute; position: absolute;
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
background-color: transparent; background-color: transparent;
} }
.l-clipper__edge:nth-child(1) { .l-clipper__edge:nth-child(1) {
left: -6rpx; left: -6rpx;
top: -6rpx; top: -6rpx;
border-bottom-width: 0 !important; border-bottom-width: 0 !important;
border-right-width: 0 !important; border-right-width: 0 !important;
} }
.l-clipper__edge:nth-child(1):before { .l-clipper__edge:nth-child(1):before {
top: -50%; top: -50%;
left: -50%; left: -50%;
} }
.l-clipper__edge:nth-child(2) { .l-clipper__edge:nth-child(2) {
right: -6rpx; right: -6rpx;
top: -6rpx; top: -6rpx;
border-bottom-width: 0 !important; border-bottom-width: 0 !important;
border-left-width: 0 !important; border-left-width: 0 !important;
} }
.l-clipper__edge:nth-child(2):before { .l-clipper__edge:nth-child(2):before {
top: -50%; top: -50%;
left: 50%; left: 50%;
} }
.l-clipper__edge:nth-child(3) { .l-clipper__edge:nth-child(3) {
left: -6rpx; left: -6rpx;
bottom: -6rpx; bottom: -6rpx;
border-top-width: 0 !important; border-top-width: 0 !important;
border-right-width: 0 !important; border-right-width: 0 !important;
} }
.l-clipper__edge:nth-child(3):before { .l-clipper__edge:nth-child(3):before {
bottom: -50%; bottom: -50%;
left: -50%; left: -50%;
} }
.l-clipper__edge:nth-child(4) { .l-clipper__edge:nth-child(4) {
right: -6rpx; right: -6rpx;
bottom: -6rpx; bottom: -6rpx;
border-top-width: 0 !important; border-top-width: 0 !important;
border-left-width: 0 !important; border-left-width: 0 !important;
} }
.l-clipper__edge:nth-child(4):before { .l-clipper__edge:nth-child(4):before {
bottom: -50%; bottom: -50%;
left: 50%; left: 50%;
} }
.l-clipper-image { .l-clipper-image {
width: 100%; width: 100%;
border-style: none; border-style: none;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
z-index: 1; z-index: 1;
-webkit-backface-visibility: hidden; -webkit-backface-visibility: hidden;
backface-visibility: hidden; backface-visibility: hidden;
transform-origin: center; transform-origin: center;
} }
.l-clipper-canvas { .l-clipper-canvas {
position: fixed; position: fixed;
z-index: 10; z-index: 10;
left: -200vw; left: -200vw;
top: -200vw; top: -200vw;
pointer-events: none; pointer-events: none;
} }
.l-clipper-tools { .l-clipper-tools {
position: fixed; position: fixed;
left: 0; left: 0;
bottom: 10px; bottom: 10px;
width: 100%; width: 100%;
z-index: 99; z-index: 99;
color: #fff; color: #fff;
} }
.l-clipper-tools__btns { .l-clipper-tools__btns {
font-weight: bold; font-weight: bold;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
padding: 20rpx 40rpx; padding: 20rpx 40rpx;
box-sizing: border-box; box-sizing: border-box;
} }
.l-clipper-tools__btns .cancel { .l-clipper-tools__btns .cancel {
width: 112rpx; width: 112rpx;
height: 60rpx; height: 60rpx;
text-align: center; text-align: center;
line-height: 60rpx; line-height: 60rpx;
} }
.l-clipper-tools__btns .confirm { .l-clipper-tools__btns .confirm {
width: 112rpx; width: 112rpx;
height: 60rpx; height: 60rpx;
line-height: 60rpx; line-height: 60rpx;
background-color: #07c160; background-color: #07c160;
border-radius: 6rpx; border-radius: 6rpx;
text-align: center; text-align: center;
} }
.l-clipper-tools__btns image { .l-clipper-tools__btns image {
display: block; display: block;
width: 60rpx; width: 60rpx;
height: 60rpx; height: 60rpx;
} }
.l-clipper-tools__btns { .l-clipper-tools__btns {
flex-direction: row; flex-direction: row;
} }
<template> <template>
<view class="l-clipper" :class="{open: value}" disable-scroll :style="'z-index: ' + zIndex + ';' + customStyle"> <view class="l-clipper" :class="{open: value}" disable-scroll :style="'z-index: ' + zIndex + ';' + customStyle">
<view class="l-clipper-mask" @touchstart.stop.prevent="clipTouchStart" @touchmove.stop.prevent="clipTouchMove" @touchend.stop.prevent="clipTouchEnd"> <view class="l-clipper-mask" @touchstart.stop.prevent="clipTouchStart" @touchmove.stop.prevent="clipTouchMove" @touchend.stop.prevent="clipTouchEnd">
<view class="l-clipper__content" :style="clipStyle"><view class="l-clipper__edge" v-for="(item, index) in [0, 0, 0, 0]" :key="index"></view></view> <view class="l-clipper__content" :style="clipStyle"><view class="l-clipper__edge" v-for="(item, index) in [0, 0, 0, 0]" :key="index"></view></view>
</view> </view>
<image <image
class="l-clipper-image" class="l-clipper-image"
@error="imageLoad" @error="imageLoad"
@load="imageLoad" @load="imageLoad"
@touchstart.stop.prevent="imageTouchStart" @touchstart.stop.prevent="imageTouchStart"
@touchmove.stop.prevent="imageTouchMove" @touchmove.stop.prevent="imageTouchMove"
@touchend.stop.prevent="imageTouchEnd" @touchend.stop.prevent="imageTouchEnd"
:src="image" :src="image"
:mode="imageWidth == 'auto' ? 'widthFix' : ''" :mode="imageWidth == 'auto' ? 'widthFix' : ''"
v-if="image" v-if="image"
:style="imageStyle" :style="imageStyle"
/> />
<canvas <canvas
:canvas-id="canvasId" :canvas-id="canvasId"
id="l-clipper" id="l-clipper"
disable-scroll disable-scroll
:style="'width: ' + canvasWidth * scaleRatio + 'px; height:' + canvasHeight * scaleRatio + 'px;'" :style="'width: ' + canvasWidth * scaleRatio + 'px; height:' + canvasHeight * scaleRatio + 'px;'"
class="l-clipper-canvas" class="l-clipper-canvas"
></canvas> ></canvas>
<view class="l-clipper-tools"> <view class="l-clipper-tools">
<view class="l-clipper-tools__btns"> <view class="l-clipper-tools__btns">
<view v-if="isShowCancelBtn" @tap="cancel"> <view v-if="isShowCancelBtn" @tap="cancel">
<slot name="cancel" v-if="$slots.cancel" /> <slot name="cancel" v-if="$slots.cancel" />
<view v-else class="cancel">取消</view> <view v-else class="cancel">取消</view>
</view> </view>
<view v-if="isShowPhotoBtn" @tap="uploadImage"> <view v-if="isShowPhotoBtn" @tap="uploadImage">
<slot name="photo" v-if="$slots.photo" /> <slot name="photo" v-if="$slots.photo" />
<image v-else src="./images/photo.svg" /> <image v-else src="./images/photo.svg" />
</view> </view>
<view v-if="isShowRotateBtn" @tap="rotate"> <view v-if="isShowRotateBtn" @tap="rotate">
<slot name="rotate" v-if="$slots.rotate" /> <slot name="rotate" v-if="$slots.rotate" />
<image v-else src="./images/rotate.svg" data-type="inverse" /> <image v-else src="./images/rotate.svg" data-type="inverse" />
</view> </view>
<view v-if="isShowConfirmBtn" @tap="confirm"> <view v-if="isShowConfirmBtn" @tap="confirm">
<slot name="confirm" v-if="$slots.confirm" /> <slot name="confirm" v-if="$slots.confirm" />
<view v-else class="confirm">确定</view> <view v-else class="confirm">确定</view>
</view> </view>
</view> </view>
<slot></slot> <slot></slot>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
import { determineDirection, calcImageOffset, calcImageScale, calcImageSize, calcPythagoreanTheorem, clipTouchMoveOfCalculate, imageTouchMoveOfCalcOffset } from './utils'; import { determineDirection, calcImageOffset, calcImageScale, calcImageSize, calcPythagoreanTheorem, clipTouchMoveOfCalculate, imageTouchMoveOfCalcOffset } from './utils';
const cache = {} const cache = {}
export default { export default {
// version: '0.6.3', // version: '0.6.3',
name: 'l-clipper', name: 'l-clipper',
props: { props: {
value: { value: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
type: { type: {
type: String, type: String,
default: '2d' default: '2d'
}, },
// #endif // #endif
customStyle: { customStyle: {
type: String, type: String,
}, },
canvasId: { canvasId: {
type: String, type: String,
default: 'l-clipper' default: 'l-clipper'
}, },
zIndex: { zIndex: {
type: Number, type: Number,
default: 99 default: 99
}, },
imageUrl: { imageUrl: {
type: String type: String
}, },
fileType: { fileType: {
type: String, type: String,
default: 'png' default: 'png'
}, },
quality: { quality: {
type: Number, type: Number,
default: 1 default: 1
}, },
width: { width: {
type: Number, type: Number,
default: 400 default: 400
}, },
height: { height: {
type: Number, type: Number,
default: 400 default: 400
}, },
minWidth: { minWidth: {
type: Number, type: Number,
default: 200 default: 200
}, },
maxWidth: { maxWidth: {
type: Number, type: Number,
default: 600 default: 600
}, },
minHeight: { minHeight: {
type: Number, type: Number,
default: 200 default: 200
}, },
maxHeight: { maxHeight: {
type: Number, type: Number,
default: 600 default: 600
}, },
isLockWidth: { isLockWidth: {
type: Boolean, type: Boolean,
default: false default: false
}, },
isLockHeight: { isLockHeight: {
type: Boolean, type: Boolean,
default: false default: false
}, },
isLockRatio: { isLockRatio: {
type: Boolean, type: Boolean,
default: true default: true
}, },
scaleRatio: { scaleRatio: {
type: Number, type: Number,
default: 1 default: 1
}, },
minRatio: { minRatio: {
type: Number, type: Number,
default: 0.5 default: 0.5
}, },
maxRatio: { maxRatio: {
type: Number, type: Number,
default: 2 default: 2
}, },
isDisableScale: { isDisableScale: {
type: Boolean, type: Boolean,
default: false default: false
}, },
isDisableRotate: { isDisableRotate: {
type: Boolean, type: Boolean,
default: false default: false
}, },
isLimitMove: { isLimitMove: {
type: Boolean, type: Boolean,
default: false default: false
}, },
isShowPhotoBtn: { isShowPhotoBtn: {
type: Boolean, type: Boolean,
default: true default: true
}, },
isShowRotateBtn: { isShowRotateBtn: {
type: Boolean, type: Boolean,
default: true default: true
}, },
isShowConfirmBtn: { isShowConfirmBtn: {
type: Boolean, type: Boolean,
default: true default: true
}, },
isShowCancelBtn: { isShowCancelBtn: {
type: Boolean, type: Boolean,
default: true default: true
}, },
rotateAngle: { rotateAngle: {
type: Number, type: Number,
default: 90 default: 90
}, },
source: { source: {
type: Object, type: Object,
default: () => ({ default: () => ({
album: '从相册中选择', album: '从相册中选择',
camera: '拍照', camera: '拍照',
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
message: '从微信中选择' message: '从微信中选择'
// #endif // #endif
}) })
} }
}, },
data() { data() {
return { return {
canvasWidth: 0, canvasWidth: 0,
canvasHeight: 0, canvasHeight: 0,
clipX: 0, clipX: 0,
clipY: 0, clipY: 0,
clipWidth: 0, clipWidth: 0,
clipHeight: 0, clipHeight: 0,
animation: false, animation: false,
imageWidth: 0, imageWidth: 0,
imageHeight: 0, imageHeight: 0,
imageTop: 0, imageTop: 0,
imageLeft: 0, imageLeft: 0,
scale: 1, scale: 1,
angle: 0, angle: 0,
image: this.imageUrl, image: this.imageUrl,
sysinfo: {}, sysinfo: {},
throttleTimer: null, throttleTimer: null,
throttleFlag: true, throttleFlag: true,
timeClipCenter: null, timeClipCenter: null,
flagClipTouch: false, flagClipTouch: false,
flagEndTouch: false, flagEndTouch: false,
clipStart: {}, clipStart: {},
animationTimer: null, animationTimer: null,
touchRelative: [{x: 0,y: 0}], touchRelative: [{x: 0,y: 0}],
hypotenuseLength: 0, hypotenuseLength: 0,
ctx: null ctx: null
}; };
}, },
computed: { computed: {
clipStyle() { clipStyle() {
const {clipWidth, clipHeight, clipY, clipX, animation} = this const {clipWidth, clipHeight, clipY, clipX, animation} = this
return ` return `
width: ${clipWidth}px; width: ${clipWidth}px;
height:${clipHeight}px; height:${clipHeight}px;
transition-property: ${animation ? '' : 'background'}; transition-property: ${animation ? '' : 'background'};
left: ${clipX}px; left: ${clipX}px;
top: ${clipY}px top: ${clipY}px
` `
}, },
imageStyle() { imageStyle() {
const {imageWidth, imageHeight, imageLeft, imageTop, animation, scale, angle} = this const {imageWidth, imageHeight, imageLeft, imageTop, animation, scale, angle} = this
return ` return `
width: ${imageWidth ? imageWidth + 'px' : 'auto'}; width: ${imageWidth ? imageWidth + 'px' : 'auto'};
height: ${imageHeight ? imageHeight + 'px' : 'auto'}; height: ${imageHeight ? imageHeight + 'px' : 'auto'};
transform: translate3d(${imageLeft - imageWidth / 2}px, ${imageTop - imageHeight / 2}px, 0) scale(${scale}) rotate(${angle}deg); transform: translate3d(${imageLeft - imageWidth / 2}px, ${imageTop - imageHeight / 2}px, 0) scale(${scale}) rotate(${angle}deg);
transition-duration: ${animation ? 0.35 : 0}s transition-duration: ${animation ? 0.35 : 0}s
` `
}, },
clipSize() { clipSize() {
const { clipWidth, clipHeight } = this; const { clipWidth, clipHeight } = this;
return { clipWidth, clipHeight }; return { clipWidth, clipHeight };
}, },
clipPoint() { clipPoint() {
const { clipY, clipX } = this; const { clipY, clipX } = this;
return { clipY, clipX }; return { clipY, clipX };
} }
}, },
watch: { watch: {
value(val) { value(val) {
if(!val) { if(!val) {
this.animation = 0 this.animation = 0
this.angle = 0 this.angle = 0
} else { } else {
if(this.imageUrl) { if(this.imageUrl) {
const {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight, path} = cache?.[this.imageUrl] || {} const {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight, path} = cache?.[this.imageUrl] || {}
if(path != this.image) { if(path != this.image) {
this.image = this.imageUrl; this.image = this.imageUrl;
} else { } else {
this.setDiffData({imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight}) this.setDiffData({imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight})
} }
} }
} }
}, },
imageUrl(url) { imageUrl(url) {
this.image = url this.image = url
}, },
image:{ image:{
handler: async function(url) { handler: async function(url) {
this.getImageInfo(url) this.getImageInfo(url)
}, },
// immediate: true, // immediate: true,
}, },
clipSize({ widthVal, heightVal }) { clipSize({ widthVal, heightVal }) {
let { minWidth, minHeight } = this; let { minWidth, minHeight } = this;
minWidth = minWidth / 2; minWidth = minWidth / 2;
minHeight = minHeight / 2; minHeight = minHeight / 2;
if (widthVal < minWidth) { if (widthVal < minWidth) {
this.setDiffData({clipWidth: minWidth}) this.setDiffData({clipWidth: minWidth})
} }
if (heightVal < minHeight) { if (heightVal < minHeight) {
this.setDiffData({clipHeight: minHeight}) this.setDiffData({clipHeight: minHeight})
} }
this.calcClipSize(); this.calcClipSize();
}, },
angle(val) { angle(val) {
this.animation = true; this.animation = true;
this.moveStop(); this.moveStop();
const { isLimitMove } = this; const { isLimitMove } = this;
if (isLimitMove && val % 90) { if (isLimitMove && val % 90) {
this.setDiffData({ this.setDiffData({
angle: Math.round(val / 90) * 90 angle: Math.round(val / 90) * 90
}) })
} }
this.imgMarginDetectionScale(); this.imgMarginDetectionScale();
}, },
animation(val) { animation(val) {
clearTimeout(this.animationTimer); clearTimeout(this.animationTimer);
if (val) { if (val) {
let animationTimer = setTimeout(() => { let animationTimer = setTimeout(() => {
this.setDiffData({ this.setDiffData({
animation: false animation: false
}) })
}, 260); }, 260);
this.setDiffData({animationTimer}) this.setDiffData({animationTimer})
this.animationTimer = animationTimer; this.animationTimer = animationTimer;
} }
}, },
isLimitMove(val) { isLimitMove(val) {
if (val) { if (val) {
if (this.angle % 90) { if (this.angle % 90) {
this.setDiffData({ this.setDiffData({
angle : Math.round(this.angle / 90) * 90 angle : Math.round(this.angle / 90) * 90
}) })
} }
this.imgMarginDetectionScale(); this.imgMarginDetectionScale();
} }
}, },
clipPoint() { clipPoint() {
this.cutDetectionPosition(); this.cutDetectionPosition();
}, },
width(width, oWidth) { width(width, oWidth) {
if (width !== oWidth) { if (width !== oWidth) {
this.setDiffData({ this.setDiffData({
clipWidth: width / 2 clipWidth: width / 2
}) })
} }
}, },
height(height, oHeight) { height(height, oHeight) {
if (height !== oHeight) { if (height !== oHeight) {
this.setDiffData({ this.setDiffData({
clipHeight: height / 2 clipHeight: height / 2
}) })
} }
} }
}, },
mounted() { mounted() {
const sysinfo = uni.getSystemInfoSync(); const sysinfo = uni.getSystemInfoSync();
this.sysinfo = sysinfo; this.sysinfo = sysinfo;
this.setClipInfo(); this.setClipInfo();
if(this.image) { if(this.image) {
this.getImageInfo(this.image) this.getImageInfo(this.image)
} }
this.setClipCenter(); this.setClipCenter();
this.calcClipSize(); this.calcClipSize();
this.cutDetectionPosition(); this.cutDetectionPosition();
}, },
methods: { methods: {
setDiffData(data) { setDiffData(data) {
Object.keys(data).forEach(key => { Object.keys(data).forEach(key => {
if (this[key] !== data[key]) { if (this[key] !== data[key]) {
this[key] = data[key]; this[key] = data[key];
} }
}); });
}, },
getImageInfo(url) { getImageInfo(url) {
if (!url) return; if (!url) return;
if(this.value) { if(this.value) {
uni.showLoading({ uni.showLoading({
title: '请稍候...', title: '请稍候...',
mask: true mask: true
}); });
} }
uni.getImageInfo({ uni.getImageInfo({
src: url, src: url,
success: res => { success: res => {
this.imgComputeSize(res.width, res.height); this.imgComputeSize(res.width, res.height);
this.image = res.path; this.image = res.path;
if (this.isLimitMove) { if (this.isLimitMove) {
this.imgMarginDetectionScale(); this.imgMarginDetectionScale();
this.$emit('ready', res); this.$emit('ready', res);
} }
const {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight} = this const {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight} = this
cache[url] = Object.assign(res, {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight}); cache[url] = Object.assign(res, {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight});
}, },
fail: (err) => { fail: (err) => {
this.imgComputeSize(); this.imgComputeSize();
if (this.isLimitMove) { if (this.isLimitMove) {
this.imgMarginDetectionScale(); this.imgMarginDetectionScale();
} }
} }
}); });
}, },
setClipInfo() { setClipInfo() {
const { width, height, sysinfo, canvasId } = this; const { width, height, sysinfo, canvasId } = this;
const clipWidth = width / 2; const clipWidth = width / 2;
const clipHeight = height / 2; const clipHeight = height / 2;
const clipY = (sysinfo.windowHeight - clipHeight) / 2; const clipY = (sysinfo.windowHeight - clipHeight) / 2;
const clipX = (sysinfo.windowWidth - clipWidth) / 2; const clipX = (sysinfo.windowWidth - clipWidth) / 2;
const imageLeft = sysinfo.windowWidth / 2; const imageLeft = sysinfo.windowWidth / 2;
const imageTop = sysinfo.windowHeight / 2; const imageTop = sysinfo.windowHeight / 2;
this.ctx = uni.createCanvasContext(canvasId, this); this.ctx = uni.createCanvasContext(canvasId, this);
this.clipWidth = clipWidth; this.clipWidth = clipWidth;
this.clipHeight = clipHeight; this.clipHeight = clipHeight;
this.clipX = clipX; this.clipX = clipX;
this.clipY = clipY; this.clipY = clipY;
this.canvasHeight = clipHeight; this.canvasHeight = clipHeight;
this.canvasWidth = clipWidth; this.canvasWidth = clipWidth;
this.imageLeft = imageLeft; this.imageLeft = imageLeft;
this.imageTop = imageTop; this.imageTop = imageTop;
}, },
setClipCenter() { setClipCenter() {
const { sysInfo, clipHeight, clipWidth, imageTop, imageLeft } = this; const { sysInfo, clipHeight, clipWidth, imageTop, imageLeft } = this;
let sys = sysInfo || uni.getSystemInfoSync(); let sys = sysInfo || uni.getSystemInfoSync();
let clipY = (sys.windowHeight - clipHeight) * 0.5; let clipY = (sys.windowHeight - clipHeight) * 0.5;
let clipX = (sys.windowWidth - clipWidth) * 0.5; let clipX = (sys.windowWidth - clipWidth) * 0.5;
this.imageTop = imageTop - this.clipY + clipY; this.imageTop = imageTop - this.clipY + clipY;
this.imageLeft = imageLeft - this.clipX + clipX; this.imageLeft = imageLeft - this.clipX + clipX;
this.clipY = clipY; this.clipY = clipY;
this.clipX = clipX; this.clipX = clipX;
}, },
calcClipSize() { calcClipSize() {
const { clipHeight, clipWidth, sysinfo, clipX, clipY } = this; const { clipHeight, clipWidth, sysinfo, clipX, clipY } = this;
if (clipWidth > sysinfo.windowWidth) { if (clipWidth > sysinfo.windowWidth) {
this.setDiffData({ this.setDiffData({
clipWidth: sysinfo.windowWidth clipWidth: sysinfo.windowWidth
}) })
} else if (clipWidth + clipX > sysinfo.windowWidth) { } else if (clipWidth + clipX > sysinfo.windowWidth) {
this.setDiffData({ this.setDiffData({
clipX: sysinfo.windowWidth - clipX clipX: sysinfo.windowWidth - clipX
}) })
} }
if (clipHeight > sysinfo.windowHeight) { if (clipHeight > sysinfo.windowHeight) {
this.setDiffData({ this.setDiffData({
clipHeight: sysinfo.windowHeight clipHeight: sysinfo.windowHeight
}) })
} else if (clipHeight + clipY > sysinfo.windowHeight) { } else if (clipHeight + clipY > sysinfo.windowHeight) {
this.clipY = sysinfo.windowHeight - clipY; this.clipY = sysinfo.windowHeight - clipY;
this.setDiffData({ this.setDiffData({
clipY: sysinfo.windowHeight - clipY clipY: sysinfo.windowHeight - clipY
}) })
} }
}, },
cutDetectionPosition() { cutDetectionPosition() {
const { clipX, clipY, sysinfo, clipHeight, clipWidth } = this; const { clipX, clipY, sysinfo, clipHeight, clipWidth } = this;
let cutDetectionPositionTop = () => { let cutDetectionPositionTop = () => {
if (clipY < 0) { if (clipY < 0) {
this.setDiffData({clipY: 0}) this.setDiffData({clipY: 0})
} }
if (clipY > sysinfo.windowHeight - clipHeight) { if (clipY > sysinfo.windowHeight - clipHeight) {
this.setDiffData({clipY: sysinfo.windowHeight - clipHeight}) this.setDiffData({clipY: sysinfo.windowHeight - clipHeight})
} }
}, },
cutDetectionPositionLeft = () => { cutDetectionPositionLeft = () => {
if (clipX < 0) { if (clipX < 0) {
this.setDiffData({clipX: 0}) this.setDiffData({clipX: 0})
} }
if (clipX > sysinfo.windowWidth - clipWidth) { if (clipX > sysinfo.windowWidth - clipWidth) {
this.setDiffData({clipX: sysinfo.windowWidth - clipWidth}) this.setDiffData({clipX: sysinfo.windowWidth - clipWidth})
} }
}; };
if (clipY === null && clipX === null) { if (clipY === null && clipX === null) {
let newClipY = (sysinfo.windowHeight - clipHeight) * 0.5; let newClipY = (sysinfo.windowHeight - clipHeight) * 0.5;
let newClipX = (sysinfo.windowWidth - clipWidth) * 0.5; let newClipX = (sysinfo.windowWidth - clipWidth) * 0.5;
this.setDiffData({ this.setDiffData({
clipX: newClipX, clipX: newClipX,
clipY: newClipY clipY: newClipY
}) })
} else if (clipY !== null && clipX !== null) { } else if (clipY !== null && clipX !== null) {
cutDetectionPositionTop(); cutDetectionPositionTop();
cutDetectionPositionLeft(); cutDetectionPositionLeft();
} else if (clipY !== null && clipX === null) { } else if (clipY !== null && clipX === null) {
cutDetectionPositionTop(); cutDetectionPositionTop();
this.setDiffData({ this.setDiffData({
clipX: (sysinfo.windowWidth - clipWidth) / 2 clipX: (sysinfo.windowWidth - clipWidth) / 2
}) })
} else if (clipY === null && clipX !== null) { } else if (clipY === null && clipX !== null) {
cutDetectionPositionLeft(); cutDetectionPositionLeft();
this.setDiffData({ this.setDiffData({
clipY: (sysinfo.windowHeight - clipHeight) / 2 clipY: (sysinfo.windowHeight - clipHeight) / 2
}) })
} }
}, },
imgComputeSize(width, height) { imgComputeSize(width, height) {
const { imageWidth, imageHeight } = calcImageSize(width, height, this); const { imageWidth, imageHeight } = calcImageSize(width, height, this);
this.imageWidth = imageWidth; this.imageWidth = imageWidth;
this.imageHeight = imageHeight; this.imageHeight = imageHeight;
}, },
imgMarginDetectionScale(scale) { imgMarginDetectionScale(scale) {
if (!this.isLimitMove) return; if (!this.isLimitMove) return;
const currentScale = calcImageScale(this, scale); const currentScale = calcImageScale(this, scale);
this.imgMarginDetectionPosition(currentScale); this.imgMarginDetectionPosition(currentScale);
}, },
imgMarginDetectionPosition(scale) { imgMarginDetectionPosition(scale) {
if (!this.isLimitMove) return; if (!this.isLimitMove) return;
const { scale: currentScale, left, top } = calcImageOffset(this, scale); const { scale: currentScale, left, top } = calcImageOffset(this, scale);
this.setDiffData({ this.setDiffData({
imageLeft: left, imageLeft: left,
imageTop: top, imageTop: top,
scale: currentScale scale: currentScale
}) })
}, },
throttle() { throttle() {
this.setDiffData({ this.setDiffData({
throttleFlag: true throttleFlag: true
}) })
}, },
moveDuring() { moveDuring() {
clearTimeout(this.timeClipCenter); clearTimeout(this.timeClipCenter);
}, },
moveStop() { moveStop() {
clearTimeout(this.timeClipCenter); clearTimeout(this.timeClipCenter);
const timeClipCenter = setTimeout(() => { const timeClipCenter = setTimeout(() => {
if (!this.animation) { if (!this.animation) {
this.setDiffData({animation: true}) this.setDiffData({animation: true})
} }
this.setClipCenter(); this.setClipCenter();
}, 800); }, 800);
this.setDiffData({timeClipCenter}) this.setDiffData({timeClipCenter})
}, },
clipTouchStart(event) { clipTouchStart(event) {
// #ifdef H5 // #ifdef H5
event.preventDefault() event.preventDefault()
// #endif // #endif
if (!this.image) { if (!this.image) {
uni.showToast({ uni.showToast({
title: '请选择图片', title: '请选择图片',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
return; return;
} }
const currentX = event.touches[0].clientX; const currentX = event.touches[0].clientX;
const currentY = event.touches[0].clientY; const currentY = event.touches[0].clientY;
const { clipX, clipY, clipWidth, clipHeight } = this; const { clipX, clipY, clipWidth, clipHeight } = this;
const corner = determineDirection(clipX, clipY, clipWidth, clipHeight, currentX, currentY); const corner = determineDirection(clipX, clipY, clipWidth, clipHeight, currentX, currentY);
this.moveDuring(); this.moveDuring();
if(!corner) {return} if(!corner) {return}
this.clipStart = { this.clipStart = {
width: clipWidth, width: clipWidth,
height: clipHeight, height: clipHeight,
x: currentX, x: currentX,
y: currentY, y: currentY,
clipY, clipY,
clipX, clipX,
corner corner
}; };
this.flagClipTouch = true; this.flagClipTouch = true;
this.flagEndTouch = true; this.flagEndTouch = true;
}, },
clipTouchMove(event) { clipTouchMove(event) {
// #ifdef H5 // #ifdef H5
event.stopPropagation() event.stopPropagation()
event.preventDefault() event.preventDefault()
// #endif // #endif
if (!this.image) { if (!this.image) {
uni.showToast({ uni.showToast({
title: '请选择图片', title: '请选择图片',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
return; return;
} }
// 只针对单指点击做处理 // 只针对单指点击做处理
if (event.touches.length !== 1) { if (event.touches.length !== 1) {
return; return;
} }
const { flagClipTouch, throttleFlag } = this; const { flagClipTouch, throttleFlag } = this;
if (flagClipTouch && throttleFlag) { if (flagClipTouch && throttleFlag) {
const { isLockRatio, isLockHeight, isLockWidth } = this; const { isLockRatio, isLockHeight, isLockWidth } = this;
if (isLockRatio && (isLockWidth || isLockHeight)) return; if (isLockRatio && (isLockWidth || isLockHeight)) return;
this.setDiffData({ this.setDiffData({
throttleFlag: false throttleFlag: false
}) })
this.throttle(); this.throttle();
const clipData = clipTouchMoveOfCalculate(this, event); const clipData = clipTouchMoveOfCalculate(this, event);
if(clipData) { if(clipData) {
const { width, height, clipX, clipY } = clipData; const { width, height, clipX, clipY } = clipData;
if (!isLockWidth && !isLockHeight) { if (!isLockWidth && !isLockHeight) {
this.setDiffData({ this.setDiffData({
clipWidth: width, clipWidth: width,
clipHeight: height, clipHeight: height,
clipX, clipX,
clipY clipY
}) })
} else if (!isLockWidth) { } else if (!isLockWidth) {
this.setDiffData({ this.setDiffData({
clipWidth: width, clipWidth: width,
clipX clipX
}) })
} else if (!isLockHeight) { } else if (!isLockHeight) {
this.setDiffData({ this.setDiffData({
clipHeight: height, clipHeight: height,
clipY clipY
}) })
} }
this.imgMarginDetectionScale(); this.imgMarginDetectionScale();
} }
} }
}, },
clipTouchEnd() { clipTouchEnd() {
this.moveStop(); this.moveStop();
this.flagClipTouch = false; this.flagClipTouch = false;
}, },
imageTouchStart(e) { imageTouchStart(e) {
// #ifdef H5 // #ifdef H5
event.preventDefault() event.preventDefault()
// #endif // #endif
this.flagEndTouch = false; this.flagEndTouch = false;
const { imageLeft, imageTop } = this; const { imageLeft, imageTop } = this;
const clientXForLeft = e.touches[0].clientX; const clientXForLeft = e.touches[0].clientX;
const clientYForLeft = e.touches[0].clientY; const clientYForLeft = e.touches[0].clientY;
let touchRelative = []; let touchRelative = [];
if (e.touches.length === 1) { if (e.touches.length === 1) {
touchRelative[0] = { touchRelative[0] = {
x: clientXForLeft - imageLeft, x: clientXForLeft - imageLeft,
y: clientYForLeft - imageTop y: clientYForLeft - imageTop
}; };
this.touchRelative = touchRelative; this.touchRelative = touchRelative;
} else { } else {
const clientXForRight = e.touches[1].clientX; const clientXForRight = e.touches[1].clientX;
const clientYForRight = e.touches[1].clientY; const clientYForRight = e.touches[1].clientY;
let width = Math.abs(clientXForLeft - clientXForRight); let width = Math.abs(clientXForLeft - clientXForRight);
let height = Math.abs(clientYForLeft - clientYForRight); let height = Math.abs(clientYForLeft - clientYForRight);
const hypotenuseLength = calcPythagoreanTheorem(width, height); const hypotenuseLength = calcPythagoreanTheorem(width, height);
touchRelative = [ touchRelative = [
{ {
x: clientXForLeft - imageLeft, x: clientXForLeft - imageLeft,
y: clientYForLeft - imageTop y: clientYForLeft - imageTop
}, },
{ {
x: clientXForRight - imageLeft, x: clientXForRight - imageLeft,
y: clientYForRight - imageTop y: clientYForRight - imageTop
} }
]; ];
this.touchRelative = touchRelative; this.touchRelative = touchRelative;
this.hypotenuseLength = hypotenuseLength; this.hypotenuseLength = hypotenuseLength;
} }
}, },
imageTouchMove(e) { imageTouchMove(e) {
// #ifdef H5 // #ifdef H5
event.preventDefault() event.preventDefault()
// #endif // #endif
const { flagEndTouch, throttleFlag } = this; const { flagEndTouch, throttleFlag } = this;
if (flagEndTouch || !throttleFlag) return; if (flagEndTouch || !throttleFlag) return;
const clientXForLeft = e.touches[0].clientX; const clientXForLeft = e.touches[0].clientX;
const clientYForLeft = e.touches[0].clientY; const clientYForLeft = e.touches[0].clientY;
this.setDiffData({throttleFlag: false}) this.setDiffData({throttleFlag: false})
this.throttle(); this.throttle();
this.moveDuring(); this.moveDuring();
if (e.touches.length === 1) { if (e.touches.length === 1) {
const { left: imageLeft, top: imageTop} = imageTouchMoveOfCalcOffset(this, clientXForLeft, clientYForLeft); const { left: imageLeft, top: imageTop} = imageTouchMoveOfCalcOffset(this, clientXForLeft, clientYForLeft);
this.setDiffData({ this.setDiffData({
imageLeft, imageLeft,
imageTop imageTop
}) })
this.imgMarginDetectionPosition(); this.imgMarginDetectionPosition();
} else { } else {
const clientXForRight = e.touches[1].clientX; const clientXForRight = e.touches[1].clientX;
const clientYForRight = e.touches[1].clientY; const clientYForRight = e.touches[1].clientY;
let width = Math.abs(clientXForLeft - clientXForRight), let width = Math.abs(clientXForLeft - clientXForRight),
height = Math.abs(clientYForLeft - clientYForRight), height = Math.abs(clientYForLeft - clientYForRight),
hypotenuse = calcPythagoreanTheorem(width, height), hypotenuse = calcPythagoreanTheorem(width, height),
scale = this.scale * (hypotenuse / this.hypotenuseLength); scale = this.scale * (hypotenuse / this.hypotenuseLength);
if (this.isDisableScale) { if (this.isDisableScale) {
scale = 1; scale = 1;
} else { } else {
scale = scale <= this.minRatio ? this.minRatio : scale; scale = scale <= this.minRatio ? this.minRatio : scale;
scale = scale >= this.maxRatio ? this.maxRatio : scale; scale = scale >= this.maxRatio ? this.maxRatio : scale;
this.$emit('change', { this.$emit('change', {
width: this.imageWidth * scale, width: this.imageWidth * scale,
height: this.imageHeight * scale height: this.imageHeight * scale
}); });
} }
this.imgMarginDetectionScale(scale); this.imgMarginDetectionScale(scale);
this.hypotenuseLength = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)); this.hypotenuseLength = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
this.scale = scale; this.scale = scale;
} }
}, },
imageTouchEnd() { imageTouchEnd() {
this.setDiffData({ this.setDiffData({
flagEndTouch: true flagEndTouch: true
}) })
this.moveStop(); this.moveStop();
}, },
uploadImage() { uploadImage() {
const itemList = Object.entries(this.source) const itemList = Object.entries(this.source)
const sizeType = ['original', 'compressed'] const sizeType = ['original', 'compressed']
const success = ({tempFilePaths:a, tempFiles: b}) => { const success = ({tempFilePaths:a, tempFiles: b}) => {
this.image = a ? a[0] : b[0].path this.image = a ? a[0] : b[0].path
}; };
const _uploadImage = (type) => { const _uploadImage = (type) => {
if(type !== 'message') { if(type !== 'message') {
uni.chooseImage({ uni.chooseImage({
count: 1, count: 1,
sizeType, sizeType,
sourceType: [type], sourceType: [type],
success success
}); });
} }
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
if(type == 'message') { if(type == 'message') {
wx.chooseMessageFile({ wx.chooseMessageFile({
count: 1, count: 1,
type: 'image', type: 'image',
success success
}) })
} }
// #endif // #endif
} }
if(itemList.length > 1) { if(itemList.length > 1) {
uni.showActionSheet({ uni.showActionSheet({
itemList: itemList.map(v => v[1]), itemList: itemList.map(v => v[1]),
success: ({tapIndex: i}) => { success: ({tapIndex: i}) => {
_uploadImage(itemList[i][0]) _uploadImage(itemList[i][0])
} }
}) })
} else { } else {
_uploadImage(itemList[0][0]) _uploadImage(itemList[0][0])
} }
}, },
imageReset() { imageReset() {
const sys = this.sysinfo || uni.getSystemInfoSync(); const sys = this.sysinfo || uni.getSystemInfoSync();
this.scale = 1; this.scale = 1;
this.angle = 0; this.angle = 0;
this.imageTop = sys.windowHeight / 2; this.imageTop = sys.windowHeight / 2;
this.imageLeft = sys.windowWidth / 2; this.imageLeft = sys.windowWidth / 2;
}, },
imageLoad(e) { imageLoad(e) {
this.imageReset(); this.imageReset();
uni.hideLoading(); uni.hideLoading();
this.$emit('ready', e.detail); this.$emit('ready', e.detail);
}, },
rotate(event) { rotate(event) {
if (this.isDisableRotate) return; if (this.isDisableRotate) return;
if (!this.image) { if (!this.image) {
uni.showToast({ uni.showToast({
title: '请选择图片', title: '请选择图片',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
return; return;
} }
const { rotateAngle } = this; const { rotateAngle } = this;
const originAngle = this.angle const originAngle = this.angle
const type = event.currentTarget.dataset.type; const type = event.currentTarget.dataset.type;
if (type === 'along') { if (type === 'along') {
this.angle = originAngle + rotateAngle this.angle = originAngle + rotateAngle
} else { } else {
this.angle = originAngle - rotateAngle this.angle = originAngle - rotateAngle
} }
this.$emit('rotate', this.angle); this.$emit('rotate', this.angle);
}, },
confirm() { confirm() {
if (!this.image) { if (!this.image) {
uni.showToast({ uni.showToast({
title: '请选择图片', title: '请选择图片',
icon: 'none', icon: 'none',
duration: 3000 duration: 3000
}); });
return; return;
} }
uni.showLoading({ uni.showLoading({
title: '加载中' title: '加载中'
}); });
const { canvasHeight, canvasWidth, clipHeight, clipWidth, ctx, scale, imageLeft, imageTop, clipX, clipY, angle, scaleRatio: dpr, image, quality, fileType, type: imageType, canvasId } = this; const { canvasHeight, canvasWidth, clipHeight, clipWidth, ctx, scale, imageLeft, imageTop, clipX, clipY, angle, scaleRatio: dpr, image, quality, fileType, type: imageType, canvasId } = this;
const draw = () => { const draw = () => {
const imageWidth = this.imageWidth * scale * dpr; const imageWidth = this.imageWidth * scale * dpr;
const imageHeight = this.imageHeight * scale * dpr; const imageHeight = this.imageHeight * scale * dpr;
const xpos = imageLeft - clipX; const xpos = imageLeft - clipX;
const ypos = imageTop - clipY; const ypos = imageTop - clipY;
ctx.translate(xpos * dpr, ypos * dpr); ctx.translate(xpos * dpr, ypos * dpr);
ctx.rotate((angle * Math.PI) / 180); ctx.rotate((angle * Math.PI) / 180);
ctx.drawImage(image, -imageWidth / 2, -imageHeight / 2, imageWidth, imageHeight); ctx.drawImage(image, -imageWidth / 2, -imageHeight / 2, imageWidth, imageHeight);
ctx.draw(false, () => { ctx.draw(false, () => {
const width = clipWidth * dpr const width = clipWidth * dpr
const height = clipHeight * dpr const height = clipHeight * dpr
let params = { let params = {
x: 0, x: 0,
y: 0, y: 0,
width, width,
height, height,
destWidth: width, destWidth: width,
destHeight: height, destHeight: height,
canvasId: canvasId, canvasId: canvasId,
fileType, fileType,
quality, quality,
success: (res) => { success: (res) => {
data.url = res.tempFilePath; data.url = res.tempFilePath;
uni.hideLoading(); uni.hideLoading();
this.$emit('success', data); this.$emit('success', data);
this.$emit('input', false) this.$emit('input', false)
}, },
fail: (error) => { fail: (error) => {
console.error('error', error) console.error('error', error)
this.$emit('fail', error); this.$emit('fail', error);
this.$emit('input', false) this.$emit('input', false)
} }
}; };
let data = { let data = {
url: '', url: '',
width, width,
height height
}; };
uni.canvasToTempFilePath(params, this) uni.canvasToTempFilePath(params, this)
}); });
}; };
if (canvasWidth !== clipWidth || canvasHeight !== clipHeight) { if (canvasWidth !== clipWidth || canvasHeight !== clipHeight) {
this.canvasWidth = clipWidth; this.canvasWidth = clipWidth;
this.canvasHeight = clipHeight; this.canvasHeight = clipHeight;
ctx.draw(); ctx.draw();
this.$nextTick(() => { this.$nextTick(() => {
setTimeout(() => { setTimeout(() => {
draw(); draw();
}, 100); }, 100);
}) })
} else { } else {
draw(); draw();
} }
}, },
cancel() { cancel() {
this.$emit('cancel', false) this.$emit('cancel', false)
this.$emit('input', false) this.$emit('input', false)
}, },
} }
}; };
</script> </script>
<style scoped> <style scoped>
@import './index' @import './index'
</style> </style>
\ No newline at end of file
<!-- 设置密码 --> <!-- 设置密码 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<match-media :min-width="690"> <match-media :min-width="690">
<view class="login-logo"> <view class="login-logo">
<image :src="logo"></image> <image :src="logo"></image>
</view> </view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title title-box ">设置密码</text> <text class="title title-box ">设置密码</text>
</match-media> </match-media>
<uni-forms class="set-password-form" ref="form" :value="formData" err-show-type="toast"> <uni-forms class="set-password-form" ref="form" :value="formData" err-show-type="toast">
<text class="tip">输入密码</text> <text class="tip">输入密码</text>
<uni-forms-item name="newPassword"> <uni-forms-item name="newPassword">
<uni-easyinput :focus="focusNewPassword" @blur="focusNewPassword = false" class="input-box" <uni-easyinput :focus="focusNewPassword" @blur="focusNewPassword = false" class="input-box"
type="password" :inputBorder="false" v-model="formData.newPassword" placeholder="请输入密码"> type="password" :inputBorder="false" v-model="formData.newPassword" placeholder="请输入密码">
</uni-easyinput> </uni-easyinput>
</uni-forms-item> </uni-forms-item>
<text class="tip">再次输入密码</text> <text class="tip">再次输入密码</text>
<uni-forms-item name="newPassword2"> <uni-forms-item name="newPassword2">
<uni-easyinput :focus="focusNewPassword2" @blur="focusNewPassword2 = false" class="input-box" <uni-easyinput :focus="focusNewPassword2" @blur="focusNewPassword2 = false" class="input-box"
type="password" :inputBorder="false" v-model="formData.newPassword2" placeholder="请再次输入新密码"> type="password" :inputBorder="false" v-model="formData.newPassword2" placeholder="请再次输入新密码">
</uni-easyinput> </uni-easyinput>
</uni-forms-item> </uni-forms-item>
<uni-id-pages-sms-form v-model="formData.code" type="set-pwd-by-sms" ref="smsCode" :phone="userInfo.mobile"> <uni-id-pages-sms-form v-model="formData.code" type="set-pwd-by-sms" ref="smsCode" :phone="userInfo.mobile">
</uni-id-pages-sms-form> </uni-id-pages-sms-form>
<view class="link-box"> <view class="link-box">
<button class="uni-btn send-btn" type="primary" @click="submit">确认</button> <button class="uni-btn send-btn" type="primary" @click="submit">确认</button>
<button v-if="allowSkip" class="uni-btn send-btn" type="default" @click="skip">跳过</button> <button v-if="allowSkip" class="uni-btn send-btn" type="default" @click="skip">跳过</button>
</view> </view>
</uni-forms> </uni-forms>
<uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="set-pwd-by-sms" ref="popup"></uni-popup-captcha> <uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="set-pwd-by-sms" ref="popup"></uni-popup-captcha>
</view> </view>
</template> </template>
<script> <script>
import passwordMod from '@/uni_modules/uni-id-pages/common/password.js' import passwordMod from '@/uni_modules/uni-id-pages/common/password.js'
import {store, mutations} from '@/uni_modules/uni-id-pages/common/store.js' import {store, mutations} from '@/uni_modules/uni-id-pages/common/store.js'
import config from '@/uni_modules/uni-id-pages/config.js' import config from '@/uni_modules/uni-id-pages/config.js'
const uniIdCo = uniCloud.importObject("uni-id-co", { const uniIdCo = uniCloud.importObject("uni-id-co", {
customUI:true customUI:true
}) })
export default { export default {
name: "set-pwd.vue", name: "set-pwd.vue",
data () { data () {
return { return {
uniIdRedirectUrl: '', uniIdRedirectUrl: '',
loginType: '', loginType: '',
logo: '/static/logo.png', logo: '/static/logo.png',
focusNewPassword: false, focusNewPassword: false,
focusNewPassword2: false, focusNewPassword2: false,
allowSkip: false, allowSkip: false,
formData: { formData: {
code: "", code: "",
captcha: "", captcha: "",
newPassword: "", newPassword: "",
newPassword2: "" newPassword2: ""
}, },
rules: passwordMod.getPwdRules('newPassword', 'newPassword2') rules: passwordMod.getPwdRules('newPassword', 'newPassword2')
} }
}, },
computed: { computed: {
userInfo () { userInfo () {
return store.userInfo return store.userInfo
} }
}, },
onReady() { onReady() {
this.$refs.form.setRules(this.rules) this.$refs.form.setRules(this.rules)
}, },
onLoad (e) { onLoad (e) {
this.uniIdRedirectUrl = e.uniIdRedirectUrl this.uniIdRedirectUrl = e.uniIdRedirectUrl
this.loginType = e.loginType this.loginType = e.loginType
if (config.setPasswordAfterLogin && config.setPasswordAfterLogin?.allowSkip) { if (config.setPasswordAfterLogin && config.setPasswordAfterLogin?.allowSkip) {
this.allowSkip = true this.allowSkip = true
} }
}, },
methods: { methods: {
submit () { submit () {
if(! /^\d{6}$/.test(this.formData.code)){ if(! /^\d{6}$/.test(this.formData.code)){
this.$refs.smsCode.focusSmsCodeInput = true this.$refs.smsCode.focusSmsCodeInput = true
return uni.showToast({ return uni.showToast({
title: '验证码格式不正确', title: '验证码格式不正确',
icon: 'none' icon: 'none'
}); });
} }
this.$refs.form.validate() this.$refs.form.validate()
.then(res => { .then(res => {
uniIdCo.setPwd({ uniIdCo.setPwd({
password: this.formData.newPassword, password: this.formData.newPassword,
code: this.formData.code, code: this.formData.code,
captcha: this.formData.captcha captcha: this.formData.captcha
}).then(e => { }).then(e => {
uni.showModal({ uni.showModal({
content: '密码设置成功', content: '密码设置成功',
showCancel: false, showCancel: false,
success: () => { success: () => {
mutations.loginBack({ mutations.loginBack({
uniIdRedirectUrl: this.uniIdRedirectUrl, uniIdRedirectUrl: this.uniIdRedirectUrl,
loginType: this.loginType loginType: this.loginType
}) })
} }
}); });
}).catch(e => { }).catch(e => {
uni.showModal({ uni.showModal({
content: e.message, content: e.message,
showCancel: false showCancel: false
}); });
}) })
}).catch(e => { }).catch(e => {
if (e.errCode == 'uni-id-captcha-required') { if (e.errCode == 'uni-id-captcha-required') {
this.$refs.popup.open() this.$refs.popup.open()
} else { } else {
console.log(e.errMsg); console.log(e.errMsg);
// console.log(e.errCode); // console.log(e.errCode);
} }
}).finally(e => { }).finally(e => {
this.formData.captcha = '' this.formData.captcha = ''
}) })
}, },
skip () { skip () {
mutations.loginBack(this.uniIdRedirectUrl) mutations.loginBack(this.uniIdRedirectUrl)
} }
} }
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
.uni-btn[type="default"] { .uni-btn[type="default"] {
color: inherit!important; color: inherit!important;
} }
.uni-content ::v-deep .uni-forms-item { .uni-content ::v-deep .uni-forms-item {
margin-bottom: 10px; margin-bottom: 10px;
} }
.popup-captcha { .popup-captcha {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
padding: 20rpx; padding: 20rpx;
background-color: #FFF; background-color: #FFF;
border-radius: 2px; border-radius: 2px;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
} }
.popup-captcha .title { .popup-captcha .title {
font-weight: normal; font-weight: normal;
padding: 0; padding: 0;
padding-bottom: 15px; padding-bottom: 15px;
color: #666; color: #666;
} }
.popup-captcha .close { .popup-captcha .close {
position: absolute; position: absolute;
bottom: -40px; bottom: -40px;
margin-left: -13px; margin-left: -13px;
left: 50%; left: 50%;
} }
.popup-captcha .uni-btn { .popup-captcha .uni-btn {
margin: 0; margin: 0;
} }
</style> </style>
<!-- 用户资料页 --> <!-- 用户资料页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<view class="avatar"> <view class="avatar">
<uni-id-pages-avatar width="260rpx" height="260rpx"></uni-id-pages-avatar> <uni-id-pages-avatar width="260rpx" height="260rpx"></uni-id-pages-avatar>
</view> </view>
<uni-list> <uni-list>
<uni-list-item class="item" @click="setNickname('')" title="昵称" :rightText="userInfo.nickname||'未设置'" link> <uni-list-item class="item" @click="setNickname('')" title="昵称" :rightText="userInfo.nickname||'未设置'" link>
</uni-list-item> </uni-list-item>
<uni-list-item class="item" @click="bindMobile" title="手机号" :rightText="userInfo.mobile||'未绑定'" link> <uni-list-item class="item" @click="bindMobile" title="手机号" :rightText="userInfo.mobile||'未绑定'" link>
</uni-list-item> </uni-list-item>
<uni-list-item v-if="userInfo.email" class="item" title="电子邮箱" :rightText="userInfo.email"> <uni-list-item v-if="userInfo.email" class="item" title="电子邮箱" :rightText="userInfo.email">
</uni-list-item> </uni-list-item>
<uni-list-item v-if="hasPwd" class="item" @click="changePassword" title="修改密码" link> <uni-list-item v-if="hasPwd" class="item" @click="changePassword" title="修改密码" link>
</uni-list-item> </uni-list-item>
</uni-list> </uni-list>
<uni-list class="mt10"> <uni-list class="mt10">
<uni-list-item @click="deactivate" title="注销账号" link="navigateTo"></uni-list-item> <uni-list-item @click="deactivate" title="注销账号" link="navigateTo"></uni-list-item>
</uni-list> </uni-list>
<uni-popup ref="dialog" type="dialog"> <uni-popup ref="dialog" type="dialog">
<uni-popup-dialog mode="input" :value="userInfo.nickname" @confirm="setNickname" title="设置昵称" <uni-popup-dialog mode="input" :value="userInfo.nickname" @confirm="setNickname" title="设置昵称"
placeholder="请输入要设置的昵称"> placeholder="请输入要设置的昵称">
</uni-popup-dialog> </uni-popup-dialog>
</uni-popup> </uni-popup>
<uni-id-pages-bind-mobile ref="bind-mobile-by-sms" @success="bindMobileSuccess"></uni-id-pages-bind-mobile> <uni-id-pages-bind-mobile ref="bind-mobile-by-sms" @success="bindMobileSuccess"></uni-id-pages-bind-mobile>
<template v-if="showLoginManage"> <template v-if="showLoginManage">
<button v-if="userInfo._id" @click="logout">退出登录</button> <button v-if="userInfo._id" @click="logout">退出登录</button>
<button v-else @click="login">去登录</button> <button v-else @click="login">去登录</button>
</template> </template>
</view> </view>
</template> </template>
<script> <script>
const db = uniCloud.database(); const db = uniCloud.database();
const usersTable = db.collection('uni-id-users') const usersTable = db.collection('uni-id-users')
const uniIdCo = uniCloud.importObject("uni-id-co") const uniIdCo = uniCloud.importObject("uni-id-co")
import { import {
store, store,
mutations mutations
} from '@/uni_modules/uni-id-pages/common/store.js' } from '@/uni_modules/uni-id-pages/common/store.js'
export default { export default {
computed: { computed: {
userInfo() { userInfo() {
return store.userInfo return store.userInfo
} }
}, },
data() { data() {
return { return {
univerifyStyle: { univerifyStyle: {
authButton: { authButton: {
"title": "本机号码一键绑定", // 授权按钮文案 "title": "本机号码一键绑定", // 授权按钮文案
}, },
otherLoginButton: { otherLoginButton: {
"title": "其他号码绑定", "title": "其他号码绑定",
} }
}, },
// userInfo: { // userInfo: {
// mobile:'', // mobile:'',
// nickname:'' // nickname:''
// }, // },
hasPwd:false, hasPwd:false,
showLoginManage:false//通过页面传参隐藏登录&退出登录按钮 showLoginManage:false//通过页面传参隐藏登录&退出登录按钮
} }
}, },
async onShow() { async onShow() {
this.univerifyStyle.authButton.title = "本机号码一键绑定" this.univerifyStyle.authButton.title = "本机号码一键绑定"
this.univerifyStyle.otherLoginButton.title = "其他号码绑定" this.univerifyStyle.otherLoginButton.title = "其他号码绑定"
}, },
async onLoad(e) { async onLoad(e) {
if(e.showLoginManage){ if(e.showLoginManage){
this.showLoginManage = true//通过页面传参隐藏登录&退出登录按钮 this.showLoginManage = true//通过页面传参隐藏登录&退出登录按钮
} }
//判断当前用户是否有密码,否则就不显示密码修改功能 //判断当前用户是否有密码,否则就不显示密码修改功能
let res = await uniIdCo.getAccountInfo() let res = await uniIdCo.getAccountInfo()
this.hasPwd = res.isPasswordSet this.hasPwd = res.isPasswordSet
}, },
methods: { methods: {
login() { login() {
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd', url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
complete: (e) => { complete: (e) => {
// console.log(e); // console.log(e);
} }
}) })
}, },
logout(){ logout(){
mutations.logout() mutations.logout()
}, },
bindMobileSuccess(){ bindMobileSuccess(){
mutations.updateUserInfo() mutations.updateUserInfo()
}, },
changePassword(){ changePassword(){
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd', url: '/uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd',
complete: (e) => { complete: (e) => {
// console.log(e); // console.log(e);
} }
}) })
}, },
bindMobile() { bindMobile() {
// #ifdef APP-PLUS // #ifdef APP-PLUS
uni.preLogin({ uni.preLogin({
provider: 'univerify', provider: 'univerify',
success: this.univerify(), //预登录成功 success: this.univerify(), //预登录成功
fail: (res) => { // 预登录失败 fail: (res) => { // 预登录失败
// 不显示一键登录选项(或置灰) // 不显示一键登录选项(或置灰)
console.log(res) console.log(res)
this.bindMobileBySmsCode() this.bindMobileBySmsCode()
} }
}) })
// #endif // #endif
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
this.$refs['bind-mobile-by-sms'].open() this.$refs['bind-mobile-by-sms'].open()
// #endif // #endif
// #ifdef H5 // #ifdef H5
//...去用验证码绑定 //...去用验证码绑定
this.bindMobileBySmsCode() this.bindMobileBySmsCode()
// #endif // #endif
}, },
univerify() { univerify() {
uni.login({ uni.login({
"provider": 'univerify', "provider": 'univerify',
"univerifyStyle": this.univerifyStyle, "univerifyStyle": this.univerifyStyle,
success: async e => { success: async e => {
// console.log(e.authResult); // console.log(e.authResult);
uniIdCo.bindMobileByUniverify(e.authResult).then(res => { uniIdCo.bindMobileByUniverify(e.authResult).then(res => {
// console.log(res); // console.log(res);
mutations.updateUserInfo() mutations.updateUserInfo()
}).catch(e => { }).catch(e => {
console.log(e); console.log(e);
}).finally(e=>{ }).finally(e=>{
// console.log(e); // console.log(e);
uni.closeAuthView() uni.closeAuthView()
}) })
}, },
fail: (err) => { fail: (err) => {
console.log(err); console.log(err);
if (err.code == '30002' || err.code == '30001') { if (err.code == '30002' || err.code == '30001') {
this.bindMobileBySmsCode() this.bindMobileBySmsCode()
} }
} }
}) })
}, },
bindMobileBySmsCode() { bindMobileBySmsCode() {
uni.navigateTo({ uni.navigateTo({
url: './bind-mobile/bind-mobile' url: './bind-mobile/bind-mobile'
}) })
}, },
setNickname(nickname) { setNickname(nickname) {
// console.log(nickname); // console.log(nickname);
if (nickname) { if (nickname) {
mutations.updateUserInfo({nickname}) mutations.updateUserInfo({nickname})
this.$refs.dialog.close() this.$refs.dialog.close()
} else { } else {
this.$refs.dialog.open() this.$refs.dialog.open()
} }
}, },
deactivate(){ deactivate(){
uni.navigateTo({ uni.navigateTo({
url:"/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate" url:"/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate"
}) })
}, },
async bindThirdAccount(provider) { async bindThirdAccount(provider) {
const uniIdCo = uniCloud.importObject("uni-id-co") const uniIdCo = uniCloud.importObject("uni-id-co")
const bindField = { const bindField = {
weixin: 'wx_openid', weixin: 'wx_openid',
alipay: 'ali_openid', alipay: 'ali_openid',
apple: 'apple_openid', apple: 'apple_openid',
qq: 'qq_openid' qq: 'qq_openid'
}[provider.toLowerCase()] }[provider.toLowerCase()]
if (this.userInfo[bindField]) { if (this.userInfo[bindField]) {
await uniIdCo['unbind' + provider]() await uniIdCo['unbind' + provider]()
await mutations.updateUserInfo() await mutations.updateUserInfo()
} else { } else {
uni.login({ uni.login({
provider: provider.toLowerCase(), provider: provider.toLowerCase(),
onlyAuthorize: true, onlyAuthorize: true,
success: async e => { success: async e => {
const res = await uniIdCo['bind' + provider]({code: e.code}) const res = await uniIdCo['bind' + provider]({code: e.code})
if (res.errCode) { if (res.errCode) {
uni.showToast({ uni.showToast({
title: res.errMsg || '绑定失败', title: res.errMsg || '绑定失败',
duration: 3000 duration: 3000
}) })
} }
await mutations.updateUserInfo() await mutations.updateUserInfo()
}, },
fail: async (err) => { fail: async (err) => {
console.log(err); console.log(err);
uni.hideLoading() uni.hideLoading()
} }
}) })
} }
} }
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
.uni-content { .uni-content {
padding: 0; padding: 0;
} }
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
view { view {
display: flex; display: flex;
box-sizing: border-box; box-sizing: border-box;
flex-direction: column; flex-direction: column;
} }
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
.uni-content { .uni-content {
padding: 0; padding: 0;
max-width: 690px; max-width: 690px;
margin-left: calc(50% - 345px); margin-left: calc(50% - 345px);
border: none; border: none;
max-height: none; max-height: none;
border-radius: 0; border-radius: 0;
box-shadow: none; box-shadow: none;
} }
} }
/* #endif */ /* #endif */
.avatar { .avatar {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin: 22px 0; margin: 22px 0;
width: 100%; width: 100%;
} }
.item { .item {
flex: 1; flex: 1;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
button { button {
margin: 10%; margin: 10%;
margin-top: 40px; margin-top: 40px;
border-radius: 0; border-radius: 0;
background-color: #FFFFFF; background-color: #FFFFFF;
width: 80%; width: 80%;
} }
.mt10{ .mt10{
margin-top: 10px; margin-top: 10px;
} }
</style> </style>
# 文档已移至uni-id-pages文档[https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html) # 文档已移至uni-id-pages文档[https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html)
关于插件更新的说明: 关于插件更新的说明:
所有uni_modules,在HBuilderX里点右键都可以直接升级。或者在插件市场导入覆盖。 所有uni_modules,在HBuilderX里点右键都可以直接升级。或者在插件市场导入覆盖。
覆盖时HBuilderX会弹出代码差异比对,可以决定接受哪些更改、拒绝哪些更改。 覆盖时HBuilderX会弹出代码差异比对,可以决定接受哪些更改、拒绝哪些更改。
当拒绝局部修改时,注意可能产生兼容性问题。 当拒绝局部修改时,注意可能产生兼容性问题。
你需要二次开发uni-id-pages的前端页面, 你需要二次开发uni-id-pages的前端页面,
- 如果改动不大,那么每次更新uni-id-pages时,在HBuilderX的对比界面对比一下就好 - 如果改动不大,那么每次更新uni-id-pages时,在HBuilderX的对比界面对比一下就好
- 如果改动较大,建议复制一套前端页面到自己工程的pages目录下,pages.json里只引用根目录pages下的页面,不引用uni_modules下的页面。然后每次uni-id-pages更新,你对比下比上一版uni-id-pages改了什么,看你是否需要再合并到你自己的pages里。pages.json里不引用uni_modules里的页面的话,打包时不会把这些页面打包进去,不影响发行后的包体积 - 如果改动较大,建议复制一套前端页面到自己工程的pages目录下,pages.json里只引用根目录pages下的页面,不引用uni_modules下的页面。然后每次uni-id-pages更新,你对比下比上一版uni-id-pages改了什么,看你是否需要再合并到你自己的pages里。pages.json里不引用uni_modules里的页面的话,打包时不会把这些页面打包进去,不影响发行后的包体积
\ No newline at end of file
{ {
"bsonType": "object", "bsonType": "object",
"required": [], "required": [],
"permission": { "permission": {
"read": false, "read": false,
"create": true, "create": true,
"update": false, "update": false,
"delete": false "delete": false
}, },
"properties": { "properties": {
"_id": { "_id": {
"description": "ID,系统自动生成" "description": "ID,系统自动生成"
}, },
"appid": { "appid": {
"bsonType": "string", "bsonType": "string",
"description": "DCloud appid" "description": "DCloud appid"
}, },
"device_id": { "device_id": {
"bsonType": "string", "bsonType": "string",
"description": "设备唯一标识" "description": "设备唯一标识"
}, },
"vendor": { "vendor": {
"bsonType": "string", "bsonType": "string",
"description": "设备厂商" "description": "设备厂商"
}, },
"push_clientid": { "push_clientid": {
"bsonType": "string", "bsonType": "string",
"description": "推送设备客户端标识" "description": "推送设备客户端标识"
}, },
"imei": { "imei": {
"bsonType": "string", "bsonType": "string",
"description": "国际移动设备识别码IMEI(International Mobile Equipment Identity)" "description": "国际移动设备识别码IMEI(International Mobile Equipment Identity)"
}, },
"oaid": { "oaid": {
"bsonType": "string", "bsonType": "string",
"description": "移动智能设备标识公共服务平台提供的匿名设备标识符(OAID)" "description": "移动智能设备标识公共服务平台提供的匿名设备标识符(OAID)"
}, },
"idfa": { "idfa": {
"bsonType": "string", "bsonType": "string",
"description": "iOS平台配置应用使用广告标识(IDFA)" "description": "iOS平台配置应用使用广告标识(IDFA)"
}, },
"imsi": { "imsi": {
"bsonType": "string", "bsonType": "string",
"description": "国际移动用户识别码(International Mobile Subscriber Identification Number)" "description": "国际移动用户识别码(International Mobile Subscriber Identification Number)"
}, },
"model": { "model": {
"bsonType": "string", "bsonType": "string",
"description": "设备型号" "description": "设备型号"
}, },
"platform": { "platform": {
"bsonType": "string", "bsonType": "string",
"description": "平台类型" "description": "平台类型"
}, },
"uni_platform": { "uni_platform": {
"bsonType": "string", "bsonType": "string",
"description": "uni-app 运行平台,与条件编译平台相同。" "description": "uni-app 运行平台,与条件编译平台相同。"
}, },
"os_name": { "os_name": {
"bsonType": "string", "bsonType": "string",
"description": "ios|android|windows|mac|linux " "description": "ios|android|windows|mac|linux "
}, },
"os_version": { "os_version": {
"bsonType": "string", "bsonType": "string",
"description": "操作系统版本号 " "description": "操作系统版本号 "
}, },
"os_language": { "os_language": {
"bsonType": "string", "bsonType": "string",
"description": "操作系统语言 " "description": "操作系统语言 "
}, },
"os_theme": { "os_theme": {
"bsonType": "string", "bsonType": "string",
"description": "操作系统主题 light|dark" "description": "操作系统主题 light|dark"
}, },
"pixel_ratio": { "pixel_ratio": {
"bsonType": "string", "bsonType": "string",
"description": "设备像素比 " "description": "设备像素比 "
}, },
"network_model": { "network_model": {
"bsonType": "string", "bsonType": "string",
"description": "设备网络型号wifi\/3G\/4G\/" "description": "设备网络型号wifi\/3G\/4G\/"
}, },
"window_width": { "window_width": {
"bsonType": "string", "bsonType": "string",
"description": "设备窗口宽度 " "description": "设备窗口宽度 "
}, },
"window_height": { "window_height": {
"bsonType": "string", "bsonType": "string",
"description": "设备窗口高度" "description": "设备窗口高度"
}, },
"screen_width": { "screen_width": {
"bsonType": "string", "bsonType": "string",
"description": "设备屏幕宽度" "description": "设备屏幕宽度"
}, },
"screen_height": { "screen_height": {
"bsonType": "string", "bsonType": "string",
"description": "设备屏幕高度" "description": "设备屏幕高度"
}, },
"rom_name": { "rom_name": {
"bsonType": "string", "bsonType": "string",
"description": "rom 名称" "description": "rom 名称"
}, },
"rom_version": { "rom_version": {
"bsonType": "string", "bsonType": "string",
"description": "rom 版本" "description": "rom 版本"
}, },
"location_latitude": { "location_latitude": {
"bsonType": "double", "bsonType": "double",
"description": "纬度" "description": "纬度"
}, },
"location_longitude": { "location_longitude": {
"bsonType": "double", "bsonType": "double",
"description": "经度" "description": "经度"
}, },
"location_country": { "location_country": {
"bsonType": "string", "bsonType": "string",
"description": "国家" "description": "国家"
}, },
"location_province": { "location_province": {
"bsonType": "string", "bsonType": "string",
"description": "省份" "description": "省份"
}, },
"location_city": { "location_city": {
"bsonType": "string", "bsonType": "string",
"description": "城市" "description": "城市"
}, },
"create_date": { "create_date": {
"bsonType": "timestamp", "bsonType": "timestamp",
"description": "创建时间", "description": "创建时间",
"forceDefaultValue": { "forceDefaultValue": {
"$env": "now" "$env": "now"
} }
}, },
"last_update_date": { "last_update_date": {
"bsonType": "timestamp", "bsonType": "timestamp",
"description": "最后一次修改时间", "description": "最后一次修改时间",
"forceDefaultValue": { "forceDefaultValue": {
"$env": "now" "$env": "now"
} }
} }
}, },
"version": "0.0.1" "version": "0.0.1"
} }
\ No newline at end of file
{ {
"bsonType": "object", "bsonType": "object",
"required": [ "required": [
"user_id" "user_id"
], ],
"properties": { "properties": {
"_id": { "_id": {
"description": "ID,系统自动生成" "description": "ID,系统自动生成"
}, },
"user_id": { "user_id": {
"bsonType": "string", "bsonType": "string",
"description": "用户id,参考uni-id-users表" "description": "用户id,参考uni-id-users表"
}, },
"ua": { "ua": {
"bsonType": "string", "bsonType": "string",
"description": "userAgent" "description": "userAgent"
}, },
"uuid": { "uuid": {
"bsonType": "string", "bsonType": "string",
"description": "设备唯一标识(需要加密存储)" "description": "设备唯一标识(需要加密存储)"
}, },
"os_name": { "os_name": {
"bsonType": "string", "bsonType": "string",
"description": "ios|android|windows|mac|linux " "description": "ios|android|windows|mac|linux "
}, },
"os_version": { "os_version": {
"bsonType": "string", "bsonType": "string",
"description": "操作系统版本号 " "description": "操作系统版本号 "
}, },
"os_language": { "os_language": {
"bsonType": "string", "bsonType": "string",
"description": "操作系统语言 " "description": "操作系统语言 "
}, },
"os_theme": { "os_theme": {
"bsonType": "string", "bsonType": "string",
"description": "操作系统主题 light|dark" "description": "操作系统主题 light|dark"
}, },
"vendor": { "vendor": {
"bsonType": "string", "bsonType": "string",
"description": "设备厂商" "description": "设备厂商"
}, },
"push_clientid": { "push_clientid": {
"bsonType": "string", "bsonType": "string",
"description": "推送设备客户端标识" "description": "推送设备客户端标识"
}, },
"imei": { "imei": {
"bsonType": "string", "bsonType": "string",
"description": "国际移动设备识别码IMEI(International Mobile Equipment Identity)" "description": "国际移动设备识别码IMEI(International Mobile Equipment Identity)"
}, },
"oaid": { "oaid": {
"bsonType": "string", "bsonType": "string",
"description": "移动智能设备标识公共服务平台提供的匿名设备标识符(OAID)" "description": "移动智能设备标识公共服务平台提供的匿名设备标识符(OAID)"
}, },
"idfa": { "idfa": {
"bsonType": "string", "bsonType": "string",
"description": "iOS平台配置应用使用广告标识(IDFA)" "description": "iOS平台配置应用使用广告标识(IDFA)"
}, },
"model": { "model": {
"bsonType": "string", "bsonType": "string",
"description": "设备型号" "description": "设备型号"
}, },
"platform": { "platform": {
"bsonType": "string", "bsonType": "string",
"description": "平台类型" "description": "平台类型"
}, },
"create_date": { "create_date": {
"bsonType": "timestamp", "bsonType": "timestamp",
"description": "创建时间", "description": "创建时间",
"forceDefaultValue": { "forceDefaultValue": {
"$env": "now" "$env": "now"
} }
}, },
"last_active_date": { "last_active_date": {
"bsonType": "timestamp", "bsonType": "timestamp",
"description": "最后登录时间" "description": "最后登录时间"
}, },
"last_active_ip": { "last_active_ip": {
"bsonType": "string", "bsonType": "string",
"description": "最后登录IP" "description": "最后登录IP"
} }
}, },
"version": "0.0.1" "version": "0.0.1"
} }
\ No newline at end of file
{ {
"bsonType": "object", "bsonType": "object",
"required": ["user_id"], "required": ["user_id"],
"permission": { "permission": {
"read": "'READ_UNI_ID_LOG' in auth.permission" "read": "'READ_UNI_ID_LOG' in auth.permission"
}, },
"properties": { "properties": {
"_id": { "_id": {
"description": "ID,系统自动生成" "description": "ID,系统自动生成"
}, },
"create_date": { "create_date": {
"bsonType": "timestamp", "bsonType": "timestamp",
"description": "创建时间", "description": "创建时间",
"forceDefaultValue": { "forceDefaultValue": {
"$env": "now" "$env": "now"
} }
}, },
"device_uuid": { "device_uuid": {
"bsonType": "string", "bsonType": "string",
"description": "设备唯一标识" "description": "设备唯一标识"
}, },
"ip": { "ip": {
"bsonType": "string", "bsonType": "string",
"description": "ip地址" "description": "ip地址"
}, },
"state": { "state": {
"bsonType": "int", "bsonType": "int",
"description": "结果:0 失败、1 成功" "description": "结果:0 失败、1 成功"
}, },
"type": { "type": {
"bsonType": "string", "bsonType": "string",
"description": "操作类型", "description": "操作类型",
"enum": [ "enum": [
"logout", "logout",
"login", "login",
"register", "register",
"reset-pwd", "reset-pwd",
"bind-mobile", "bind-mobile",
"bind-weixin", "bind-weixin",
"bind-qq", "bind-qq",
"bind-apple", "bind-apple",
"bind-alipay" "bind-alipay"
] ]
}, },
"ua": { "ua": {
"bsonType": "string", "bsonType": "string",
"description": "userAgent" "description": "userAgent"
}, },
"user_id": { "user_id": {
"bsonType": "string", "bsonType": "string",
"foreignKey": "uni-id-users._id", "foreignKey": "uni-id-users._id",
"description": "用户id,参考uni-id-users表" "description": "用户id,参考uni-id-users表"
}, },
"username": { "username": {
"bsonType": "string", "bsonType": "string",
"description": "用户名" "description": "用户名"
}, },
"email": { "email": {
"bsonType": "string", "bsonType": "string",
"description": "邮箱" "description": "邮箱"
}, },
"mobile": { "mobile": {
"bsonType": "string", "bsonType": "string",
"description": "手机号" "description": "手机号"
}, },
"appid": { "appid": {
"bsonType": "string", "bsonType": "string",
"description": "客户端DCloud AppId" "description": "客户端DCloud AppId"
} }
} }
} }
{ {
"bsonType": "object", "bsonType": "object",
"required": ["permission_id", "permission_name"], "required": ["permission_id", "permission_name"],
"permission": { "permission": {
"read": "'READ_UNI_ID_PERMISSIONS' in auth.permission", "read": "'READ_UNI_ID_PERMISSIONS' in auth.permission",
"create": "'CREATE_UNI_ID_PERMISSIONS' in auth.permission", "create": "'CREATE_UNI_ID_PERMISSIONS' in auth.permission",
"update": "'UPDATE_UNI_ID_PERMISSIONS' in auth.permission", "update": "'UPDATE_UNI_ID_PERMISSIONS' in auth.permission",
"delete": "'DELETE_UNI_ID_PERMISSIONS' in auth.permission" "delete": "'DELETE_UNI_ID_PERMISSIONS' in auth.permission"
}, },
"properties": { "properties": {
"_id": { "_id": {
"description": "存储文档 ID,系统自动生成" "description": "存储文档 ID,系统自动生成"
}, },
"comment": { "comment": {
"bsonType": "string", "bsonType": "string",
"component": { "component": {
"name": "textarea" "name": "textarea"
}, },
"description": "备注", "description": "备注",
"label": "备注", "label": "备注",
"title": "备注", "title": "备注",
"trim": "both" "trim": "both"
}, },
"create_date": { "create_date": {
"bsonType": "timestamp", "bsonType": "timestamp",
"description": "创建时间", "description": "创建时间",
"forceDefaultValue": { "forceDefaultValue": {
"$env": "now" "$env": "now"
} }
}, },
"permission_id": { "permission_id": {
"bsonType": "string", "bsonType": "string",
"component": { "component": {
"name": "input" "name": "input"
}, },
"description": "权限唯一标识,不可修改,不允许重复", "description": "权限唯一标识,不可修改,不允许重复",
"label": "权限标识", "label": "权限标识",
"title": "权限ID", "title": "权限ID",
"trim": "both" "trim": "both"
}, },
"permission_name": { "permission_name": {
"bsonType": "string", "bsonType": "string",
"component": { "component": {
"name": "input" "name": "input"
}, },
"description": "权限名称", "description": "权限名称",
"label": "权限名称", "label": "权限名称",
"title": "权限名称", "title": "权限名称",
"trim": "both" "trim": "both"
} }
} }
} }
{ {
"bsonType": "object", "bsonType": "object",
"required": ["role_id", "role_name"], "required": ["role_id", "role_name"],
"permission": { "permission": {
"read": "'READ_UNI_ID_ROLES' in auth.permission", "read": "'READ_UNI_ID_ROLES' in auth.permission",
"create": "'CREATE_UNI_ID_ROLES' in auth.permission", "create": "'CREATE_UNI_ID_ROLES' in auth.permission",
"update": "'UPDATE_UNI_ID_ROLES' in auth.permission", "update": "'UPDATE_UNI_ID_ROLES' in auth.permission",
"delete": "'DELETE_UNI_ID_ROLES' in auth.permission" "delete": "'DELETE_UNI_ID_ROLES' in auth.permission"
}, },
"properties": { "properties": {
"_id": { "_id": {
"description": "存储文档 ID,系统自动生成" "description": "存储文档 ID,系统自动生成"
}, },
"comment": { "comment": {
"title": "备注", "title": "备注",
"bsonType": "string", "bsonType": "string",
"description": "备注", "description": "备注",
"trim": "both" "trim": "both"
}, },
"create_date": { "create_date": {
"bsonType": "timestamp", "bsonType": "timestamp",
"description": "创建时间", "description": "创建时间",
"forceDefaultValue": { "forceDefaultValue": {
"$env": "now" "$env": "now"
} }
}, },
"permission": { "permission": {
"title": "权限", "title": "权限",
"bsonType": "array", "bsonType": "array",
"foreignKey": "uni-id-permissions.permission_id", "foreignKey": "uni-id-permissions.permission_id",
"description": "角色拥有的权限列表", "description": "角色拥有的权限列表",
"enum": { "enum": {
"collection": "uni-id-permissions", "collection": "uni-id-permissions",
"field": "permission_name as text, permission_id as value" "field": "permission_name as text, permission_id as value"
} }
}, },
"role_id": { "role_id": {
"title": "唯一ID", "title": "唯一ID",
"bsonType": "string", "bsonType": "string",
"description": "角色唯一标识,不可修改,不允许重复", "description": "角色唯一标识,不可修改,不允许重复",
"trim": "both" "trim": "both"
}, },
"role_name": { "role_name": {
"title": "名称", "title": "名称",
"bsonType": "string", "bsonType": "string",
"description": "角色名称", "description": "角色名称",
"trim": "both" "trim": "both"
} }
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册