首次完整推送,
V:1.20240808.006
This commit is contained in:
35
uni_modules/uni-id-pages/pages/common/webview/webview.vue
Normal file
35
uni_modules/uni-id-pages/pages/common/webview/webview.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<!-- 网络链接内容展示页(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{
|
||||
this.url = url;
|
||||
}
|
||||
if(title){
|
||||
uni.setNavigationBarTitle({title});
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
url:null
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
120
uni_modules/uni-id-pages/pages/login/login-smscode.vue
Normal file
120
uni_modules/uni-id-pages/pages/login/login-smscode.vue
Normal file
@ -0,0 +1,120 @@
|
||||
<!-- 短信验证码登录页 -->
|
||||
<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() {
|
||||
// #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 => {
|
||||
this.loginSuccess(e)
|
||||
}).catch(e => {
|
||||
if (e.errCode == 'uni-id-captcha-required') {
|
||||
this.$refs.popup.open()
|
||||
} else {
|
||||
console.log(e.errMsg);
|
||||
}
|
||||
}).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>
|
257
uni_modules/uni-id-pages/pages/login/login-withoutpwd.vue
Normal file
257
uni_modules/uni-id-pages/pages/login/login-withoutpwd.vue
Normal file
@ -0,0 +1,257 @@
|
||||
<!-- 免密登录页 -->
|
||||
<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 this.type == 'weixin' ? '/uni_modules/uni-id-pages/static/login/weixin.png' :
|
||||
'/uni_modules/uni-id-pages/static/app-plus/apple.png'
|
||||
}
|
||||
},
|
||||
async onLoad(e) {
|
||||
//获取通过url传递的参数type设置当前登录方式,如果没传递直接默认以配置的登录
|
||||
let type = e.type || config.loginTypes[0]
|
||||
this.type = type
|
||||
|
||||
// console.log("this.type: -----------",this.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-setLoginType', 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-setLoginType')
|
||||
},
|
||||
onReady() {
|
||||
// 是否优先启动一键登录。即:页面一加载就启动一键登录
|
||||
//#ifdef APP-PLUS
|
||||
if (config.loginTypes.includes('univerify') && this.type == "univerify") {
|
||||
uni.preLogin({
|
||||
provider: 'univerify',
|
||||
success: () => {
|
||||
const pages = getCurrentPages();
|
||||
currentWebview = pages[pages.length - 1].$getAppWebview();
|
||||
currentWebview.setStyle({
|
||||
"top": "2000px" // 隐藏当前页面窗体
|
||||
})
|
||||
// this.type == this.loginTypes[1]
|
||||
// console.log('开始一键登录');
|
||||
this.$refs.uniFabLogin.login_before('univerify')
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err);
|
||||
if (config.loginTypes.length > 1) {
|
||||
this.$refs.uniFabLogin.login_before(config.loginTypes[1])
|
||||
} else {
|
||||
uni.showModal({
|
||||
content: err.message,
|
||||
showCancel: false
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
//#endif
|
||||
},
|
||||
methods: {
|
||||
showCurrentWebview() {
|
||||
// 恢复当前页面窗体的显示 一键登录,默认不显示当前窗口
|
||||
currentWebview.setStyle({
|
||||
"top": 0
|
||||
})
|
||||
},
|
||||
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() {
|
||||
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 {
|
||||
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>
|
176
uni_modules/uni-id-pages/pages/login/login-withpwd.vue
Normal file
176
uni_modules/uni-id-pages/pages/login/login-withpwd.vue
Normal file
@ -0,0 +1,176 @@
|
||||
<!-- 账号密码登录页 -->
|
||||
<template>
|
||||
<view class="uni-content">
|
||||
<view class="login-logo">
|
||||
<image :src="logo"></image>
|
||||
</view>
|
||||
<!-- 顶部文字 -->
|
||||
<text class="title title-box">账号密码登录</text>
|
||||
<uni-forms>
|
||||
<uni-forms-item name="username">
|
||||
<uni-easyinput :focus="focusUsername" @blur="focusUsername = false" class="input-box"
|
||||
:inputBorder="false" v-model="username" placeholder="请输入手机号/用户名/邮箱" />
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="password">
|
||||
<uni-easyinput :focus="focusPassword" @blur="focusPassword = false" class="input-box" clearable
|
||||
type="password" :inputBorder="false" v-model="password" placeholder="请输入密码" />
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
<uni-captcha v-if="needCaptcha" focus ref="captcha" scene="login-by-pwd" v-model="captcha" />
|
||||
<!-- 带选择框的隐私政策协议组件 -->
|
||||
<uni-id-pages-agreements scope="login" ref="agreements"></uni-id-pages-agreements>
|
||||
<button class="uni-btn" type="primary" @click="pwdLogin">登录</button>
|
||||
<!-- 忘记密码 -->
|
||||
<view class="link-box">
|
||||
<view v-if="!config.isAdmin">
|
||||
<text class="forget">忘记了?</text>
|
||||
<text class="link" @click="toRetrievePwd">找回密码</text>
|
||||
</view>
|
||||
<text class="link" @click="toRegister">{{config.isAdmin ? '注册管理员账号': '注册账号'}}</text>
|
||||
<!-- <text class="link" @click="toRegister" v-if="!config.isAdmin">注册账号</text> -->
|
||||
</view>
|
||||
<!-- 悬浮登录方式组件 -->
|
||||
<uni-id-pages-fab-login ref="uniFabLogin"></uni-id-pages-fab-login>
|
||||
</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 {
|
||||
"password": "",
|
||||
"username": "",
|
||||
"captcha": "",
|
||||
"needCaptcha": false,
|
||||
"focusUsername": false,
|
||||
"focusPassword": false,
|
||||
"logo": "/static/logo.png"
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// #ifdef H5
|
||||
document.onkeydown = event => {
|
||||
var e = event || window.event;
|
||||
if (e && e.keyCode == 13) { //回车键的键值为13
|
||||
this.pwdLogin()
|
||||
}
|
||||
};
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
// 页面跳转,找回密码
|
||||
toRetrievePwd() {
|
||||
let url = '/uni_modules/uni-id-pages/pages/retrieve/retrieve'
|
||||
//如果刚好用户名输入框的值为手机号码,就把它传到retrieve页面,根据该手机号找回密码
|
||||
if (/^1\d{10}$/.test(this.username)) {
|
||||
url += `?phoneNumber=${this.username}`
|
||||
}
|
||||
uni.navigateTo({
|
||||
url
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 密码登录
|
||||
*/
|
||||
pwdLogin() {
|
||||
if (!this.password.length) {
|
||||
this.focusPassword = true
|
||||
return uni.showToast({
|
||||
title: '请输入密码',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
if (!this.username.length) {
|
||||
this.focusUsername = true
|
||||
return uni.showToast({
|
||||
title: '请输入手机号/用户名/邮箱',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
if (this.needCaptcha && this.captcha.length != 4) {
|
||||
this.$refs.captcha.getImageCaptcha()
|
||||
return uni.showToast({
|
||||
title: '请输入验证码',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
|
||||
if (this.needAgreements && !this.agree) {
|
||||
return this.$refs.agreements.popup(this.pwdLogin)
|
||||
}
|
||||
|
||||
let data = {
|
||||
"password": this.password,
|
||||
"captcha": this.captcha
|
||||
}
|
||||
|
||||
if (/^1\d{10}$/.test(this.username)) {
|
||||
data.mobile = this.username
|
||||
} else if (/@/.test(this.username)) {
|
||||
data.email = this.username
|
||||
} else {
|
||||
data.username = this.username
|
||||
}
|
||||
|
||||
uniIdCo.login(data).then(e => {
|
||||
this.loginSuccess(e)
|
||||
}).catch(e => {
|
||||
if (e.errCode == 'uni-id-captcha-required') {
|
||||
this.needCaptcha = true
|
||||
} else if (this.needCaptcha) {
|
||||
//登录失败,自动重新获取验证码
|
||||
this.$refs.captcha.getImageCaptcha()
|
||||
}
|
||||
})
|
||||
},
|
||||
/* 前往注册 */
|
||||
toRegister() {
|
||||
uni.navigateTo({
|
||||
url: this.config.isAdmin ? '/uni_modules/uni-id-pages/pages/register/register-admin' :
|
||||
'/uni_modules/uni-id-pages/pages/register/register',
|
||||
fail(e) {
|
||||
console.error(e);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/uni_modules/uni-id-pages/common/login-page.scss";
|
||||
|
||||
@media screen and (min-width: 690px) {
|
||||
.uni-content {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.forget {
|
||||
font-size: 12px;
|
||||
color: #8a8f8b;
|
||||
}
|
||||
|
||||
.link-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
178
uni_modules/uni-id-pages/pages/register/register-admin.vue
Normal file
178
uni_modules/uni-id-pages/pages/register/register-admin.vue
Normal file
@ -0,0 +1,178 @@
|
||||
<!-- 创建超级管理员 -->
|
||||
<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 => {
|
||||
uni.navigateBack()
|
||||
})
|
||||
.catch(e => {
|
||||
//更好的体验:登录错误,直接刷新验证码
|
||||
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>
|
216
uni_modules/uni-id-pages/pages/register/register-by-email.vue
Normal file
216
uni_modules/uni-id-pages/pages/register/register-by-email.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<!-- 邮箱验证码注册 -->
|
||||
<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(/[\u4E00-\u9FA5\uF900-\uFA2D]{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>
|
181
uni_modules/uni-id-pages/pages/register/register.vue
Normal file
181
uni_modules/uni-id-pages/pages/register/register.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<!-- 账号注册页 -->
|
||||
<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="registerByEmail">邮箱验证码注册</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 {
|
||||
store,
|
||||
mutations
|
||||
} from '@/uni_modules/uni-id-pages/common/store.js'
|
||||
|
||||
const uniIdCo = uniCloud.importObject("uni-id-co")
|
||||
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())
|
||||
this['focus' + key] = true
|
||||
})
|
||||
},
|
||||
submitForm(params) {
|
||||
uniIdCo.registerUser(this.formData).then(e => {
|
||||
this.loginSuccess(e)
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e.message);
|
||||
//更好的体验:登录错误,直接刷新验证码
|
||||
this.$refs.captcha.getImageCaptcha()
|
||||
})
|
||||
},
|
||||
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: 530px;
|
||||
}
|
||||
|
||||
.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>
|
56
uni_modules/uni-id-pages/pages/register/validator.js
Normal file
56
uni_modules/uni-id-pages/pages/register/validator.js
Normal file
@ -0,0 +1,56 @@
|
||||
import passwordMod from '@/uni_modules/uni-id-pages/common/password.js'
|
||||
export default {
|
||||
"username": {
|
||||
"rules": [{
|
||||
required: true,
|
||||
errorMessage: '请输入用户名',
|
||||
},
|
||||
{
|
||||
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(/[\u4E00-\u9FA5\uF900-\uFA2D]{1,}/.test(value)){
|
||||
callback('用户名不能包含中文')
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
],
|
||||
"label": "用户名"
|
||||
},
|
||||
"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(/[\u4E00-\u9FA5\uF900-\uFA2D]{1,}/.test(value)){
|
||||
callback('昵称不能包含中文')
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
],
|
||||
"label": "昵称"
|
||||
},
|
||||
...passwordMod.getPwdRules()
|
||||
}
|
218
uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email.vue
Normal file
218
uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email.vue
Normal file
@ -0,0 +1,218 @@
|
||||
<!-- 找回密码页 -->
|
||||
<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() {
|
||||
this.$refs.form.validate()
|
||||
.then(res => {
|
||||
let {
|
||||
email,
|
||||
password: password,
|
||||
captcha,
|
||||
code
|
||||
} = this.formData
|
||||
uniIdCo.resetPwdByEmail({
|
||||
email,
|
||||
code,
|
||||
password,
|
||||
captcha
|
||||
}).then(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'){
|
||||
return this.$refs.shortCode.focusSmsCodeInput = true
|
||||
}
|
||||
key = key.replace(key[0], key[0].toUpperCase())
|
||||
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>
|
241
uni_modules/uni-id-pages/pages/retrieve/retrieve.vue
Normal file
241
uni_modules/uni-id-pages/pages/retrieve/retrieve.vue
Normal file
@ -0,0 +1,241 @@
|
||||
<!-- 找回密码页 -->
|
||||
<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() {
|
||||
this.$refs.form.validate()
|
||||
.then(res => {
|
||||
let {
|
||||
"phone": mobile,
|
||||
"password": password,
|
||||
captcha,
|
||||
code
|
||||
} = this.formData
|
||||
uniIdCo.resetPwdBySms({
|
||||
mobile,
|
||||
code,
|
||||
password,
|
||||
captcha
|
||||
}).then(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'){
|
||||
return this.$refs.shortCode.focusSmsCodeInput = true
|
||||
}
|
||||
key = key.replace(key[0], key[0].toUpperCase())
|
||||
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>
|
@ -0,0 +1,131 @@
|
||||
<!-- 绑定手机号码页 -->
|
||||
<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
|
||||
});
|
||||
}
|
||||
|
||||
const uniIdCo = uniCloud.importObject("uni-id-co")
|
||||
uniIdCo.bindMobileBySms(this.formData).then(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>
|
@ -0,0 +1,130 @@
|
||||
<!-- 修改密码 -->
|
||||
<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="oldPassword">
|
||||
<uni-easyinput :focus="focusOldPassword" @blur="focusOldPassword = false" class="input-box"
|
||||
type="password" :inputBorder="false" v-model="formData.oldPassword" placeholder="请输入旧密码">
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
<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>
|
||||
<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>
|
||||
<button class="uni-btn send-btn-box" type="primary" @click="submit">提交</button>
|
||||
</uni-forms>
|
||||
</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", {
|
||||
customUI:true
|
||||
})
|
||||
export default {
|
||||
mixins: [mixin],
|
||||
data() {
|
||||
return {
|
||||
focusOldPassword: false,
|
||||
focusNewPassword: false,
|
||||
focusNewPassword2: false,
|
||||
formData: {
|
||||
'oldPassword': '',
|
||||
'newPassword': '',
|
||||
'newPassword2': '',
|
||||
},
|
||||
rules: {
|
||||
oldPassword: {
|
||||
rules: [{
|
||||
required: true,
|
||||
errorMessage: '请输入新密码',
|
||||
},
|
||||
{
|
||||
pattern: /^.{6,20}$/,
|
||||
errorMessage: '密码为6 - 20位',
|
||||
}
|
||||
]
|
||||
},
|
||||
...passwordMod.getPwdRules('newPassword', 'newPassword2')
|
||||
},
|
||||
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 => {
|
||||
let {
|
||||
oldPassword,
|
||||
newPassword
|
||||
} = this.formData
|
||||
uniIdCo.updatePwd({
|
||||
oldPassword,
|
||||
newPassword
|
||||
}).then(e => {
|
||||
uni.removeStorageSync('uni_id_token');
|
||||
uni.setStorageSync('uni_id_token_expired', 0)
|
||||
uni.redirectTo({
|
||||
url:'/uni_modules/uni-id-pages/pages/login/login-withpwd'
|
||||
})
|
||||
}).catch(e => {
|
||||
uni.showModal({
|
||||
content: e.message,
|
||||
showCancel: false
|
||||
});
|
||||
})
|
||||
}).catch(errors => {
|
||||
let key = errors[0].key
|
||||
key = key.replace(key[0], key[0].toUpperCase())
|
||||
this['focus' + key] = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</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;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,39 @@
|
||||
<!-- 图片裁剪页 -->
|
||||
<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: 400rpx;
|
||||
}
|
||||
.mt{
|
||||
margin-top: -10px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,227 @@
|
||||
> 插件来源:[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>
|
||||

|
@ -0,0 +1,19 @@
|
||||
<?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>
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,15 @@
|
||||
<?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>
|
After Width: | Height: | Size: 791 B |
@ -0,0 +1,160 @@
|
||||
.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: 1rpx 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: 1rpx 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: 34rpx;
|
||||
height: 34rpx;
|
||||
border: 6rpx solid #fff;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.l-clipper__edge::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-color: transparent;
|
||||
}
|
||||
.l-clipper__edge:nth-child(1) {
|
||||
left: -6rpx;
|
||||
top: -6rpx;
|
||||
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: -6rpx;
|
||||
top: -6rpx;
|
||||
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: -6rpx;
|
||||
bottom: -6rpx;
|
||||
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: -6rpx;
|
||||
bottom: -6rpx;
|
||||
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: 20rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.l-clipper-tools__btns .cancel {
|
||||
width: 112rpx;
|
||||
height: 60rpx;
|
||||
text-align: center;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
.l-clipper-tools__btns .confirm {
|
||||
width: 112rpx;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
background-color: #07c160;
|
||||
border-radius: 6rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.l-clipper-tools__btns image {
|
||||
display: block;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
.l-clipper-tools__btns {
|
||||
flex-direction: row;
|
||||
}
|
@ -0,0 +1,820 @@
|
||||
<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>
|
@ -0,0 +1,244 @@
|
||||
/**
|
||||
* 判断手指触摸位置
|
||||
*/
|
||||
export function determineDirection(clipX, clipY, clipWidth, clipHeight, currentX, currentY) {
|
||||
/*
|
||||
* (右下>>1 右上>>2 左上>>3 左下>>4)
|
||||
*/
|
||||
let corner;
|
||||
/**
|
||||
* 思路:(利用直角坐标系)
|
||||
* 1.找出裁剪框中心点
|
||||
* 2.如点击坐标在上方点与左方点区域内,则点击为左上角
|
||||
* 3.如点击坐标在下方点与右方点区域内,则点击为右下角
|
||||
* 4.其他角同理
|
||||
*/
|
||||
const mainPoint = [clipX + clipWidth / 2, clipY + clipHeight / 2]; // 中心点
|
||||
const currentPoint = [currentX, currentY]; // 触摸点
|
||||
|
||||
if (currentPoint[0] <= mainPoint[0] && currentPoint[1] <= mainPoint[1]) {
|
||||
corner = 3; // 左上
|
||||
} else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] <= mainPoint[1]) {
|
||||
corner = 2; // 右上
|
||||
} else if (currentPoint[0] <= mainPoint[0] && currentPoint[1] >= mainPoint[1]) {
|
||||
corner = 4; // 左下
|
||||
} else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] >= mainPoint[1]) {
|
||||
corner = 1; // 右下
|
||||
}
|
||||
|
||||
return corner;
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片边缘检测检测时,计算图片偏移量
|
||||
*/
|
||||
export function calcImageOffset(data, scale) {
|
||||
let left = data.imageLeft;
|
||||
let top = data.imageTop;
|
||||
scale = scale || data.scale;
|
||||
|
||||
let imageWidth = data.imageWidth;
|
||||
let imageHeight = data.imageHeight;
|
||||
if ((data.angle / 90) % 2) {
|
||||
imageWidth = data.imageHeight;
|
||||
imageHeight = data.imageWidth;
|
||||
}
|
||||
const {
|
||||
clipX,
|
||||
clipWidth,
|
||||
clipY,
|
||||
clipHeight
|
||||
} = data;
|
||||
|
||||
// 当前图片宽度/高度
|
||||
const currentImageSize = (size) => (size * scale) / 2;
|
||||
const currentImageWidth = currentImageSize(imageWidth);
|
||||
const currentImageHeight = currentImageSize(imageHeight);
|
||||
|
||||
left = clipX + currentImageWidth >= left ? left : clipX + currentImageWidth;
|
||||
left = clipX + clipWidth - currentImageWidth <= left ? left : clipX + clipWidth - currentImageWidth;
|
||||
top = clipY + currentImageHeight >= top ? top : clipY + currentImageHeight;
|
||||
top = clipY + clipHeight - currentImageHeight <= top ? top : clipY + clipHeight - currentImageHeight;
|
||||
return {
|
||||
left,
|
||||
top,
|
||||
scale
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片边缘检测时,计算图片缩放比例
|
||||
*/
|
||||
export function calcImageScale(data, scale) {
|
||||
scale = scale || data.scale;
|
||||
let {
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
clipWidth,
|
||||
clipHeight,
|
||||
angle
|
||||
} = data
|
||||
if ((angle / 90) % 2) {
|
||||
imageWidth = imageHeight;
|
||||
imageHeight = imageWidth;
|
||||
}
|
||||
if (imageWidth * scale < clipWidth) {
|
||||
scale = clipWidth / imageWidth;
|
||||
}
|
||||
if (imageHeight * scale < clipHeight) {
|
||||
scale = Math.max(scale, clipHeight / imageHeight);
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算图片尺寸
|
||||
*/
|
||||
export function calcImageSize(width, height, data) {
|
||||
let imageWidth = width,
|
||||
imageHeight = height;
|
||||
let {
|
||||
clipWidth,
|
||||
clipHeight,
|
||||
sysinfo,
|
||||
width: originWidth,
|
||||
height: originHeight
|
||||
} = data
|
||||
if (imageWidth && imageHeight) {
|
||||
if (imageWidth / imageHeight > (clipWidth || originWidth) / (clipWidth || originHeight)) {
|
||||
imageHeight = clipHeight || originHeight;
|
||||
imageWidth = (width / height) * imageHeight;
|
||||
} else {
|
||||
imageWidth = clipWidth || originWidth;
|
||||
imageHeight = (height / width) * imageWidth;
|
||||
}
|
||||
} else {
|
||||
let sys = sysinfo || uni.getSystemInfoSync();
|
||||
imageWidth = sys.windowWidth;
|
||||
imageHeight = 0;
|
||||
}
|
||||
return {
|
||||
imageWidth,
|
||||
imageHeight
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 勾股定理求斜边
|
||||
*/
|
||||
export function calcPythagoreanTheorem(width, height) {
|
||||
return Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖动裁剪框时计算
|
||||
*/
|
||||
export function clipTouchMoveOfCalculate(data, event) {
|
||||
const clientX = event.touches[0].clientX;
|
||||
const clientY = event.touches[0].clientY;
|
||||
|
||||
let {
|
||||
clipWidth,
|
||||
clipHeight,
|
||||
clipY: oldClipY,
|
||||
clipX: oldClipX,
|
||||
clipStart,
|
||||
isLockRatio,
|
||||
maxWidth,
|
||||
minWidth,
|
||||
maxHeight,
|
||||
minHeight
|
||||
} = data;
|
||||
maxWidth = maxWidth / 2;
|
||||
minWidth = minWidth / 2;
|
||||
minHeight = minHeight / 2;
|
||||
maxHeight = maxHeight / 2;
|
||||
|
||||
let width = clipWidth,
|
||||
height = clipHeight,
|
||||
clipY = oldClipY,
|
||||
clipX = oldClipX,
|
||||
// 获取裁剪框实际宽度/高度
|
||||
// 如果大于最大值则使用最大值
|
||||
// 如果小于最小值则使用最小值
|
||||
sizecorrect = () => {
|
||||
width = width <= maxWidth ? (width >= minWidth ? width : minWidth) : maxWidth;
|
||||
height = height <= maxHeight ? (height >= minHeight ? height : minHeight) : maxHeight;
|
||||
},
|
||||
sizeinspect = () => {
|
||||
sizecorrect();
|
||||
if ((width > maxWidth || width < minWidth || height > maxHeight || height < minHeight) && isLockRatio) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
//if (clipStart.corner) {
|
||||
height = clipStart.height + (clipStart.corner > 1 && clipStart.corner < 4 ? 1 : -1) * (clipStart.y - clientY);
|
||||
//}
|
||||
switch (clipStart.corner) {
|
||||
case 1:
|
||||
width = clipStart.width - clipStart.x + clientX;
|
||||
if (isLockRatio) {
|
||||
height = width / (clipWidth / clipHeight);
|
||||
}
|
||||
if (!sizeinspect()) return;
|
||||
break;
|
||||
case 2:
|
||||
width = clipStart.width - clipStart.x + clientX;
|
||||
if (isLockRatio) {
|
||||
height = width / (clipWidth / clipHeight);
|
||||
}
|
||||
if (!sizeinspect()) {
|
||||
return;
|
||||
} else {
|
||||
clipY = clipStart.clipY - (height - clipStart.height);
|
||||
}
|
||||
|
||||
break;
|
||||
case 3:
|
||||
width = clipStart.width + clipStart.x - clientX;
|
||||
if (isLockRatio) {
|
||||
height = width / (clipWidth / clipHeight);
|
||||
}
|
||||
if (!sizeinspect()) {
|
||||
return;
|
||||
} else {
|
||||
clipY = clipStart.clipY - (height - clipStart.height);
|
||||
clipX = clipStart.clipX - (width - clipStart.width);
|
||||
}
|
||||
|
||||
break;
|
||||
case 4:
|
||||
width = clipStart.width + clipStart.x - clientX;
|
||||
if (isLockRatio) {
|
||||
height = width / (clipWidth / clipHeight);
|
||||
}
|
||||
if (!sizeinspect()) {
|
||||
return;
|
||||
} else {
|
||||
clipX = clipStart.clipX - (width - clipStart.width);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
clipX,
|
||||
clipY
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 单指拖动图片计算偏移
|
||||
*/
|
||||
export function imageTouchMoveOfCalcOffset(data, clientXForLeft, clientYForLeft) {
|
||||
let left = clientXForLeft - data.touchRelative[0].x,
|
||||
top = clientYForLeft - data.touchRelative[0].y;
|
||||
return {
|
||||
left,
|
||||
top
|
||||
};
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
<!-- 注销(销毁)账号 -->
|
||||
<template>
|
||||
<view class="uni-content">
|
||||
<text class="words" space="emsp">
|
||||
一、注销是不可逆操作,注销后:\n
|
||||
1.帐号将无法登录、无法找回。\n
|
||||
2.帐号所有信息都会清除(个人身份信息、粉丝数等;发布的作品、评论、点赞等;交易信息等),你
|
||||
的朋友将无法通过本应用帐号联系你,请自行备份相关
|
||||
信息和数据。\n
|
||||
|
||||
二、重要提示\n
|
||||
1.封禁帐号(永久封禁、社交封禁、直播权限封禁)不能申请注销。\n
|
||||
2.注销后,你的身份证、三方帐号(微信、QQ、微博、支付宝)、手机号等绑定关系将解除,解除后可以绑定到其他帐号。\n
|
||||
3.注销后,手机号可以注册新的帐号,新帐号不会存在之前帐号的任何信息(作品、粉丝、评论、个人信息等)。\n
|
||||
4.注销本应用帐号前,需尽快处理帐号下的资金问题。\n
|
||||
5.视具体帐号情况而定,注销最多需要7天。\n
|
||||
</text>
|
||||
<view class="button-group">
|
||||
<button @click="nextStep" class="next" type="default">下一步</button>
|
||||
<button @click="cancel" type="warn">取消</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
onLoad() {},
|
||||
methods: {
|
||||
cancel() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
nextStep() {
|
||||
uni.showModal({
|
||||
content: '已经仔细阅读注销提示,知晓可能带来的后果,并确认要注销',
|
||||
complete: (e) => {
|
||||
if (e.confirm) {
|
||||
const uniIdco = uniCloud.importObject("uni-id-co");
|
||||
uniIdco.closeAccount().then((e) => {
|
||||
uni.showToast({
|
||||
title: '注销成功',
|
||||
duration: 3000
|
||||
});
|
||||
uni.removeStorageSync('uni_id_token');
|
||||
uni.setStorageSync('uni_id_token_expired', 0)
|
||||
uni.navigateTo({
|
||||
url:"/uni_modules/uni-id-pages/pages/login/login-withoutpwd"
|
||||
})
|
||||
})
|
||||
} else {
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.uni-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.words {
|
||||
padding: 0 26rpx;
|
||||
line-height: 46rpx;
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
|
||||
.button-group button {
|
||||
border-radius: 100px;
|
||||
border: none;
|
||||
width: 300rpx;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.button-group button:after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.button-group button.next {
|
||||
color: #e64340;
|
||||
border: solid 1px #e64340;
|
||||
}
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: fixed;
|
||||
height: 50px;
|
||||
bottom: 10px;
|
||||
width: 750rpx;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-top: solid 1px #e4e6ec;
|
||||
padding-top: 10px;
|
||||
background-color: #FFFFFF;
|
||||
max-width: 690px;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 690px) {
|
||||
.uni-content{
|
||||
max-width: 690px;
|
||||
margin-left: calc(50% - 345px);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1675667510055" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4003" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M807.936 106.656h-76a24.32 24.32 0 0 0-17.92 7.936 27.744 27.744 0 0 0-7.424 19.104c0 6.944 2.464 13.792 7.424 19.104a24.32 24.32 0 0 0 17.92 7.904h76v81.088c0 6.944 2.432 13.76 7.424 19.104a24.32 24.32 0 0 0 35.808 0 27.744 27.744 0 0 0 7.424-19.104V160.704c0-29.824-22.72-54.048-50.656-54.048zM833.248 512a25.12 25.12 0 0 0-17.92 7.392 25.12 25.12 0 0 0-7.392 17.92v76h-76a25.12 25.12 0 0 0-17.92 7.424c-1.344 1.344-2.08 3.072-3.072 4.704-28.576-27.52-60.704-50.112-96.256-65.152 72.192-43.136 117.888-126.08 103.872-219.296-13.216-87.456-81.056-160.576-167.648-178.656a228.16 228.16 0 0 0-46.944-4.896 217.056 217.056 0 0 0-217.12 217.12c0 79.264 42.976 147.936 106.368 185.824-35.456 15.04-67.648 37.632-96.256 65.152-0.96-1.632-1.696-3.36-3.072-4.704a25.12 25.12 0 0 0-17.92-7.424H200v-76a25.12 25.12 0 0 0-7.424-17.92 25.12 25.12 0 0 0-17.92-7.424 25.12 25.12 0 0 0-17.92 7.424 25.12 25.12 0 0 0-7.392 17.92v76c0 27.936 22.72 50.656 50.656 50.656H262.4c-42.336 54.816-71.712 123.488-80.96 200.192-3.424 28.224 19.104 53.12 47.488 53.12h550.048c28.416 0 50.848-24.96 47.488-53.12-9.216-76.8-38.624-145.472-80.96-200.288h62.4c27.968 0 50.688-22.72 50.688-50.656V537.28a25.12 25.12 0 0 0-7.424-17.92 25.12 25.12 0 0 0-17.92-7.392zM174.72 268.8a24.32 24.32 0 0 0 17.888-7.904 27.744 27.744 0 0 0 7.424-19.104V160.704h76a24.32 24.32 0 0 0 17.92-7.904 27.744 27.744 0 0 0 7.392-19.104 27.744 27.744 0 0 0-7.424-19.104 24.32 24.32 0 0 0-17.92-7.936H200c-27.968 0-50.656 24.224-50.656 54.08v81.056c0 6.944 2.432 13.76 7.392 19.104a24.32 24.32 0 0 0 17.92 7.904z" fill="#72a7ff" p-id="4004"></path></svg>
|
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<view>
|
||||
<template v-if="isCertify">
|
||||
<uni-list>
|
||||
<uni-list-item class="item" title="姓名" :rightText="userInfo.realNameAuth.realName"></uni-list-item>
|
||||
<uni-list-item class="item" title="身份证号码" :rightText="userInfo.realNameAuth.identity"></uni-list-item>
|
||||
</uni-list>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view class="uni-content">
|
||||
<template v-if="verifyFail">
|
||||
<view class="face-icon">
|
||||
<image src="./face-verify-icon.svg" class="face-icon-image" />
|
||||
</view>
|
||||
<view class="error-title">{{verifyFailTitle}}</view>
|
||||
<view class="error-description">{{verifyFailContent}}</view>
|
||||
<button type="primary" @click="retry" v-if="verifyFailCode !== 10013">重新开始验证</button>
|
||||
<button type="primary" @click="retry" v-else>返回</button>
|
||||
<view class="dev-tip" v-if="isDev">请在控制台查看详细错误(此提示仅在开发环境展示)</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="title">实名认证</text>
|
||||
<uni-forms>
|
||||
<uni-forms-item name="realName">
|
||||
<uni-easyinput placeholder="姓名" class="input-box" v-model="realName" :clearable="false">
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="idCard">
|
||||
<uni-easyinput placeholder="身份证号码" class="input-box" v-model="idCard" :clearable="false">
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
<uni-id-pages-agreements scope="realNameVerify" ref="agreements" style="margin-bottom: 20px;">
|
||||
</uni-id-pages-agreements>
|
||||
<button type="primary" :disabled="!certifyIdNext" @click="getCertifyId">确定</button>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import checkIdCard from '@/uni_modules/uni-id-pages/common/check-id-card.js';
|
||||
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
|
||||
|
||||
import {
|
||||
store,
|
||||
mutations
|
||||
} from '@/uni_modules/uni-id-pages/common/store.js'
|
||||
|
||||
const uniIdCo = uniCloud.importObject('uni-id-co')
|
||||
const tempFrvInfoKey = 'uni-id-pages-temp-frv'
|
||||
export default {
|
||||
mixins: [mixin],
|
||||
data() {
|
||||
return {
|
||||
realName: '',
|
||||
idCard: '',
|
||||
certifyId: '',
|
||||
verifyFail: false,
|
||||
verifyFailCode: 0,
|
||||
verifyFailTitle: '',
|
||||
verifyFailContent: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
userInfo() {
|
||||
return store.userInfo
|
||||
},
|
||||
certifyIdNext() {
|
||||
return Boolean(this.realName) && Boolean(this.idCard) && (this.needAgreements && this.agree)
|
||||
},
|
||||
isCertify() {
|
||||
return this.userInfo.realNameAuth && this.userInfo.realNameAuth.authStatus === 2
|
||||
},
|
||||
isDev() {
|
||||
return process.env.NODE_ENV === 'development'
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
const tempFrvInfo = uni.getStorageSync(tempFrvInfoKey);
|
||||
if (tempFrvInfo) {
|
||||
this.realName = tempFrvInfo.realName
|
||||
this.idCard = tempFrvInfo.idCard
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getCertifyId() {
|
||||
if (!this.certifyIdNext) return
|
||||
|
||||
// #ifndef APP
|
||||
return uni.showModal({
|
||||
content: "暂不支持实名认证",
|
||||
showCancel: false
|
||||
})
|
||||
// #endif
|
||||
|
||||
if (!checkIdCard(this.idCard)) {
|
||||
uni.showToast({
|
||||
title: "身份证不合法",
|
||||
icon: "none"
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
typeof this.realName !== 'string' ||
|
||||
this.realName.length < 2 ||
|
||||
!/^[\u4e00-\u9fa5]{1,10}(·?[\u4e00-\u9fa5]{1,10}){0,5}$/.test(this.realName)
|
||||
) {
|
||||
uni.showToast({
|
||||
title: "姓名只能是汉字",
|
||||
icon: "none"
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.setStorage({
|
||||
key: tempFrvInfoKey,
|
||||
data: {
|
||||
realName: this.realName,
|
||||
idCard: this.idCard
|
||||
}
|
||||
});
|
||||
|
||||
const metaInfo = uni.getFacialRecognitionMetaInfo()
|
||||
|
||||
const res = await uniIdCo.getFrvCertifyId({
|
||||
realName: this.realName,
|
||||
idCard: this.idCard,
|
||||
metaInfo
|
||||
})
|
||||
|
||||
this.certifyId = res.certifyId
|
||||
|
||||
this.startFacialRecognitionVerify()
|
||||
},
|
||||
startFacialRecognitionVerify() {
|
||||
|
||||
// #ifdef APP
|
||||
uni.startFacialRecognitionVerify({
|
||||
certifyId: this.certifyId,
|
||||
progressBarColor: "#2979ff",
|
||||
success: () => {
|
||||
this.verifyFail = false
|
||||
this.getFrvAuthResult()
|
||||
},
|
||||
fail: (e) => {
|
||||
let title = "验证失败"
|
||||
let content
|
||||
|
||||
console.log(
|
||||
`[frv-debug] certifyId auth error: certifyId -> ${this.certifyId}, error -> ${JSON.stringify(e, null, 4)}`
|
||||
)
|
||||
|
||||
switch (e.errCode) {
|
||||
case 10001:
|
||||
content = '认证ID为空'
|
||||
break
|
||||
case 10010:
|
||||
title = '刷脸异常'
|
||||
content = e.cause.message || '错误代码: 10010'
|
||||
break
|
||||
case 10011:
|
||||
title = '验证中断'
|
||||
content = e.cause.message || '错误代码: 10011'
|
||||
break
|
||||
case 10012:
|
||||
content = '网络异常'
|
||||
break
|
||||
case 10013:
|
||||
this.verifyFailCode = e.errCode
|
||||
this.verifyFailContent = e.cause.message || '错误代码: 10013'
|
||||
this.getFrvAuthResult()
|
||||
|
||||
console.log(
|
||||
`[frv-debug] 刷脸失败, certifyId -> ${this.certifyId}, 如在开发环境请检查用户的姓名、身份证号与刷脸用户是否为同一用户。如遇到认证ID已使用请检查opendb-frv-logs表中certifyId状态`
|
||||
)
|
||||
return
|
||||
case 10020:
|
||||
content = '设备设置时间异常'
|
||||
break
|
||||
default:
|
||||
title = ''
|
||||
content = `验证未知错误 (${e.errCode})`
|
||||
break
|
||||
}
|
||||
|
||||
this.verifyFail = true
|
||||
this.verifyFailCode = e.errCode
|
||||
this.verifyFailTitle = title
|
||||
this.verifyFailContent = content
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
async getFrvAuthResult() {
|
||||
const uniIdCo = uniCloud.importObject('uni-id-co', {
|
||||
customUI: true
|
||||
})
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: "验证中...",
|
||||
mask: false
|
||||
})
|
||||
const res = await uniIdCo.getFrvAuthResult({
|
||||
certifyId: this.certifyId
|
||||
})
|
||||
|
||||
const {
|
||||
errCode,
|
||||
...rest
|
||||
} = res
|
||||
|
||||
if (this.verifyFailContent) {
|
||||
console.log(`[frv-debug] 客户端刷脸失败,由实人认证服务查询具体原因,原因:${this.verifyFailContent}`)
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
content: "实名认证成功",
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
mutations.setUserInfo({
|
||||
realNameAuth: rest
|
||||
})
|
||||
this.verifyFail = false
|
||||
}
|
||||
})
|
||||
|
||||
uni.removeStorage({
|
||||
key: tempFrvInfoKey
|
||||
})
|
||||
} catch (e) {
|
||||
this.verifyFail = true
|
||||
this.verifyFailTitle = e.errMsg
|
||||
console.error(JSON.stringify(e));
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
},
|
||||
retry() {
|
||||
if (this.verifyFailCode !== 10013) {
|
||||
this.getCertifyId()
|
||||
} else {
|
||||
this.verifyFail = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/uni_modules/uni-id-pages/common/login-page.scss";
|
||||
|
||||
.checkbox-box,
|
||||
.uni-label-pointer {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.item {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.text {
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.checkbox-box ::v-deep .uni-checkbox-input {
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.checkbox-box ::v-deep .uni-checkbox-input.uni-checkbox-input-checked {
|
||||
border-color: $uni-color-primary;
|
||||
color: #FFFFFF !important;
|
||||
background-color: $uni-color-primary;
|
||||
}
|
||||
|
||||
.agreements {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.face-icon {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 50px auto 30px;
|
||||
}
|
||||
|
||||
.face-icon-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-description {
|
||||
font-size: 13px;
|
||||
color: #999999;
|
||||
margin: 10px 0 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dev-tip {
|
||||
margin-top: 20px;
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
171
uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd.vue
Normal file
171
uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd.vue
Normal file
@ -0,0 +1,171 @@
|
||||
<!-- 设置密码 -->
|
||||
<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);
|
||||
}
|
||||
}).finally(e => {
|
||||
this.formData.captcha = ''
|
||||
})
|
||||
},
|
||||
skip () {
|
||||
mutations.loginBack({
|
||||
uniIdRedirectUrl: 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>
|
272
uni_modules/uni-id-pages/pages/userinfo/userinfo.vue
Normal file
272
uni_modules/uni-id-pages/pages/userinfo/userinfo.vue
Normal file
@ -0,0 +1,272 @@
|
||||
<!-- 用户资料页 -->
|
||||
<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>
|
||||
<!-- #ifdef APP -->
|
||||
<!-- 如未开通实人认证服务,可以将实名认证入口注释 -->
|
||||
<uni-list-item class="item" @click="realNameVerify" title="实名认证" :rightText="realNameStatus !== 2 ? '未认证': '已认证'" link>
|
||||
</uni-list-item>
|
||||
<!-- #endif -->
|
||||
<uni-list-item v-if="hasPwd" class="item" @click="changePassword" title="修改密码" link>
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
<!-- #ifndef MP -->
|
||||
<uni-list class="mt10">
|
||||
<uni-list-item @click="deactivate" title="注销账号" link="navigateTo"></uni-list-item>
|
||||
</uni-list>
|
||||
<!-- #endif -->
|
||||
<uni-popup ref="dialog" type="dialog">
|
||||
<uni-popup-dialog mode="input" :value="userInfo.nickname" @confirm="setNickname" :inputType="setNicknameIng?'nickname':'text'"
|
||||
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 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
|
||||
},
|
||||
realNameStatus () {
|
||||
if (!this.userInfo.realNameAuth) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return this.userInfo.realNameAuth.authStatus
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
univerifyStyle: {
|
||||
authButton: {
|
||||
"title": "本机号码一键绑定", // 授权按钮文案
|
||||
},
|
||||
otherLoginButton: {
|
||||
"title": "其他号码绑定",
|
||||
}
|
||||
},
|
||||
// userInfo: {
|
||||
// mobile:'',
|
||||
// nickname:''
|
||||
// },
|
||||
hasPwd: false,
|
||||
showLoginManage: false ,//通过页面传参隐藏登录&退出登录按钮
|
||||
setNicknameIng: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 => {
|
||||
uniIdCo.bindMobileByUniverify(e.authResult).then(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) {
|
||||
if (nickname) {
|
||||
mutations.updateUserInfo({
|
||||
nickname
|
||||
})
|
||||
this.setNicknameIng = false
|
||||
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()
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
realNameVerify () {
|
||||
uni.navigateTo({
|
||||
url: "/uni_modules/uni-id-pages/pages/userinfo/realname-verify/realname-verify"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</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>
|
Reference in New Issue
Block a user