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