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

修复已知问题

上级 bd7be1a7
...@@ -87,12 +87,6 @@ console.log(` ...@@ -87,12 +87,6 @@ console.log(`
}) })
}) })
},1) },1)
//更新 uni-starter.config agreements
let agreementsTitle = $i18n('agreementsTitle').split(',')
let agreements = uniStarterConfig.about.agreements
agreements[0].title = agreementsTitle[0]
agreements[1].title = agreementsTitle[1]
uniStarterConfig.about.agreements = agreements
} }
initLanguageAfter() initLanguageAfter()
uni.$on('changeLanguage', e => { uni.$on('changeLanguage', e => {
......
...@@ -108,7 +108,7 @@ export default { ...@@ -108,7 +108,7 @@ export default {
}, },
settings:{ settings:{
navigationBarTitle:"设置", navigationBarTitle:"设置",
userInfo: "个人资料", userInfo: "账号资料",
changePassword: "修改密码", changePassword: "修改密码",
clearTmp: "清理缓存", clearTmp: "清理缓存",
pushServer: "推送功能", pushServer: "推送功能",
......
...@@ -122,12 +122,13 @@ ...@@ -122,12 +122,13 @@
"style": { "style": {
"navigationBarTitleText": "注销账号" "navigationBarTitleText": "注销账号"
} }
}, { },
"path": "uni_modules/uni-id-pages/pages/userinfo/userinfo", {
"style": { "path": "uni_modules/uni-id-pages/pages/userinfo/userinfo",
"navigationBarTitleText": "个人资料" "style": {
} "navigationBarTitleText": "个人资料"
}, { }
},{
"path": "uni_modules/uni-id-pages/pages/userinfo/bind-mobile/bind-mobile", "path": "uni_modules/uni-id-pages/pages/userinfo/bind-mobile/bind-mobile",
"style": { "style": {
"navigationBarTitleText": "绑定手机号码" "navigationBarTitleText": "绑定手机号码"
......
...@@ -18,12 +18,8 @@ ...@@ -18,12 +18,8 @@
<uni-list-item v-if="i18nEnable" :title="$t('settings.changeLanguage')" @click="changeLanguage" :rightText="currentLanguage" link></uni-list-item> <uni-list-item v-if="i18nEnable" :title="$t('settings.changeLanguage')" @click="changeLanguage" :rightText="currentLanguage" link></uni-list-item>
</uni-list> </uni-list>
<uni-list class="mt10" :border="false">
<uni-list-item @click="deactivate" :title="$t('settings.deactivate')" link="navigateTo"></uni-list-item>
</uni-list>
<!-- 退出/登录 按钮 --> <!-- 退出/登录 按钮 -->
<view class="bottom-back" @click="clickLogout"> <view class="bottom-back" @click="changeLoginState">
<text class="bottom-back-text" v-if="hasLogin">{{$t('settings.logOut')}}</text> <text class="bottom-back-text" v-if="hasLogin">{{$t('settings.logOut')}}</text>
<text class="bottom-back-text" v-else>{{$t('settings.login')}}</text> <text class="bottom-back-text" v-else>{{$t('settings.login')}}</text>
</view> </view>
...@@ -31,7 +27,8 @@ ...@@ -31,7 +27,8 @@
</template> </template>
<script> <script>
import pushServer from './dc-push/push.js'; import pushServer from './dc-push/push.js';
import common from '@/uni_modules/uni-id-pages/common/common.js';
export default { export default {
data() { data() {
return { return {
...@@ -77,16 +74,15 @@ ...@@ -77,16 +74,15 @@
//#endif //#endif
}, },
methods: { methods: {
toEdit() { async changeLoginState(){
uni.navigateTo({ if(this.hasLogin){
url: '/pages/ucenter/userinfo/userinfo' await common.logout()
}); }else{
}, uni.redirectTo({
deactivate(){ url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
uni.navigateTo({ });
url:"/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate" }
}) },
},
/** /**
* 开始生物认证 * 开始生物认证
*/ */
...@@ -163,32 +159,6 @@ ...@@ -163,32 +159,6 @@
}) })
}) })
}, },
clickLogout() {
if (this.hasLogin) {
uni.showModal({
title: this.$t('settings.tips'),
content: this.$t('settings.exitLogin'),
cancelText: this.$t('settings.cancelText'),
confirmText: this.$t('settings.confirmText'),
success: res => {
if (res.confirm) {
uni.removeStorageSync('uni_id_token');
uni.setStorageSync('uni_id_token_expired', 0)
uni.redirectTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
});
}
}
});
} else {
uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
complete: (e) => {
console.log(6369696,e);
}
});
}
},
clearTmp() { clearTmp() {
uni.showLoading({ uni.showLoading({
title: this.$t('settings.clearing'), title: this.$t('settings.clearing'),
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<view class="center"> <view class="center">
<uni-sign-in ref="signIn"></uni-sign-in> <uni-sign-in ref="signIn"></uni-sign-in>
<view class="userInfo" @click.capture="toUserInfo"> <view class="userInfo" @click.capture="toUserInfo">
<cloud-image width="150rpx" height="150rpx" v-if="userInfo.avatar_file&&userInfo.avatar_file.url" :src="userInfo.avatar_file.url"></cloud-image> <cloud-image width="150rpx" height="150rpx" v-if="hasLogin&&userInfo.avatar_file&&userInfo.avatar_file.url" :src="userInfo.avatar_file.url"></cloud-image>
<image v-else class="logo-img" src="@/static/uni-center/defaultAvatarUrl.png"></image> <image v-else class="logo-img" src="@/static/uni-center/defaultAvatarUrl.png"></image>
<view class="logo-title"> <view class="logo-title">
<text class="uer-name" v-if="hasLogin">{{userInfo.nickname||userInfo.username||userInfo.mobile}}</text> <text class="uer-name" v-if="hasLogin">{{userInfo.nickname||userInfo.username||userInfo.mobile}}</text>
...@@ -133,7 +133,8 @@ ...@@ -133,7 +133,8 @@
"radius": "100%" // 边框圆角,支持百分比 "radius": "100%" // 边框圆角,支持百分比
} }
}, },
userInfo:{} userInfo:{},
hasLogin:false
} }
}, },
onLoad() { onLoad() {
...@@ -145,15 +146,25 @@ ...@@ -145,15 +146,25 @@
icon: 'loop', icon: 'loop',
showBadge: this.appVersion.hasNew showBadge: this.appVersion.hasNew
}) })
//#endif //#endif
}, },
onShow() { onShow() {
console.log('this.hasLogin',this.hasLogin); this.hasLogin = uniCloud.getCurrentUserInfo().tokenExpired > Date.now()
if(this.hasLogin){ if(this.hasLogin){
this.getUserInfo() this.getUserInfo()
} }
}, },
computed: { computed: {
test:{
get(){
console.log(this._test,Date.now());
this._test = Date.now()
return ''
},
set(){
}
},
// #ifdef APP-PLUS // #ifdef APP-PLUS
appVersion() { appVersion() {
return getApp().appVersion return getApp().appVersion
...@@ -161,19 +172,10 @@ ...@@ -161,19 +172,10 @@
// #endif // #endif
appConfig() { appConfig() {
return getApp().globalData.config return getApp().globalData.config
}, }
hasLogin(){
return uniCloud.getCurrentUserInfo().tokenExpired > Date.now()
}
}, },
methods: { methods: {
setUserInfo(data){
},
getUserInfo(e) { getUserInfo(e) {
uni.showLoading({
mask: true
});
const db = uniCloud.database(); const db = uniCloud.database();
db.collection('uni-id-users').where("'_id' == $cloudEnv_uid").field('mobile,nickname,avatar_file').get().then(res => { db.collection('uni-id-users').where("'_id' == $cloudEnv_uid").field('mobile,nickname,avatar_file').get().then(res => {
console.log({res}); console.log({res});
...@@ -184,7 +186,6 @@ ...@@ -184,7 +186,6 @@
console.log(e.message, e.errCode); console.log(e.message, e.errCode);
}).finally(e => { }).finally(e => {
// console.log(e); // console.log(e);
uni.hideLoading()
}) })
}, },
toSettings() { toSettings() {
......
{
"schedule": {
"__UNI__xxxxxx": {
"enable": true,
"h5-weixin": {
"enable": false,
"tasks": ["ticket"]
}
}
},
"ipWhitelist": ["0.0.0.0"]
}
\ No newline at end of file
## 1.0.12(2022-09-07)
- 修复 getSupportedLoginType判断是否支持微信公众号、PC网页微信扫码登录方式报错的Bug
- 优化 适配pc端样式
- 新增 邮箱验证码注册
- 新增 邮箱验证码找回密码
- 新增 退出登录(全局)回调事件:`uni-id-pages-logout`,支持通过[uni.$on](https://uniapp.dcloud.net.cn/api/window/communication.html#on)监听;
- 调整 抽离退出登录方法至`/uni_modules/uni-id-pages/common/common.js`中,方便在项目其他页面中调用
- 调整 用户中心(路径:`/uni_modules/uni-id-pages/pages/userinfo/userinfo`)默认不再显示退出登录按钮。支持页面传参数`showLoginManage=true`恢复显示
## 1.0.11(2022-09-01)
- 修复 iOS端,一键登录功能卡在showLoading的问题
- 更新 合并密码强度与长度配置 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#config)
- uni-id-co 修复 调用 removeAuthorizedApp 接口报错的Bug
- uni-id-co 新增 管理端接口 updateUser [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-user)
- uni-id-co 调整 为兼容旧版本,未配置密码强度时提供最简单的密码规则校验(长度大于6即可)
- uni-id-co 调整 注册、登录时如果携带了token则尝试对此token进行登出操作
- uni-id-co 调整 管理端接口 addUser 增加 mobile、email等参数 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#add-user)
## 1.0.10(2022-08-25) ## 1.0.10(2022-08-25)
- 修复 导入uni-id-pages插件时未自动导入uni-open-bridge-common的Bug - 修复 导入uni-id-pages插件时未自动导入uni-open-bridge-common的Bug
## 1.0.9(2022-08-23) ## 1.0.9(2022-08-23)
......
const uniIdCo = uniCloud.importObject("uni-id-co")
export default {
async logout() {
await uniIdCo.logout()
uni.removeStorageSync('uni_id_token');
uni.setStorageSync('uni_id_token_expired', 0)
uni.redirectTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
});
uni.$emit('uni-id-pages-logout')
},
}
\ No newline at end of file
...@@ -9,11 +9,11 @@ let mixin = { ...@@ -9,11 +9,11 @@ let mixin = {
}, },
onUnload() { onUnload() {
// #ifdef H5 // #ifdef H5
document.onkeydown = false document.onkeydown = false
// #endif // #endif
}, },
mounted() { mounted() {
this.isMounted = true; this.isMounted = true;
}, },
onLoad(e) { onLoad(e) {
if (e.is_weixin_redirect) { if (e.is_weixin_redirect) {
......
...@@ -2,20 +2,54 @@ ...@@ -2,20 +2,54 @@
padding: 0 60rpx; padding: 0 60rpx;
} }
.login-logo {
display: none;
}
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
.uni-content { .uni-content {
padding: 0; /* #ifndef H5 */
max-width: 400px; padding: 0;
margin-left: calc(50% - 200px); max-width: 300px;
margin-left: calc(50% - 200px);
/* #endif */
/* #ifdef H5 */
margin: 0 auto;
position: relative;
top: 100px;
padding: 30px 40px 80px 40px;
max-width: 450px;
max-height: 450px;
border-radius: 10px;
box-shadow: 0 0 20px #efefef;
background-color: #FFF;
/* #endif */
}
/* #ifdef H5 */
.login-logo {
display: flex;
justify-content: center;
} }
.login-logo image {
width: 60px;
height: 60px;
}
.register-back{
display: none;
}
/* #endif */
} }
.uni-content view { .uni-content view {
box-sizing: border-box; box-sizing: border-box;
} }
/* #endif */ /* #endif */
.title { .title {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
...@@ -61,12 +95,9 @@ ...@@ -61,12 +95,9 @@
margin: 15px 0 0 0; margin: 15px 0 0 0;
color: #FFF !important; color: #FFF !important;
border-radius: 5px; border-radius: 5px;
} }
/*
.uni-btn::after{ .uni-body.uni_modules-uni-id-pages-pages-login-login-withoutpwd{
display: none; height: auto !important;
} }
.uni-btn[disabled]{
background-color: $uni-color-primary !important; \ No newline at end of file
opacity: 0.3;
}*/
\ No newline at end of file
// 导入配置 // 导入配置
import config from '@/uni_modules/uni-id-pages/config.js' import config from '@/uni_modules/uni-id-pages/config.js'
const passwordLength = config.password.length const {passwordStrength} = config
const passwordStrength = config.password.strength
let minPasswordLength = 6
let maxPasswordLength = 20
if (passwordLength) {
if (passwordLength[0]) {
minPasswordLength = passwordLength[0]
}
if (passwordLength[1]) {
maxPasswordLength = passwordLength[1]
}
}
// 密码强度表达式 // 密码强度表达式
const passwordRules = { const passwordRules = {
// 密码必须包含大小写字母、数字和特殊符号 // 密码必须包含大小写字母、数字和特殊符号
...@@ -33,15 +22,10 @@ const ERROR = { ...@@ -33,15 +22,10 @@ const ERROR = {
rePwdErr: '两次输入密码不一致' rePwdErr: '两次输入密码不一致'
}, },
passwordStrengthError: { passwordStrengthError: {
superstrong: '密码必须包含大小写字母、数字和特殊符号', super: '密码必须包含大小写字母、数字和特殊符号,密码长度必须在8-16位之间',
strong: '密码必须包含字母、数字和特殊符号', strong: '密码必须包含字母、数字和特殊符号,密码长度必须在8-16位之间',
medium: '密码必须为字母、数字和特殊符号任意两种的组合', medium: '密码必须为字母、数字和特殊符号任意两种的组合,密码长度必须在8-16位之间',
weak: '密码必须包含字母' weak: '密码必须包含字母,密码长度必须在6-16位之间'
},
passwordLengthError: {
normal: '密码长度必须在' + minPasswordLength + '-' + maxPasswordLength + '位之间',
minLimit: '密码长度不得少于' + minPasswordLength + '',
maxLimit: '密码长度不得超过' + maxPasswordLength + ''
} }
} }
...@@ -52,16 +36,6 @@ function validPwd(password) { ...@@ -52,16 +36,6 @@ function validPwd(password) {
return ERROR.passwordStrengthError[passwordStrength] return ERROR.passwordStrengthError[passwordStrength]
} }
} }
//长度校验
if (passwordLength) {
if (passwordLength[0] && password.length < passwordLength[0]) {
return ERROR.passwordLengthError.minLimit
}
if (passwordLength[1] && password.length > passwordLength[1]) {
return ERROR.passwordLengthError.maxLimit
}
}
return true return true
} }
...@@ -106,8 +80,6 @@ function getPwdRules(pwdName = 'password', rePwdName = 'password2') { ...@@ -106,8 +80,6 @@ function getPwdRules(pwdName = 'password', rePwdName = 'password2') {
export default { export default {
ERROR, ERROR,
minPasswordLength,
maxPasswordLength,
validPwd, validPwd,
getPwdRules getPwdRules
} }
...@@ -46,8 +46,8 @@ ...@@ -46,8 +46,8 @@
watch: { watch: {
src:{ src:{
handler(src) { handler(src) {
// console.log(src); console.log(src);
// console.log(src.substring(0, 8)); console.log(src.substring(0, 8));
if (src&&src.substring(0, 8) == "cloud://") { if (src&&src.substring(0, 8) == "cloud://") {
uniCloud.getTempFileURL({ uniCloud.getTempFileURL({
fileList: [src] fileList: [src]
......
<template>
<view>
<uni-captcha :focus="focusCaptchaInput" ref="captcha" scene="send-email-code" v-model="captcha" />
<view class="box">
<uni-easyinput :focus="focusEmailCodeInput" @blur="focusEmailCodeInput = false" type="number" class="input-box" :inputBorder="false" v-model="modelValue" maxlength="6"
placeholder="请输入邮箱验证码">
</uni-easyinput>
<view class="short-code-btn" hover-class="hover" @click="start">
<text class="inner-text" :class="reverseNumber==0?'inner-text-active':''">{{innerText}}</text>
</view>
</view>
</view>
</template>
<script>
function debounce(func, wait) {
let timer;
wait = wait || 500;
return function() {
let context = this;
let args = arguments;
if (timer) clearTimeout(timer);
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait)
if (callNow) func.apply(context, args);
}
}
/**
* email-code-form
* @description 获取邮箱验证码组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=
* @property {Number} count 倒计时时长 s
* @property {String} email 邮箱
* @property {String} type = [login-by-email-code|reset-pwd-by-email-code|bind-email] 验证码类型,用于防止不同功能的验证码混用,目前支持的类型login登录、register注册、bind绑定邮箱、unbind解绑邮箱
* @property {false} focusCaptchaInput = [true|false] 验证码输入框是否默认获取焦点
*/
export default {
name: "uni-email-code-form",
model: {
prop: 'modelValue',
event: 'update:modelValue'
},
props: {
event: ['update:modelValue'],
/**
* 倒计时时长 s
*/
count: {
type: [String, Number],
default: 60
},
/**
* 邮箱
*/
email: {
type: [String],
default: ''
},
/*
验证码类型,用于防止不同功能的验证码混用,目前支持的类型login登录、register注册、bind绑定邮箱、unbind解绑邮箱
*/
type: {
type: String,
default () {
return 'register'
}
},
/*
验证码输入框是否默认获取焦点
*/
focusCaptchaInput: {
type: Boolean,
default () {
return false
}
},
},
data() {
return {
captcha: "",
reverseNumber: 0,
reverseTimer: null,
modelValue: "",
focusEmailCodeInput:false
};
},
watch: {
captcha(value, oldValue) {
if (value.length == 4 && oldValue.length != 4) {
this.start()
}
},
modelValue(value) {
// TODO 兼容 vue2
this.$emit('input', value);
// TODO 兼容 vue3
this.$emit('update:modelValue', value)
}
},
computed: {
innerText() {
if (this.reverseNumber == 0) return "获取邮箱验证码";
return "重新发送" + '(' + this.reverseNumber + 's)';
}
},
created() {
this.initClick();
},
methods: {
getImageCaptcha(focus) {
this.$refs.captcha.getImageCaptcha(focus)
},
initClick() {
this.start = debounce(() => {
if (this.reverseNumber != 0) return;
this.sendMsg();
})
},
sendMsg() {
if (this.captcha.length != 4) {
this.$refs.captcha.focusCaptchaInput = true
return uni.showToast({
title: '请先输入图形验证码',
icon: 'none'
});
}
if(!this.email) return uni.showToast({
title: "请输入邮箱",
icon: 'none'
});
let reg_email = /@/;
if (!reg_email.test(this.email)) return uni.showToast({
title: "邮箱格式错误",
icon: 'none'
});
const uniIdCo = uniCloud.importObject("uni-id-co", {
customUI: true
})
console.log('uniIdCo', uniIdCo)
console.log('sendEmailCode',{
"email": this.email,
"scene": this.type,
"captcha": this.captcha
});
uniIdCo.sendEmailCode({
"email": this.email,
"scene": this.type,
"captcha": this.captcha
}).then(result => {
console.log(result.code);
uni.showToast({
title: "邮箱验证码发送成功",
icon: 'none'
});
this.reverseNumber = Number(this.count);
this.getCode();
}).catch(e => {
console.log(JSON.stringify(e));
if (e.code == "uni-id-invalid-mail-template") {
this.modelValue = "123456"
uni.showToast({
title: '已启动测试模式,详情【控制台信息】',
icon: 'none',
duration: 3000
});
console.warn(e.message);
} else {
this.getImageCaptcha()
this.captcha = ""
uni.showToast({
title: e.message,
icon: 'none'
});
}
})
},
getCode() {
if (this.reverseNumber == 0) {
clearTimeout(this.reverseTimer);
this.reverseTimer = null;
return;
}
this.reverseNumber--;
this.reverseTimer = setTimeout(() => {
this.getCode();
}, 1000)
}
}
}
</script>
<style lang="scss" scoped>
.box {
position: relative;
margin-top: 10px;
}
.short-code-btn {
padding: 0;
position: absolute;
top: 0;
right: 8px;
width: 260rpx;
max-width: 130px;
height: 44px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
}
.inner-text {
font-size: 14px;
color: #AAAAAA;
}
.inner-text-active {
color: #04498c;
}
.captcha {
width: 350rpx;
}
.input-box {
margin: 0;
padding: 4px;
background-color: #F8F8F8;
font-size: 14px;
}
.box ::v-deep .content-clear-icon {
margin-right: 100px;
}
.box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
</style>
...@@ -15,12 +15,12 @@ ...@@ -15,12 +15,12 @@
<script> <script>
import config from '@/uni_modules/uni-id-pages/config.js' import config from '@/uni_modules/uni-id-pages/config.js'
//前一个窗口的页面地址。控制点击切换快捷登录方式是创建还是返回 //前一个窗口的页面地址。控制点击切换快捷登录方式是创建还是返回
import loginSuccess from '../../common/loginSuccess.js'; import loginSuccess from '../../common/loginSuccess.js';
const db = uniCloud.database(); const db = uniCloud.database();
const usersTable = db.collection('uni-id-users') const usersTable = db.collection('uni-id-users')
let allServicesList = [] let allServicesList = []
export default { export default {
computed: { computed: {
agreements() { agreements() {
...@@ -42,17 +42,17 @@ ...@@ -42,17 +42,17 @@
] ]
}, },
agree: { agree: {
get() { get() {
return this.getParentComponent().agree return this.getParentComponent().agree
}, },
set(agree) { set(agree) {
console.log('setAgree', agree); console.log('setAgree', agree);
return this.getParentComponent().agree = agree return this.getParentComponent().agree = agree
} }
} }
}, },
data() { data() {
return { return {
servicesList: [{ servicesList: [{
"id": "username", "id": "username",
"text": "账号登录", "text": "账号登录",
...@@ -232,7 +232,7 @@ ...@@ -232,7 +232,7 @@
} }
}, },
async login_before(type, navigateBack = true) { async login_before(type, navigateBack = true) {
console.log(type); console.log(type);
//提示空实现 //提示空实现
if (["qq", if (["qq",
"xiaomi", "xiaomi",
...@@ -288,46 +288,46 @@ ...@@ -288,46 +288,46 @@
} }
//判断是否需要弹出隐私协议授权框 //判断是否需要弹出隐私协议授权框
console.log(type, this.agree); console.log(type, this.agree);
let needAgreements = (config?.agreements?.scope || []).includes('register') let needAgreements = (config?.agreements?.scope || []).includes('register')
console.log({ console.log({
needAgreements needAgreements
}); });
if (type != 'univerify' && needAgreements && !this.agree) { if (type != 'univerify' && needAgreements && !this.agree) {
let agreementsRef = this.getParentComponent().$refs.agreements let agreementsRef = this.getParentComponent().$refs.agreements
return agreementsRef.popup(() => { return agreementsRef.popup(() => {
console.log(type, navigateBack); console.log(type, navigateBack);
this.login_before(type, navigateBack) this.login_before(type, navigateBack)
}) })
} }
// #ifdef H5 // #ifdef H5
if(type == 'weixin'){ if(type == 'weixin'){
// console.log('开始微信网页登录'); // console.log('开始微信网页登录');
let redirectUrl = location.protocol +'//'+ let redirectUrl = location.protocol +'//'+
document.domain + document.domain +
(window.location.href.includes('#')?'/#':'') + (window.location.href.includes('#')?'/#':'') +
'/uni_modules/uni-id-pages/pages/login/login-withoutpwd?is_weixin_redirect=true&type=weixin' '/uni_modules/uni-id-pages/pages/login/login-withoutpwd?is_weixin_redirect=true&type=weixin'
console.log('redirectUrl----',redirectUrl); console.log('redirectUrl----',redirectUrl);
let ua = window.navigator.userAgent.toLowerCase(); let ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == 'micromessenger'){ if (ua.match(/MicroMessenger/i) == 'micromessenger'){
// console.log('在微信公众号内'); // console.log('在微信公众号内');
return window.open(`https://open.weixin.qq.com/connect/oauth2/authorize? return window.open(`https://open.weixin.qq.com/connect/oauth2/authorize?
appid=${config.appid.weixin.h5} appid=${config.appid.weixin.h5}
&redirect_uri=${encodeURIComponent(redirectUrl)} &redirect_uri=${encodeURIComponent(redirectUrl)}
&response_type=code &response_type=code
&scope=snsapi_userinfo &scope=snsapi_userinfo
&state=STATE&connect_redirect=1#wechat_redirect`); &state=STATE&connect_redirect=1#wechat_redirect`);
}else{ }else{
// console.log('非微信公众号内'); // console.log('非微信公众号内');
return location.href = `https://open.weixin.qq.com/connect/qrconnect?appid=${config.appid.weixin.web} return location.href = `https://open.weixin.qq.com/connect/qrconnect?appid=${config.appid.weixin.web}
&redirect_uri=${encodeURIComponent(redirectUrl)} &redirect_uri=${encodeURIComponent(redirectUrl)}
&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect` &response_type=code&scope=snsapi_login&state=STATE#wechat_redirect`
} }
} }
// #endif // #endif
uni.showLoading({ uni.showLoading({
mask: true mask: true
...@@ -419,7 +419,7 @@ ...@@ -419,7 +419,7 @@
} }
}) })
}, },
login(params, type) { //联网验证登录 login(params, type) { //联网验证登录
console.log('执行登录开始----'); console.log('执行登录开始----');
console.log({ console.log({
params, params,
...@@ -446,9 +446,9 @@ ...@@ -446,9 +446,9 @@
}) })
return this.$refs.userProfile.open(result.uid) return this.$refs.userProfile.open(result.uid)
} }
// #endif // #endif
// #ifdef H5 // #ifdef H5
result.loginType = type result.loginType = type
// #endif // #endif
loginSuccess(result) loginSuccess(result)
}) })
...@@ -511,10 +511,17 @@ ...@@ -511,10 +511,17 @@
flex-wrap: wrap; flex-wrap: wrap;
width: 750rpx; width: 750rpx;
justify-content: space-around; justify-content: space-around;
position: fixed; position: fixed;
bottom: 10rpx;
left: 0; left: 0;
} }
.item {
flex-direction: column;
justify-content: center;
align-items: center;
height: 200rpx;
cursor: pointer;
}
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
...@@ -522,17 +529,20 @@ ...@@ -522,17 +529,20 @@
max-width: 500px; max-width: 500px;
margin-left: calc(50% - 250px); margin-left: calc(50% - 250px);
} }
.item {
height: 160rpx;
}
}
@media screen and (max-width: 690px) {
.fab-login-box {
bottom: 10rpx;
}
} }
/* #endif */ /* #endif */
.item {
flex-direction: column;
justify-content: center;
align-items: center;
height: 200rpx;
cursor: pointer;
}
.logo { .logo {
width: 60rpx; width: 60rpx;
......
export default { export default {
//调试模式 //调试模式
"debug": true, "debug": false,
/* /*
登录类型 未列举到的或运行环境不支持的,将被自动隐藏。 登录类型 未列举到的或运行环境不支持的,将被自动隐藏。
如果需要在不同平台有不同的配置,直接用条件编译即可 如果需要在不同平台有不同的配置,直接用条件编译即可
...@@ -43,16 +43,13 @@ export default { ...@@ -43,16 +43,13 @@ export default {
"web": "xxxxxx" "web": "xxxxxx"
} }
}, },
/** /**
* 密码强度 * 密码强度
* superstrong(超强:密码必须包含大小写字母、数字和特殊符号) * super(超强:密码必须包含大小写字母、数字和特殊符号,长度范围:8-16位之间)
* strong(强: 密码必须包含字母、数字和特殊符号) * strong(强: 密密码必须包含字母、数字和特殊符号,长度范围:8-16位之间)
* medium (中:密码必须为字母、数字和特殊符号任意两种的组合) * medium (中:密码必须为字母、数字和特殊符号任意两种的组合,长度范围:8-16位之间)
* weak(弱:密码必须包含字母) * weak(弱:密码必须包含字母和数字,长度范围:6-16位之间)
* 为空或false则不验证密码强度 * 为空或false则不验证密码强度
*/ */
"password": { "passwordStrength":"medium"
// "strength": "strong",
"length": [6, 20] //密码长度,默认在6-20位之间
}
} }
<!-- 短信验证码登录页 --> <!-- 短信验证码登录页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<view class="login-logo">
<image :src="logo"></image>
</view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title">请输入验证码</text> <text class="title">请输入验证码</text>
<text class="tip">先输入图形验证码,再获取短信验证码</text> <text class="tip">先输入图形验证码,再获取短信验证码</text>
...@@ -21,6 +24,7 @@ ...@@ -21,6 +24,7 @@
"code": "", "code": "",
"phone": "", "phone": "",
"captcha": "", "captcha": "",
"logo": "/static/logo.png"
} }
}, },
computed: { computed: {
......
<!-- 免密登录页 --> <!-- 免密登录页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<view class="login-logo">
<image :src="logo"></image>
</view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title">请选择登录方式</text> <text class="title">请选择登录方式</text>
<!-- 快捷登录框 当url带参数时有效 --> <!-- 快捷登录框 当url带参数时有效 -->
...@@ -36,7 +39,8 @@ ...@@ -36,7 +39,8 @@
return { return {
type: "", //快捷登录方式 type: "", //快捷登录方式
phone: "", //手机号码 phone: "", //手机号码
focusPhone:false focusPhone:false,
logo: "/static/logo.png"
} }
}, },
computed: { computed: {
...@@ -177,7 +181,7 @@ ...@@ -177,7 +181,7 @@
} }
.quickLogin { .quickLogin {
width: 650rpx; // width: 650rpx;
height: 350px; height: 350px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
...@@ -186,6 +190,9 @@ ...@@ -186,6 +190,9 @@
.quickLoginBtn { .quickLoginBtn {
margin: 20px 0; margin: 20px 0;
width: 450rpx; width: 450rpx;
/* #ifndef APP-NVUE */
max-width: 230px;
/* #endif */
height: 82rpx; height: 82rpx;
} }
...@@ -193,4 +200,10 @@ ...@@ -193,4 +200,10 @@
margin-top: -15px; margin-top: -15px;
margin-bottom: 20px; margin-bottom: 20px;
} }
@media screen and (min-width: 690px) {
.quickLogin{
height: auto;
}
}
</style> </style>
<!-- 账号密码登录页 --> <!-- 账号密码登录页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<view class="login-logo">
<image :src="logo"></image>
</view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title title-box">账号密码登录</text> <text class="title title-box">账号密码登录</text>
<uni-forms> <uni-forms>
<uni-forms-item name="username"> <uni-forms-item name="username">
<uni-easyinput :focus="focusUsername" @blur="focusUsername = false" class="input-box" :inputBorder="false" v-model="username" placeholder="请输入手机号/用户名" /> <uni-easyinput :focus="focusUsername" @blur="focusUsername = false" class="input-box" :inputBorder="false" v-model="username" placeholder="请输入手机号/用户名/邮箱" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password"> <uni-forms-item name="password">
<uni-easyinput :focus="focusPassword" @blur="focusPassword = false" class="input-box" clearable type="password" :inputBorder="false" v-model="password" <uni-easyinput :focus="focusPassword" @blur="focusPassword = false" class="input-box" clearable type="password" :inputBorder="false" v-model="password"
...@@ -45,7 +48,8 @@ ...@@ -45,7 +48,8 @@
"captcha": "", "captcha": "",
"needCaptcha": false, "needCaptcha": false,
"focusUsername":false, "focusUsername":false,
"focusPassword":false "focusPassword":false,
"logo": "/static/logo.png"
} }
}, },
onShow() { onShow() {
...@@ -84,7 +88,7 @@ ...@@ -84,7 +88,7 @@
if(!this.username.length){ if(!this.username.length){
this.focusUsername = true this.focusUsername = true
return uni.showToast({ return uni.showToast({
title: '请输入手机号/用户名', title: '请输入手机号/用户名/邮箱',
icon: 'none' icon: 'none'
}); });
} }
...@@ -107,6 +111,8 @@ ...@@ -107,6 +111,8 @@
if (/^1\d{10}$/.test(this.username)) { if (/^1\d{10}$/.test(this.username)) {
data.mobile = this.username data.mobile = this.username
}else if(/@/.test(this.username)) {
data.email = this.username
}else{ }else{
data.username = this.username data.username = this.username
} }
...@@ -135,7 +141,9 @@ ...@@ -135,7 +141,9 @@
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
@media screen and (min-width: 690px) {
}
.forget{ .forget{
font-size: 12px; font-size: 12px;
color: #8a8f8b; color: #8a8f8b;
......
<!-- 邮箱验证码注册 -->
<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: 550px;
}
.link-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
margin-top: 10px;
}
.link {
font-size: 12px;
}
}
.uni-content ::v-deep .uni-forms-item__label {
position: absolute;
left: -15px;
}
button {
margin-top: 15px;
}
</style>
<!-- 账号注册页 --> <!-- 账号注册页 -->
<template> <template>
<view class="uni-content"> <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 ref="form" :value="formData" :rules="rules" validate-trigger="submit" err-show-type="toast">
<uni-forms-item name="username" required> <uni-forms-item name="username" required>
<uni-easyinput :inputBorder="false" :focus="focusUsername" @blur="focusUsername = false" <uni-easyinput :inputBorder="false" :focus="focusUsername" @blur="focusUsername = false"
...@@ -12,7 +19,7 @@ ...@@ -12,7 +19,7 @@
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password" v-model="formData.password" required> <uni-forms-item name="password" v-model="formData.password" required>
<uni-easyinput :inputBorder="false" :focus="focusPassword" @blur="focusPassword = false" <uni-easyinput :inputBorder="false" :focus="focusPassword" @blur="focusPassword = false"
class="input-box" maxlength="20" :placeholder="'请输入' + passwordLength[0] + '-' + passwordLength[1] + '位密码'" type="password" class="input-box" maxlength="20" :placeholder="'请输入' + (config.passwordStrength == 'weak'?'6':'8') + '-16位密码'" type="password"
v-model="formData.password" trim="both" /> v-model="formData.password" trim="both" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password2" v-model="formData.password2" required> <uni-forms-item name="password2" v-model="formData.password2" required>
...@@ -25,7 +32,13 @@ ...@@ -25,7 +32,13 @@
</uni-forms-item> </uni-forms-item>
<uni-id-pages-agreements scope="register" ref="agreements" ></uni-id-pages-agreements> <uni-id-pages-agreements scope="register" ref="agreements" ></uni-id-pages-agreements>
<button class="uni-btn" type="primary" @click="submit">注册</button> <button class="uni-btn" type="primary" @click="submit">注册</button>
<button @click="navigateBack">返回</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> </uni-forms>
</view> </view>
</template> </template>
...@@ -50,12 +63,8 @@ ...@@ -50,12 +63,8 @@
focusUsername:false, focusUsername:false,
focusNickname:false, focusNickname:false,
focusPassword:false, focusPassword:false,
focusPassword2:false focusPassword2:false,
} logo: "/static/logo.png"
},
computed:{
passwordLength(){
return config.password.length
} }
}, },
onReady() { onReady() {
...@@ -111,6 +120,16 @@ ...@@ -111,6 +120,16 @@
}, },
navigateBack() { navigateBack() {
uni.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'
})
} }
} }
} }
...@@ -118,9 +137,33 @@ ...@@ -118,9 +137,33 @@
<style lang="scss"> <style lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
.uni-content{
margin-top: 15px; @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;
}
.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 { .uni-content ::v-deep .uni-forms-item__label {
position: absolute; position: absolute;
left: -15px; left: -15px;
......
<!-- 找回密码页 -->
<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>
</view>
</match-media>
</uni-forms>
<uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="reset-pwd-by-sms" ref="popup"></uni-popup-captcha>
</view>
</template>
<script>
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
import passwordMod from '@/uni_modules/uni-id-pages/common/password.js'
const uniIdCo = uniCloud.importObject("uni-id-co",{
errorOptions:{
type:'toast'
}
})
export default {
mixins: [mixin],
data() {
return {
lock: false,
focusEmail:true,
focusPassword:false,
focusPassword2:false,
formData: {
"email": "",
"code": "",
'password': '',
'password2': '',
"captcha": ""
},
rules: {
email: {
rules: [{
required: true,
errorMessage: '请输入邮箱',
},
{
format:'email',
errorMessage: '邮箱格式不正确',
}
]
},
code: {
rules: [{
required: true,
errorMessage: '请输入邮箱验证码',
},
{
pattern: /^.{6}$/,
errorMessage: '请输入6位验证码',
}
]
},
...passwordMod.getPwdRules()
},
logo: "/static/logo.png"
}
},
computed: {
isEmail() {
let reg_email = /@/;
let isEmail = reg_email.test(this.formData.email);
return isEmail;
},
isPwd() {
let reg_pwd = /^.{6,20}$/;
let isPwd = reg_pwd.test(this.formData.password);
return isPwd;
},
isCode() {
let reg_code = /^\d{6}$/;
let isCode = reg_code.test(this.formData.code);
return isCode;
}
},
onLoad(event) {
if (event && event.emailNumber) {
this.formData.email = event.emailNumber;
if(event.lock){
this.lock = event.lock //如果是已经登录的账号,点击找回密码就锁定指定的账号绑定的邮箱码
this.focusEmail = true
}
}
},
onReady() {
if (this.formData.email) {
this.$refs.shortCode.start();
}
this.$refs.form.setRules(this.rules)
},
onShow() {
// #ifdef H5
document.onkeydown = event => {
var e = event || window.event;
if (e && e.keyCode == 13) { //回车键的键值为13
this.submit()
}
};
// #endif
},
methods: {
/**
* 完成并提交
*/
submit() {
console.log("formData", this.formData);
console.log('rules', this.rules);
this.$refs.form.validate()
.then(res => {
let {
email,
password: password,
captcha,
code
} = this.formData
uniIdCo.resetPwdByEmail({
email,
code,
password,
captcha
}).then(e => {
console.log(e);
uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withpwd',
complete: (e) => {
console.log(e);
}
})
})
.catch(e => {
if (e.errCode == 'uni-id-captcha-required') {
this.$refs.popup.open()
}
}).finally(e => {
this.formData.captcha = ""
})
}).catch(errors=>{
let key = errors[0].key
if(key == 'code'){
console.log(this.$refs.shortCode);
return this.$refs.shortCode.focusSmsCodeInput = true
}
key = key.replace(key[0], key[0].toUpperCase())
console.log(key,'focus'+key);
this['focus'+key] = true
})
},
retrieveByPhone() {
uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/retrieve/retrieve'
})
}
}
}
</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;
}
.link-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
margin-top: 10px;
}
.link {
font-size: 12px;
}
}
</style>
<!-- 找回密码页 --> <!-- 找回密码页 -->
<template> <template>
<view class="uni-content"> <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 ref="form" :value="formData" err-show-type="toast">
<uni-forms-item name="phone"> <uni-forms-item name="phone">
<uni-easyinput :focus="focusPhone" @blur="focusPhone = false" class="input-box" :disabled="lock" type="number" :inputBorder="false" <uni-easyinput :focus="focusPhone" @blur="focusPhone = false" class="input-box" :disabled="lock" type="number" :inputBorder="false"
...@@ -20,6 +27,12 @@ ...@@ -20,6 +27,12 @@
placeholder="请再次输入新密码"></uni-easyinput> placeholder="请再次输入新密码"></uni-easyinput>
</uni-forms-item> </uni-forms-item>
<button class="uni-btn send-btn-box" type="primary" @click="submit">提交</button> <button class="uni-btn send-btn-box" type="primary" @click="submit">提交</button>
<match-media :min-width="690">
<view class="link-box">
<text class="link" @click="retrieveByEmail">通过邮箱验证码找回密码</text>
<view></view>
</view>
</match-media>
</uni-forms> </uni-forms>
<uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="reset-pwd-by-sms" ref="popup"></uni-popup-captcha> <uni-popup-captcha @confirm="submit" v-model="formData.captcha" scene="reset-pwd-by-sms" ref="popup"></uni-popup-captcha>
</view> </view>
...@@ -101,7 +114,8 @@ ...@@ -101,7 +114,8 @@
} }
] ]
} }
} },
logo: "/static/logo.png"
} }
}, },
computed: { computed: {
...@@ -187,6 +201,11 @@ ...@@ -187,6 +201,11 @@
console.log(key,'focus'+key); console.log(key,'focus'+key);
this['focus'+key] = true this['focus'+key] = true
}) })
},
retrieveByEmail() {
uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email'
})
} }
} }
} }
...@@ -195,7 +214,26 @@ ...@@ -195,7 +214,26 @@
<style lang="scss"> <style lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
.uni-content { @media screen and (max-width: 690px) {
margin-top: 15px; .uni-content{
margin-top: 15px;
}
}
@media screen and (min-width: 690px) {
.uni-content{
padding: 30px 40px 40px;
}
.link-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
margin-top: 10px;
}
.link {
font-size: 12px;
}
} }
</style> </style>
<!-- 绑定手机号码页 --> <!-- 绑定手机号码页 -->
<template> <template>
<view class="uni-content"> <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" <uni-easyinput clearable :focus="focusMobile" @blur="focusMobile = false" type="number" class="input-box" :inputBorder="false" v-model="formData.mobile"
maxlength="11" placeholder="请输入手机号"></uni-easyinput> maxlength="11" placeholder="请输入手机号"></uni-easyinput>
...@@ -20,7 +27,8 @@ ...@@ -20,7 +27,8 @@
code: "", code: "",
captcha: "" captcha: ""
}, },
focusMobile:true focusMobile:true,
logo: "/static/logo.png"
} }
}, },
computed: { computed: {
...@@ -90,6 +98,13 @@ ...@@ -90,6 +98,13 @@
padding: 50rpx; padding: 50rpx;
padding-top: 10px; padding-top: 10px;
} }
@media screen and (min-width: 690px) {
.uni-content{
padding: 30px 40px 40px;
}
}
/* #ifndef APP-NVUE || VUE3 */ /* #ifndef APP-NVUE || VUE3 */
.uni-content ::v-deep .uni-easyinput__content {} .uni-content ::v-deep .uni-easyinput__content {}
......
<!-- 修改密码 --> <!-- 修改密码 -->
<template> <template>
<view class="uni-content"> <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 ref="form" :value="formData" err-show-type="toast">
<uni-forms-item name="oldPassword"> <uni-forms-item name="oldPassword">
<uni-easyinput :focus="focusOldPassword" @blur="focusOldPassword = false" class="input-box" <uni-easyinput :focus="focusOldPassword" @blur="focusOldPassword = false" class="input-box"
...@@ -81,7 +88,8 @@ ...@@ -81,7 +88,8 @@
} }
] ]
} }
} },
logo: "/static/logo.png"
} }
}, },
onReady() { onReady() {
...@@ -140,7 +148,15 @@ ...@@ -140,7 +148,15 @@
<style lang="scss"> <style lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
.uni-content { @media screen and (max-width: 690px) {
margin-top: 15px; .uni-content{
margin-top: 15px;
}
}
@media screen and (min-width: 690px) {
.uni-content{
padding: 30px 40px 40px;
}
} }
</style> </style>
<!-- 注销(销毁)账号 --> <!-- 注销(销毁)账号 -->
<template> <template>
<view class="content"> <view class="uni-content">
<text class="words" space="emsp"> <text class="words" space="emsp">
一、注销是不可逆操作,注销后:\n 一、注销是不可逆操作,注销后:\n
1.帐号将无法登录、无法找回。\n 1.帐号将无法登录、无法找回。\n
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
</script> </script>
<style> <style>
.content { .uni-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font-size: 28rpx; font-size: 28rpx;
...@@ -77,20 +77,6 @@ ...@@ -77,20 +77,6 @@
margin-bottom: 80px; margin-bottom: 80px;
} }
.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;
}
.button-group button { .button-group button {
border-radius: 100px; border-radius: 100px;
border: none; border: none;
...@@ -108,4 +94,26 @@ ...@@ -108,4 +94,26 @@
color: #e64340; color: #e64340;
border: solid 1px #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> </style>
const db = uniCloud.database();
const usersTable = db.collection('uni-id-users')
const uniIdCo = uniCloud.importObject("uni-id-co")
export default {
data() {
return {
univerifyStyle: {
authButton: {
"title": "本机号码一键绑定", // 授权按钮文案
},
otherLoginButton: {
"title": "其他号码绑定",
}
},
userInfo: {
mobile: '',
nickname: ''
},
hasLogin: false,
hasPwd: false
}
},
async onShow() {
this.univerifyStyle.authButton.title = "本机号码一键绑定"
this.univerifyStyle.otherLoginButton.title = "其他号码绑定"
},
async onLoad() {
this.getUserInfo()
//判断当前用户是否有密码,否则就不显示密码修改功能
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);
}
})
},
async logout() {
await uniIdCo.logout()
uni.removeStorageSync('uni_id_token');
uni.setStorageSync('uni_id_token_expired', 0)
uni.redirectTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
});
},
changePassword() {
uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd',
complete: (e) => {
console.log(e);
}
})
},
getUserInfo(e) {
uni.showLoading({
mask: true
});
usersTable.where("'_id' == $cloudEnv_uid").field('mobile,nickname').get().then(res => {
console.log({
res
});
this.userInfo = res.result.data[0]
console.log('this.userInfo', this.userInfo);
this.hasLogin = true
}).catch(e => {
this.userInfo = {}
this.hasLogin = false
console.log(e.message, e.errCode);
}).finally(e => {
// console.log(e);
uni.hideLoading()
})
},
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'].open()
// #endif
// #ifdef H5
//...去用验证码绑定
this.bindMobileBySmsCode()
// #endif
},
univerify() {
uni.login({
"provider": 'univerify',
"univerifyStyle": this.univerifyStyle,
success: async e => {
console.log(e.authResult);
uniIdCo.bindMobileByUniverify(e.authResult).then(res => {
console.log(res);
this.getUserInfo()
}).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',
events: {
getUserInfo: () => {
this.getUserInfo()
}
},
complete(e) {
console.log(e);
}
})
},
setNickname(nickname) {
console.log(nickname);
if (nickname) {
usersTable.where('_id==$env.uid').update({
nickname
}).then(e => {
console.log(e);
if (e.result.updated) {
uni.showToast({
title: "更新成功",
icon: 'none'
});
this.userInfo.nickname = nickname
} else {
uni.showToast({
title: "没有改变",
icon: 'none'
});
}
})
this.$refs.dialog.close()
} else {
this.$refs.dialog.open()
}
},
deactivate() {
uni.navigateTo({
url: "/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate"
})
}
}
}
<!-- 用户资料页 --> <!-- 用户资料页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<view class="avatar"> <view class="avatar">
<uni-id-pages-avatar width="260rpx" height="260rpx"></uni-id-pages-avatar> <uni-id-pages-avatar width="260rpx" height="260rpx"></uni-id-pages-avatar>
</view> </view>
<uni-list> <uni-list>
<uni-list-item class="item" @click="setNickname('')" title="昵称" :rightText="userInfo.nickname||'未设置'" link> <uni-list-item class="item" @click="setNickname('')" title="昵称" :rightText="userInfo.nickname||'未设置'" link>
</uni-list-item> </uni-list-item>
<uni-list-item class="item" @click="bindMobile" title="手机号" :rightText="userInfo.mobile||'未绑定'" link> <uni-list-item class="item" @click="bindMobile" title="手机号" :rightText="userInfo.mobile||'未绑定'" link>
</uni-list-item> </uni-list-item>
<uni-list-item v-if="hasPwd" class="item" @click="changePassword" title="修改密码" link> <uni-list-item v-if="hasPwd" class="item" @click="changePassword" title="修改密码" link>
</uni-list-item> </uni-list-item>
</uni-list> </uni-list>
<uni-list class="mt10"> <uni-list class="mt10">
<uni-list-item @click="deactivate" title="注销账号" link="navigateTo"></uni-list-item> <uni-list-item @click="deactivate" title="注销账号" link="navigateTo"></uni-list-item>
</uni-list> </uni-list>
<uni-popup ref="dialog" type="dialog"> <uni-popup ref="dialog" type="dialog">
<uni-popup-dialog mode="input" :value="userInfo.nickname" @confirm="setNickname" title="设置昵称" <uni-popup-dialog mode="input" :value="userInfo.nickname" @confirm="setNickname" title="设置昵称"
placeholder="请输入要设置的昵称"> placeholder="请输入要设置的昵称">
</uni-popup-dialog> </uni-popup-dialog>
</uni-popup> </uni-popup>
<uni-id-pages-bind-mobile ref="bind-mobile-by-sms" @success="getUserInfo"></uni-id-pages-bind-mobile> <uni-id-pages-bind-mobile ref="bind-mobile-by-sms" @success="getUserInfo"></uni-id-pages-bind-mobile>
<button v-if="hasLogin" @click="logout">退出登录</button> <template v-if="showLoginManage">
<button v-else @click="login">去登录</button> <button v-if="hasLogin" @click="logout">退出登录</button>
</view> <button v-else @click="login">去登录</button>
</template> </template>
<script> </view>
const db = uniCloud.database(); </template>
const usersTable = db.collection('uni-id-users') <script>
const uniIdCo = uniCloud.importObject("uni-id-co") const db = uniCloud.database();
export default { const usersTable = db.collection('uni-id-users')
data() { const uniIdCo = uniCloud.importObject("uni-id-co")
return { import common from '@/uni_modules/uni-id-pages/common/common.js';
univerifyStyle: { export default {
authButton: { data() {
"title": "本机号码一键绑定", // 授权按钮文案 return {
}, univerifyStyle: {
otherLoginButton: { authButton: {
"title": "其他号码绑定", "title": "本机号码一键绑定", // 授权按钮文案
} },
}, otherLoginButton: {
userInfo: { "title": "其他号码绑定",
mobile:'', }
nickname:'' },
}, userInfo: {
mobile:'',
nickname:''
},
hasLogin: false, hasLogin: false,
hasPwd:false hasPwd:false,
} showLoginManage:false//通过页面传参隐藏登录&退出登录按钮
}, }
async onShow() { },
this.univerifyStyle.authButton.title = "本机号码一键绑定" async onShow() {
this.univerifyStyle.otherLoginButton.title = "其他号码绑定" this.univerifyStyle.authButton.title = "本机号码一键绑定"
}, this.univerifyStyle.otherLoginButton.title = "其他号码绑定"
async onLoad() { },
async onLoad(e) {
if(e.showLoginManage){
this.showLoginManage = true//通过页面传参隐藏登录&退出登录按钮
}
this.getUserInfo() this.getUserInfo()
//判断当前用户是否有密码,否则就不显示密码修改功能 //判断当前用户是否有密码,否则就不显示密码修改功能
let res = await uniIdCo.getAccountInfo() let res = await uniIdCo.getAccountInfo()
this.hasPwd = res.isPasswordSet this.hasPwd = res.isPasswordSet
}, },
methods: { methods: {
login() { login() {
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd', url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
complete: (e) => { complete: (e) => {
console.log(e); console.log(e);
} }
}) })
},
async logout() {
await uniIdCo.logout()
uni.removeStorageSync('uni_id_token');
uni.setStorageSync('uni_id_token_expired', 0)
uni.redirectTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
});
}, },
logout:common.logout,
changePassword(){ changePassword(){
uni.navigateTo({ uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd', url: '/uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd',
...@@ -82,155 +82,165 @@ ...@@ -82,155 +82,165 @@
console.log(e); console.log(e);
} }
}) })
}, },
getUserInfo(e) { getUserInfo(e) {
uni.showLoading({ uni.showLoading({
mask: true mask: true
}); });
usersTable.where("'_id' == $cloudEnv_uid").field('mobile,nickname').get().then(res => { usersTable.where("'_id' == $cloudEnv_uid").field('mobile,nickname').get().then(res => {
console.log({res}); console.log({res});
this.userInfo = res.result.data[0] this.userInfo = res.result.data[0]
console.log('this.userInfo', this.userInfo); console.log('this.userInfo', this.userInfo);
this.hasLogin = true this.hasLogin = true
}).catch(e => { }).catch(e => {
this.userInfo = {} this.userInfo = {}
this.hasLogin = false this.hasLogin = false
console.log(e.message, e.errCode); console.log(e.message, e.errCode);
}).finally(e => { }).finally(e => {
// console.log(e); // console.log(e);
uni.hideLoading() uni.hideLoading()
}) })
}, },
bindMobile() { bindMobile() {
// #ifdef APP-PLUS // #ifdef APP-PLUS
uni.preLogin({ uni.preLogin({
provider: 'univerify', provider: 'univerify',
success: this.univerify(), //预登录成功 success: this.univerify(), //预登录成功
fail: (res) => { // 预登录失败 fail: (res) => { // 预登录失败
// 不显示一键登录选项(或置灰) // 不显示一键登录选项(或置灰)
console.log(res) console.log(res)
this.bindMobileBySmsCode() this.bindMobileBySmsCode()
} }
}) })
// #endif // #endif
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
this.$refs['bind-mobile'].open() this.$refs['bind-mobile'].open()
// #endif // #endif
// #ifdef H5 // #ifdef H5
//...去用验证码绑定 //...去用验证码绑定
this.bindMobileBySmsCode() this.bindMobileBySmsCode()
// #endif // #endif
}, },
univerify() { univerify() {
uni.login({ uni.login({
"provider": 'univerify', "provider": 'univerify',
"univerifyStyle": this.univerifyStyle, "univerifyStyle": this.univerifyStyle,
success: async e => { success: async e => {
console.log(e.authResult); console.log(e.authResult);
uniIdCo.bindMobileByUniverify(e.authResult).then(res => { uniIdCo.bindMobileByUniverify(e.authResult).then(res => {
console.log(res); console.log(res);
this.getUserInfo() this.getUserInfo()
}).catch(e => { }).catch(e => {
console.log(e); console.log(e);
}).finally(e=>{ }).finally(e=>{
console.log(e); console.log(e);
uni.closeAuthView() uni.closeAuthView()
}) })
}, },
fail: (err) => { fail: (err) => {
console.log(err); console.log(err);
if (err.code == '30002' || err.code == '30001') { if (err.code == '30002' || err.code == '30001') {
this.bindMobileBySmsCode() this.bindMobileBySmsCode()
} }
} }
}) })
}, },
bindMobileBySmsCode() { bindMobileBySmsCode() {
uni.navigateTo({ uni.navigateTo({
url: './bind-mobile/bind-mobile', url: './bind-mobile/bind-mobile',
events: { events: {
getUserInfo: () => { getUserInfo: () => {
this.getUserInfo() this.getUserInfo()
} }
}, },
complete(e) { complete(e) {
console.log(e); console.log(e);
} }
}) })
}, },
setNickname(nickname) { setNickname(nickname) {
console.log(nickname); console.log(nickname);
if (nickname) { if (nickname) {
usersTable.where('_id==$env.uid').update({ usersTable.where('_id==$env.uid').update({
nickname nickname
}).then(e => { }).then(e => {
console.log(e); console.log(e);
if (e.result.updated) { if (e.result.updated) {
uni.showToast({ uni.showToast({
title: "更新成功", title: "更新成功",
icon: 'none' icon: 'none'
}); });
this.userInfo.nickname = nickname this.userInfo.nickname = nickname
} else { } else {
uni.showToast({ uni.showToast({
title: "没有改变", title: "没有改变",
icon: 'none' icon: 'none'
}); });
} }
}) })
this.$refs.dialog.close() this.$refs.dialog.close()
} else { } else {
this.$refs.dialog.open() this.$refs.dialog.open()
} }
}, },
deactivate(){ deactivate(){
uni.navigateTo({ uni.navigateTo({
url:"/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate" url:"/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate"
}) })
} }
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import url("/uni_modules/uni-id-pages/common/login-page.scss"); @import url("/uni_modules/uni-id-pages/common/login-page.scss");
.uni-content { .uni-content {
padding: 0; padding: 0;
} }
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
view { view {
display: flex; display: flex;
box-sizing: border-box; box-sizing: border-box;
flex-direction: column; flex-direction: column;
} }
@media screen and (min-width: 690px) {
/* #endif */ .uni-content {
.avatar { padding: 0;
align-items: center; max-width: 690px;
justify-content: center; margin-left: calc(50% - 345px);
margin: 22px 0; border: none;
width: 100%; max-height: none;
} border-radius: 0;
box-shadow: none;
.item { }
flex: 1; }
flex-direction: row; /* #endif */
justify-content: space-between; .avatar {
align-items: center; align-items: center;
} justify-content: center;
margin: 22px 0;
button { width: 100%;
margin: 10%; }
margin-top: 40px;
border-radius: 0; .item {
background-color: #FFFFFF; flex: 1;
width: 80%; flex-direction: row;
} justify-content: space-between;
align-items: center;
.mt10{ }
margin-top: 10px;
} button {
</style> margin: 10%;
margin-top: 40px;
border-radius: 0;
background-color: #FFFFFF;
width: 80%;
}
.mt10{
margin-top: 10px;
}
</style>
{
"pages": [
{
"path": "uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate",
"style": {
"navigationBarTitleText": "注销账号"
}
},
{
"path": "uni_modules/uni-id-pages/pages/userinfo/userinfo",
"style": {
"navigationBarTitleText": "个人资料"
}
},
{
"path": "uni_modules/uni-id-pages/pages/userinfo/bind-mobile/bind-mobile",
"style": {
"navigationBarTitleText": "绑定手机号码"
}
},
{
"path": "uni_modules/uni-id-pages/pages/userinfo/cropImage/cropImage",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "uni_modules/uni-id-pages/pages/login/login-withoutpwd",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "uni_modules/uni-id-pages/pages/login/login-withpwd",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "uni_modules/uni-id-pages/pages/login/login-smscode",
"style": {
"navigationBarTitleText": "手机验证码登录"
}
},
{
"path": "uni_modules/uni-id-pages/pages/register/register",
"style": {
"navigationBarTitleText": "注册"
}
},
{
"path": "uni_modules/uni-id-pages/pages/register/register-by-email",
"style": {
"navigationBarTitleText": "邮箱验证码注册"
}
},
{
"path": "uni_modules/uni-id-pages/pages/retrieve/retrieve",
"style": {
"navigationBarTitleText": "重置密码"
}
},
{
"path": "uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email",
"style": {
"navigationBarTitleText": "通过邮箱重置密码"
}
},
{
"path": "uni_modules/uni-id-pages/pages/common/webview/webview",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd",
"style" :
{
"navigationBarTitleText": "修改密码",
"enablePullDownRefresh": false
}
}
]
}
\ No newline at end of file
...@@ -29,6 +29,21 @@ baseValidator.username = function (username) { ...@@ -29,6 +29,21 @@ baseValidator.username = function (username) {
} }
} }
baseValidator.password = function (password) {
const errCode = ERROR.INVALID_PASSWORD
if (!isValidString(password)) {
return {
errCode
}
}
if (password.length < 6) {
// 密码长度不能小于6
return {
errCode
}
}
}
baseValidator.mobile = function (mobile) { baseValidator.mobile = function (mobile) {
const errCode = ERROR.INVALID_MOBILE const errCode = ERROR.INVALID_MOBILE
if (!isValidString(mobile)) { if (!isValidString(mobile)) {
...@@ -92,7 +107,7 @@ baseType.forEach((type) => { ...@@ -92,7 +107,7 @@ baseType.forEach((type) => {
} }
}) })
function tokenize (name) { function tokenize(name) {
let i = 0 let i = 0
const result = [] const result = []
let token = '' let token = ''
...@@ -122,7 +137,7 @@ function tokenize (name) { ...@@ -122,7 +137,7 @@ function tokenize (name) {
* 处理validator名 * 处理validator名
* @param {string} name * @param {string} name
*/ */
function parseValidatorName (name) { function parseValidatorName(name) {
const tokenList = tokenize(name) const tokenList = tokenize(name)
let i = 0 let i = 0
let currentToken = tokenList[i] let currentToken = tokenList[i]
...@@ -172,7 +187,7 @@ function parseValidatorName (name) { ...@@ -172,7 +187,7 @@ function parseValidatorName (name) {
return result return result
} }
function getRuleCategory (rule) { function getRuleCategory(rule) {
switch (rule.type) { switch (rule.type) {
case 'array': case 'array':
return 'array' return 'array'
...@@ -183,7 +198,7 @@ function getRuleCategory (rule) { ...@@ -183,7 +198,7 @@ function getRuleCategory (rule) {
} }
} }
function isMatchUnionType (val, rule) { function isMatchUnionType(val, rule) {
if (!rule.children || rule.children.length === 0) { if (!rule.children || rule.children.length === 0) {
return true return true
} }
...@@ -209,7 +224,7 @@ function isMatchUnionType (val, rule) { ...@@ -209,7 +224,7 @@ function isMatchUnionType (val, rule) {
return false return false
} }
function isMatchBaseType (val, rule) { function isMatchBaseType(val, rule) {
if (typeof baseValidator[rule.type] !== 'function') { if (typeof baseValidator[rule.type] !== 'function') {
throw new Error(`invalid schema type: ${rule.type}`) throw new Error(`invalid schema type: ${rule.type}`)
} }
...@@ -220,7 +235,7 @@ function isMatchBaseType (val, rule) { ...@@ -220,7 +235,7 @@ function isMatchBaseType (val, rule) {
return true return true
} }
function isMatchArrayType (arr, rule) { function isMatchArrayType(arr, rule) {
if (getType(arr) !== 'array') { if (getType(arr) !== 'array') {
return false return false
} }
...@@ -249,11 +264,12 @@ const passwordRules = { ...@@ -249,11 +264,12 @@ const passwordRules = {
// 密码必须为字母、数字和特殊符号任意两种的组合 // 密码必须为字母、数字和特殊符号任意两种的组合
medium: /^(?![0-9]+$)(?![a-zA-Z]+$)(?![~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]+$)[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/, medium: /^(?![0-9]+$)(?![a-zA-Z]+$)(?![~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]+$)[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/,
// 密码必须包含字母和数字 // 密码必须包含字母和数字
weak: /^(?=.*[0-9])(?=.*[a-zA-Z])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{6,16}$/ weak: /^(?=.*[0-9])(?=.*[a-zA-Z])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{6,16}$/,
} }
function createPasswordVerifier ({ function createPasswordVerifier({
passwordStrength = 'medium' passwordStrength = ''
} = {}) { } = {}) {
return function (password) { return function (password) {
const passwordRegExp = passwordRules[passwordStrength] const passwordRegExp = passwordRules[passwordStrength]
...@@ -275,28 +291,30 @@ function createPasswordVerifier ({ ...@@ -275,28 +291,30 @@ function createPasswordVerifier ({
} }
class Validator { class Validator {
constructor ({ constructor({
passwordStrength = 'medium' passwordStrength = ''
} = {}) { } = {}) {
this.baseValidator = baseValidator this.baseValidator = baseValidator
this.customValidator = Object.create(null) this.customValidator = Object.create(null)
this.mixin( if (passwordStrength) {
'password', this.mixin(
createPasswordVerifier({ 'password',
passwordStrength createPasswordVerifier({
}) passwordStrength
) })
)
}
} }
mixin (type, handler) { mixin(type, handler) {
this.customValidator[type] = handler this.customValidator[type] = handler
} }
getRealBaseValidator (type) { getRealBaseValidator(type) {
return this.customValidator[type] || this.baseValidator[type] return this.customValidator[type] || this.baseValidator[type]
} }
get validator () { get validator() {
return new Proxy({}, { return new Proxy({}, {
get: (_, prop) => { get: (_, prop) => {
if (typeof prop !== 'string') { if (typeof prop !== 'string') {
...@@ -318,7 +336,7 @@ class Validator { ...@@ -318,7 +336,7 @@ class Validator {
}) })
} }
validate (value = {}, schema = {}) { validate(value = {}, schema = {}) {
for (const schemaKey in schema) { for (const schemaKey in schema) {
let schemaValue = schema[schemaKey] let schemaValue = schema[schemaKey]
if (getType(schemaValue) === 'string') { if (getType(schemaValue) === 'string') {
...@@ -358,7 +376,7 @@ class Validator { ...@@ -358,7 +376,7 @@ class Validator {
} }
} }
function checkClientInfo (clientInfo) { function checkClientInfo(clientInfo) {
const stringNotRequired = { const stringNotRequired = {
required: false, required: false,
type: 'string' type: 'string'
......
const assert = require('assert')
const {
Validator
} = require('./validator')
const {
ERROR
} = require('./error')
const {
getType
} = require('./utils')
const testCaseList = [{
value: {
username: 'uname'
},
schema: {
username: 'username'
},
expected: undefined
}, {
value: {
username: '123456'
},
schema: {
username: 'username'
},
expected: {
errCode: ERROR.INVALID_USERNAME
}
}, {
value: {
username: '数字天堂'
},
schema: {
username: 'username'
},
expected: {
errCode: ERROR.INVALID_USERNAME
}
}, {
value: {
password: '123456abc'
},
schema: {
password: 'password'
}
}, {
value: {
password: '123456def'
},
schema: {
password: 'password'
},
mixin: [{
type: 'password',
handler: function (password) {
if (typeof password !== 'string' || password.length < 10) {
return {
errCode: ERROR.INVALID_PASSWORD
}
}
}
}],
expected: {
errCode: ERROR.INVALID_PASSWORD
}
}, {
value: {
numberOrString: '123456'
},
schema: {
numberOrString: 'number|string'
}
}, {
value: {
numberOrString: 123456
},
schema: {
numberOrString: 'number|string'
}
}, {
value: {
numberOrString: '123456'
},
schema: {
numberOrString: 'string|number'
}
}, {
value: {
numberOrString: 123456
},
schema: {
numberOrString: 'string|number'
}
}, {
value: {
numberOrString: ['123456']
},
schema: {
numberOrString: 'array<number|string>'
}
}, {
value: {
numberOrString: [123456]
},
schema: {
numberOrString: 'array<number|string>'
}
}, {
value: {
numberOrString: [123456, '123456']
},
schema: {
numberOrString: 'array<number|string>'
}
}, {
value: {
numberOrString: ['123456']
},
schema: {
numberOrString: 'array<string|number>'
}
}, {
value: {
numberOrString: [123456]
},
schema: {
numberOrString: 'array<string|number>'
}
}, {
value: {
numberOrString: [123456, '123456']
},
schema: {
numberOrString: 'array<string|number>'
}
}, {
value: {
numberOrString: ['123456']
},
mixin: [{
type: '1to6',
handler: function (val) {
if (val !== '123456') {
return {
errCode: ERROR.INVALID_PARAM
}
}
}
}],
schema: {
numberOrString: 'array<number|1to6>'
}
}]
function execTestCase ({
value = {},
schema = {},
mixin = [],
expected = undefined,
error = ''
} = {}) {
const validator = new Validator()
for (let i = 0; i < mixin.length; i++) {
const {
type,
handler
} = mixin[i]
validator.mixin(type, handler)
}
let validateResult,
validateError
try {
validateResult = validator.validate(value, schema)
} catch (err) {
validateError = err
}
const tag = JSON.stringify({ value, schema })
if (error) {
if (typeof error === 'string') {
assert.strictEqual(validateError.message.indexOf(error) > -1, true, `[${tag}] error message error`)
} else if (getType(error) === 'regexp') {
assert.strictEqual(error.test(validateError), true, `[${tag}] error message error`)
} else {
throw new Error(`[${tag}] invalid test case`)
}
return
}
if (expected === undefined) {
assert.strictEqual(validateResult, undefined, `[${tag}] validate result error`)
return
}
const expectedKeys = Object.keys(expected)
let passResultCheck = true
for (let i = 0; i < expectedKeys.length; i++) {
const key = expectedKeys[i]
if (expected[key] !== validateResult[key]) {
passResultCheck = false
break
}
}
assert.strictEqual(passResultCheck, true, `[${tag}] validate result error`)
}
for (let i = 0; i < testCaseList.length; i++) {
console.log(`test case: ${i}`)
execTestCase(testCaseList[i])
console.log(`test case: ${i}, pass`)
}
...@@ -7,6 +7,9 @@ module.exports = { ...@@ -7,6 +7,9 @@ module.exports = {
// permission: [] // 允许进行此操作的权限,包含任一权限均可操作。 // permission: [] // 允许进行此操作的权限,包含任一权限均可操作。
// 权限角色均配置时,用户拥有任一权限或任一角色均可操作 // 权限角色均配置时,用户拥有任一权限或任一角色均可操作
}, },
updateUser: {
role: ['admin']
},
authorizeAppLogin: { authorizeAppLogin: {
role: ['admin'] role: ['admin']
}, },
......
...@@ -18,7 +18,8 @@ const { ...@@ -18,7 +18,8 @@ const {
registerUser registerUser
} = require('./module/register/index') } = require('./module/register/index')
const { const {
addUser addUser,
updateUser
} = require('./module/admin/index') } = require('./module/admin/index')
const { const {
login, login,
...@@ -109,7 +110,7 @@ module.exports = { ...@@ -109,7 +110,7 @@ module.exports = {
this.hooks = this.configUtils.getHooks() this.hooks = this.configUtils.getHooks()
this.validator = new Validator({ this.validator = new Validator({
passwordStrength: this.config.passwordStrength || 'medium' passwordStrength: this.config.passwordStrength
}) })
/** /**
* 示例:覆盖密码验证规则 * 示例:覆盖密码验证规则
...@@ -216,6 +217,23 @@ module.exports = { ...@@ -216,6 +217,23 @@ module.exports = {
* @returns * @returns
*/ */
addUser, addUser,
/**
* 修改用户
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-user
* @param {Object} params
* @param {String} params.id 要更新的用户id
* @param {String} params.username 用户名
* @param {String} params.password 密码
* @param {String} params.nickname 昵称
* @param {Array} params.authorizedApp 允许登录的AppID列表
* @param {Array} params.role 用户角色列表
* @param {String} params.mobile 手机号
* @param {String} params.email 邮箱
* @param {Array} params.tags 用户标签
* @param {Number} params.status 用户状态
* @returns
*/
updateUser,
/** /**
* 授权用户登录应用 * 授权用户登录应用
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#authorize-app-login * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#authorize-app-login
...@@ -490,4 +508,4 @@ module.exports = { ...@@ -490,4 +508,4 @@ module.exports = {
* @returns * @returns
*/ */
getSupportedLoginType getSupportedLoginType
} }
...@@ -4,8 +4,8 @@ const { ...@@ -4,8 +4,8 @@ const {
const createConfig = require('uni-config-center') const createConfig = require('uni-config-center')
const requiredConfig = { const requiredConfig = {
'web.h5-weixin': ['appid', 'appsecret'], 'web.weixin-h5': ['appid', 'appsecret'],
'web.web-weixin': ['appid', 'appsecret'], 'web.weixin-web': ['appid', 'appsecret'],
'app.weixin': ['appid', 'appsecret'], 'app.weixin': ['appid', 'appsecret'],
'mp-weixin.weixin': ['appid', 'appsecret'], 'mp-weixin.weixin': ['appid', 'appsecret'],
'app.qq': ['appid', 'appsecret'], 'app.qq': ['appid', 'appsecret'],
......
...@@ -8,6 +8,9 @@ const { ...@@ -8,6 +8,9 @@ const {
const { const {
ERROR ERROR
} = require('../../common/error') } = require('../../common/error')
const {
logout
} = require('./logout')
const PasswordUtils = require('./password') const PasswordUtils = require('./password')
async function realPreLogin (params = {}) { async function realPreLogin (params = {}) {
...@@ -173,10 +176,14 @@ async function postLogin (params = {}) { ...@@ -173,10 +176,14 @@ async function postLogin (params = {}) {
extraData, extraData,
isThirdParty = false isThirdParty = false
} = params } = params
const {
clientIP,
uniIdToken
} = this.getClientInfo()
const uid = user._id const uid = user._id
const updateData = { const updateData = {
last_login_date: Date.now(), last_login_date: Date.now(),
last_login_ip: this.getClientInfo().clientIP, last_login_ip: clientIP,
...extraData ...extraData
} }
const { const {
...@@ -185,6 +192,13 @@ async function postLogin (params = {}) { ...@@ -185,6 +192,13 @@ async function postLogin (params = {}) {
} = await this.uniIdCommon.createToken({ } = await this.uniIdCommon.createToken({
uid uid
}) })
if (uniIdToken) {
try {
await logout.call(this)
} catch (error) {}
}
await userCollection.doc(uid).update(updateData) await userCollection.doc(uid).update(updateData)
await this.middleware.uniIdLog({ await this.middleware.uniIdLog({
data: { data: {
......
const {
dbCmd,
LOG_TYPE,
deviceCollection,
userCollection
} = require('../../common/constants')
async function logout() {
const {
uniIdToken,
deviceId
} = this.getClientInfo()
const {
uid
} = await this.uniIdCommon.checkToken(
uniIdToken,
{
autoRefresh: false
}
)
// 删除token
await userCollection.doc(uid).update({
token: dbCmd.pull(uniIdToken)
})
// 仅当device表的device_id和user_id均对应时才进行更新
await deviceCollection.where({
device_id: deviceId,
user_id: uid
}).update({
token_expired: 0
})
await this.middleware.uniIdLog({
data: {
user_id: uid
},
type: LOG_TYPE.LOGOUT
})
return {
errCode: 0
}
}
module.exports = {
logout
}
\ No newline at end of file
...@@ -5,7 +5,7 @@ const { ...@@ -5,7 +5,7 @@ const {
ERROR ERROR
} = require('../../common/error') } = require('../../common/error')
function getQQPlatform () { function getQQPlatform() {
const platform = this.clientPlatform const platform = this.clientPlatform
switch (platform) { switch (platform) {
case 'app': case 'app':
...@@ -18,7 +18,7 @@ function getQQPlatform () { ...@@ -18,7 +18,7 @@ function getQQPlatform () {
} }
} }
async function saveQQUserKey ({ async function saveQQUserKey({
openid, openid,
sessionKey, // QQ小程序用户sessionKey sessionKey, // QQ小程序用户sessionKey
accessToken, // App端QQ用户accessToken accessToken, // App端QQ用户accessToken
...@@ -45,14 +45,17 @@ async function saveQQUserKey ({ ...@@ -45,14 +45,17 @@ async function saveQQUserKey ({
await this.uniOpenBridge.setUserAccessToken(keyObj, { await this.uniOpenBridge.setUserAccessToken(keyObj, {
access_token: accessToken, access_token: accessToken,
access_token_expired: accessTokenExpired access_token_expired: accessTokenExpired
}, Math.floor((accessTokenExpired - Date.now()) / 1000)) }, accessTokenExpired ?
Math.floor((accessTokenExpired - Date.now()) / 1000) :
30 * 24 * 60 * 60
)
break break
default: default:
break break
} }
} }
function generateQQCache ({ function generateQQCache({
sessionKey, // QQ小程序用户sessionKey sessionKey, // QQ小程序用户sessionKey
accessToken, // App端QQ用户accessToken accessToken, // App端QQ用户accessToken
accessTokenExpired // App端QQ用户accessToken过期时间 accessTokenExpired // App端QQ用户accessToken过期时间
...@@ -81,7 +84,7 @@ function generateQQCache ({ ...@@ -81,7 +84,7 @@ function generateQQCache ({
} }
} }
function getQQOpenid ({ function getQQOpenid({
userRecord userRecord
} = {}) { } = {}) {
const qqPlatform = getQQPlatform.call(this) const qqPlatform = getQQPlatform.call(this)
...@@ -93,7 +96,7 @@ function getQQOpenid ({ ...@@ -93,7 +96,7 @@ function getQQOpenid ({
return qqOpenidObj[`${qqPlatform}_${appId}`] || qqOpenidObj[qqPlatform] return qqOpenidObj[`${qqPlatform}_${appId}`] || qqOpenidObj[qqPlatform]
} }
async function getQQCacheFallback ({ async function getQQCacheFallback({
userRecord, userRecord,
key key
} = {}) { } = {}) {
...@@ -106,7 +109,7 @@ async function getQQCacheFallback ({ ...@@ -106,7 +109,7 @@ async function getQQCacheFallback ({
return qqCache && qqCache[key] return qqCache && qqCache[key]
} }
async function getQQCache ({ async function getQQCache({
uid, uid,
userRecord, userRecord,
key key
......
...@@ -12,10 +12,13 @@ const { ...@@ -12,10 +12,13 @@ const {
getValidInviteCode, getValidInviteCode,
generateInviteInfo generateInviteInfo
} = require('./fission') } = require('./fission')
const {
logout
} = require('./logout')
const PasswordUtils = require('./password') const PasswordUtils = require('./password')
const merge = require('lodash.merge') const merge = require('lodash.merge')
async function realPreRegister (params = {}) { async function realPreRegister(params = {}) {
const { const {
user user
} = params } = params
...@@ -30,7 +33,7 @@ async function realPreRegister (params = {}) { ...@@ -30,7 +33,7 @@ async function realPreRegister (params = {}) {
} }
} }
async function preRegister (params = {}) { async function preRegister(params = {}) {
try { try {
await realPreRegister.call(this, params) await realPreRegister.call(this, params)
} catch (error) { } catch (error) {
...@@ -42,7 +45,7 @@ async function preRegister (params = {}) { ...@@ -42,7 +45,7 @@ async function preRegister (params = {}) {
} }
} }
async function preRegisterWithPassword (params = {}) { async function preRegisterWithPassword(params = {}) {
const { const {
user, user,
password password
...@@ -69,7 +72,7 @@ async function preRegisterWithPassword (params = {}) { ...@@ -69,7 +72,7 @@ async function preRegisterWithPassword (params = {}) {
} }
} }
async function thirdPartyRegister ({ async function thirdPartyRegister({
user = {} user = {}
} = {}) { } = {}) {
return { return {
...@@ -78,7 +81,7 @@ async function thirdPartyRegister ({ ...@@ -78,7 +81,7 @@ async function thirdPartyRegister ({
} }
} }
async function postRegister (params = {}) { async function postRegister(params = {}) {
const { const {
user, user,
extraData = {}, extraData = {},
...@@ -93,7 +96,8 @@ async function postRegister (params = {}) { ...@@ -93,7 +96,8 @@ async function postRegister (params = {}) {
channel, channel,
scene, scene,
clientIP, clientIP,
osName osName,
uniIdToken
} = this.getClientInfo() } = this.getClientInfo()
merge(user, extraData) merge(user, extraData)
...@@ -147,6 +151,12 @@ async function postRegister (params = {}) { ...@@ -147,6 +151,12 @@ async function postRegister (params = {}) {
user.invite_time = inviteTime user.invite_time = inviteTime
} }
if (uniIdToken) {
try {
await logout.call(this)
} catch (error) {}
}
const beforeRegister = this.hooks.beforeRegister const beforeRegister = this.hooks.beforeRegister
let userRecord = user let userRecord = user
if (beforeRegister) { if (beforeRegister) {
......
...@@ -78,7 +78,7 @@ async function saveWeixinUserKey ({ ...@@ -78,7 +78,7 @@ async function saveWeixinUserKey ({
access_token: accessToken, access_token: accessToken,
refresh_token: refreshToken, refresh_token: refreshToken,
access_token_expired: accessTokenExpired access_token_expired: accessTokenExpired
}, 30 * 24 * 3600) }, 30 * 24 * 60 * 60)
break break
default: default:
break break
......
...@@ -18,6 +18,10 @@ const PasswordUtils = require('../../lib/utils/password') ...@@ -18,6 +18,10 @@ const PasswordUtils = require('../../lib/utils/password')
* @param {String} params.nickname 昵称 * @param {String} params.nickname 昵称
* @param {Array} params.authorizedApp 允许登录的AppID列表 * @param {Array} params.authorizedApp 允许登录的AppID列表
* @param {Array} params.role 用户角色列表 * @param {Array} params.role 用户角色列表
* @param {String} params.mobile 手机号
* @param {String} params.email 邮箱
* @param {Array} params.tags 用户标签
* @param {Number} params.status 用户状态
* @returns * @returns
*/ */
module.exports = async function (params = {}) { module.exports = async function (params = {}) {
...@@ -35,6 +39,22 @@ module.exports = async function (params = {}) { ...@@ -35,6 +39,22 @@ module.exports = async function (params = {}) {
role: { role: {
require: false, require: false,
type: 'array<string>' type: 'array<string>'
},
mobile: {
required: false,
type: 'mobile'
},
email: {
required: false,
type: 'email'
},
tags: {
required: false,
type: 'array<string>'
},
status: {
required: false,
type: 'number'
} }
} }
this.middleware.validate(params, schema) this.middleware.validate(params, schema)
...@@ -43,11 +63,17 @@ module.exports = async function (params = {}) { ...@@ -43,11 +63,17 @@ module.exports = async function (params = {}) {
password, password,
authorizedApp, authorizedApp,
nickname, nickname,
role role,
mobile,
email,
tags,
status
} = params } = params
const userMatched = await findUser({ const userMatched = await findUser({
userQuery: { userQuery: {
username username,
mobile,
email
}, },
authorizedApp authorizedApp
}) })
...@@ -65,17 +91,28 @@ module.exports = async function (params = {}) { ...@@ -65,17 +91,28 @@ module.exports = async function (params = {}) {
} = passwordUtils.generatePasswordHash({ } = passwordUtils.generatePasswordHash({
password password
}) })
const data = {
await userCollection.add({
username, username,
password: passwordHash, password: passwordHash,
password_secret_version: version, password_secret_version: version,
dcloud_appid: authorizedApp || [], dcloud_appid: authorizedApp || [],
nickname, nickname,
role: role || [] role: role || [],
}) mobile,
email,
tags: tags || [],
status
}
if (email) {
data.email_confirmed = 1
}
if (mobile) {
data.mobile_confirmed = 1
}
await userCollection.add(data)
return { return {
errCode: 0 errCode: 0,
errMsg: ''
} }
} }
module.exports = { module.exports = {
addUser: require('./add-user') addUser: require('./add-user'),
updateUser: require('./update-user')
} }
const {
findUser
} = require('../../lib/utils/account')
const {
ERROR
} = require('../../common/error')
const {
userCollection
} = require('../../common/constants')
const PasswordUtils = require('../../lib/utils/password')
/**
* 修改用户
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-user
* @param {Object} params
* @param {String} params.uid 要更新的用户id
* @param {String} params.username 用户名
* @param {String} params.password 密码
* @param {String} params.nickname 昵称
* @param {Array} params.authorizedApp 允许登录的AppID列表
* @param {Array} params.role 用户角色列表
* @param {String} params.mobile 手机号
* @param {String} params.email 邮箱
* @param {Array} params.tags 用户标签
* @param {Number} params.status 用户状态
* @returns
*/
module.exports = async function (params = {}) {
const schema = {
uid: 'string',
username: 'username',
password: {
required: false,
type: 'password'
},
authorizedApp: {
required: false,
type: 'array<string>'
}, // 指定允许登录的app,传空数组或不传时表示可以不可以在任何端登录
nickname: {
required: false,
type: 'nickname'
},
role: {
require: false,
type: 'array<string>'
},
mobile: {
required: false,
type: 'mobile'
},
email: {
required: false,
type: 'email'
},
tags: {
required: false,
type: 'array<string>'
},
status: {
required: false,
type: 'number'
}
}
this.middleware.validate(params, schema)
const {
uid,
username,
password,
authorizedApp,
nickname,
role,
mobile,
email,
tags,
status
} = params
// 更新的用户数据字段
const data = {
username,
dcloud_appid: authorizedApp || [],
nickname,
role: role || [],
mobile,
email,
tags: tags || [],
status
}
// 更新用户名时验证用户名是否重新
if (username) {
const userMatched = await findUser({
userQuery: {
username
},
authorizedApp
})
if (userMatched.filter(user => user._id !== uid).length) {
throw {
errCode: ERROR.ACCOUNT_EXISTS
}
}
}
if (password) {
const passwordUtils = new PasswordUtils({
passwordSecret: this.config.passwordSecret
})
const {
passwordHash,
version
} = passwordUtils.generatePasswordHash({
password
})
data.password = passwordHash
data.password_secret_version = version
}
await userCollection.doc(uid).update(data)
return {
errCode: 0
}
}
const { const {
dbCmd, logout
LOG_TYPE, } = require('../../lib/utils/logout')
deviceCollection,
userCollection
} = require('../../common/constants')
/** /**
* 用户退出登录 * 用户退出登录
...@@ -11,37 +8,7 @@ const { ...@@ -11,37 +8,7 @@ const {
* @returns * @returns
*/ */
module.exports = async function () { module.exports = async function () {
const { await logout.call(this)
uniIdToken,
deviceId
} = this.getClientInfo()
const {
uid
} = await this.uniIdCommon.checkToken(
uniIdToken,
{
autoRefresh: false
}
)
// 删除token
await userCollection.doc(uid).update({
token: dbCmd.pull(uniIdToken)
})
// 仅当device表的device_id和user_id均对应时才进行更新
await deviceCollection.where({
device_id: deviceId,
user_id: uid
}).update({
token_expired: 0
})
await this.middleware.uniIdLog({
data: {
user_id: uid
},
type: LOG_TYPE.LOGOUT
})
return { return {
errCode: 0 errCode: 0
} }
......
...@@ -16,7 +16,7 @@ module.exports = async function (params = {}) { ...@@ -16,7 +16,7 @@ module.exports = async function (params = {}) {
uid: 'string', uid: 'string',
appId: 'string' appId: 'string'
} }
this.moddleware.validate(params, schema) this.middleware.validate(params, schema)
const { const {
uid, uid,
appId appId
......
{ {
"name": "uni-id-co", "name": "uni-id-co",
"version": "1.0.10", "version": "1.0.12",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"keywords": [], "keywords": [],
......
'use strict'; 'use strict';
class BridgeError extends Error { class BridgeError extends Error {
...@@ -20,7 +20,7 @@ class BridgeError extends Error { ...@@ -20,7 +20,7 @@ class BridgeError extends Error {
return this.message return this.message
} }
} }
module.exports = { module.exports = {
BridgeError BridgeError
} }
'use strict'; 'use strict';
const { const {
PlatformType PlatformType
} = require('./consts.js') } = require('./consts.js')
const configCenter = require('uni-config-center') const configCenter = require('uni-config-center')
const OauthConfig = { const OauthConfig = {
'weixin-mp': ['mp-weixin', 'oauth', 'weixin'], 'weixin-mp': ['mp-weixin', 'oauth', 'weixin'],
'weixin-h5': ['web', 'oauth', 'weixin-h5'] 'weixin-h5': ['web', 'oauth', 'weixin-h5']
} }
class ConfigBase { class ConfigBase {
constructor() { constructor() {
this._ready = false this._ready = false
this._uniId = null this._uniId = null
const uniIdConfig = configCenter({ const uniIdConfig = configCenter({
pluginId: 'uni-id' pluginId: 'uni-id'
}) })
this._uniId = uniIdConfig.config() this._uniId = uniIdConfig.config()
this._ready = true this._ready = true
} }
getAppConfig(appid) { getAppConfig(appid) {
if (Array.isArray(this._uniId)) { if (Array.isArray(this._uniId)) {
return this._uniId.find((item) => { return this._uniId.find((item) => {
return (item.dcloudAppid === appid) return (item.dcloudAppid === appid)
}) })
} }
return this._uniId return this._uniId
} }
get ready() { get ready() {
return this._ready return this._ready
} }
} }
class AppConfig extends ConfigBase { class AppConfig extends ConfigBase {
constructor() { constructor() {
super() super()
} }
get(appid, platform) { get(appid, platform) {
if (!this.isSupport(platform)) { if (!this.isSupport(platform)) {
return null return null
} }
let appConfig = this.getAppConfig(appid) let appConfig = this.getAppConfig(appid)
if (!appConfig) { if (!appConfig) {
return null return null
} }
return this.getOauthConfig(appConfig, platform) return this.getOauthConfig(appConfig, platform)
} }
isSupport(platformName) { isSupport(platformName) {
return (AppConfig.Support_Platforms.indexOf(platformName) >= 0) return (AppConfig.Support_Platforms.indexOf(platformName) >= 0)
} }
getOauthConfig(appConfig, platformName) { getOauthConfig(appConfig, platformName) {
let tree = OauthConfig[platformName] let tree = OauthConfig[platformName]
let node = appConfig let node = appConfig
for (let i = 0; i < tree.length; i++) { for (let i = 0; i < tree.length; i++) {
let nodeName = tree[i] let nodeName = tree[i]
if (node[nodeName]) { if (node[nodeName]) {
node = node[nodeName] node = node[nodeName]
} else { } else {
node = null node = null
break break
} }
} }
if (node && node.appid && node.appsecret) { if (node && node.appid && node.appsecret) {
return { return {
appid: node.appid, appid: node.appid,
secret: node.appsecret secret: node.appsecret
} }
} }
return null return null
} }
} }
AppConfig.Support_Platforms = [PlatformType.WEIXIN_MP, PlatformType.WEIXIN_H5] AppConfig.Support_Platforms = [PlatformType.WEIXIN_MP, PlatformType.WEIXIN_H5]
module.exports = { module.exports = {
AppConfig AppConfig
}; };
'use strict'; 'use strict';
const HTTP_STATUS = { const TAG = "UNI_OPEN_BRIDGE"
SUCCESS: 200
} const HTTP_STATUS = {
SUCCESS: 200
const PlatformType = { }
WEIXIN_MP: 'weixin-mp',
WEIXIN_H5: 'weixin-h5', const PlatformType = {
WEIXIN_APP: 'weixin-app', WEIXIN_MP: 'weixin-mp',
WEIXIN_WEB: 'weixin-web', WEIXIN_H5: 'weixin-h5',
QQ_MP: 'qq-mp', WEIXIN_APP: 'weixin-app',
QQ_APP: 'qq-app' WEIXIN_WEB: 'weixin-web',
} QQ_MP: 'qq-mp',
QQ_APP: 'qq-app'
module.exports = { }
HTTP_STATUS,
PlatformType const ErrorCodeType = {
SYSTEM_ERROR: TAG + "_SYSTEM_ERROR"
}
module.exports = {
HTTP_STATUS,
PlatformType,
ErrorCodeType
} }
'use strict'; 'use strict';
const { const {
PlatformType PlatformType,
} = require('./consts.js') ErrorCodeType
} = require('./consts.js')
const {
AppConfig const {
} = require('./config.js') AppConfig
} = require('./config.js')
const {
Storage, const {
Factory Storage,
} = require('./storage.js') Factory
} = require('./storage.js')
const {
BridgeError const {
} = require('./bridge-error.js') BridgeError
} = require('./bridge-error.js')
const {
WeixinServer const {
} = require('./weixin-server.js') WeixinServer
} = require('./weixin-server.js')
const appConfig = new AppConfig()
const appConfig = new AppConfig()
class AccessToken extends Storage {
class AccessToken extends Storage {
constructor() {
super('access-token', ['dcloudAppid', 'platform']) constructor() {
} super('access-token', ['dcloudAppid', 'platform'])
}
async fallback(parameters) {
const oauthConfig = appConfig.get(parameters.dcloudAppid, parameters.platform) async fallback(parameters) {
const methodName = (parameters.platform === PlatformType.WEIXIN_MP) ? 'GetMPAccessTokenData' : const oauthConfig = appConfig.get(parameters.dcloudAppid, parameters.platform)
'GetH5AccessTokenData' let methodName
const responseData = await WeixinServer[methodName](oauthConfig) if (parameters.platform === PlatformType.WEIXIN_MP) {
methodName = 'GetMPAccessTokenData'
const duration = responseData.expires_in } else if (parameters.platform === PlatformType.WEIXIN_H5) {
delete responseData.expires_in methodName = 'GetH5AccessTokenData'
} else {
return { throw new BridgeError(ErrorCodeType.SYSTEM_ERROR, "platform invalid")
value: responseData, }
duration
} const responseData = await WeixinServer[methodName](oauthConfig)
}
} const duration = responseData.expires_in
delete responseData.expires_in
class UserAccessToken extends Storage {
return {
constructor() { value: responseData,
super('user-access-token', ['dcloudAppid', 'platform', 'openid']) duration
} }
} }
}
class SessionKey extends Storage {
class UserAccessToken extends Storage {
constructor() {
super('session-key', ['dcloudAppid', 'platform', 'openid']) constructor() {
} super('user-access-token', ['dcloudAppid', 'platform', 'openid'])
} }
}
class Encryptkey extends Storage {
class SessionKey extends Storage {
constructor() {
super('encrypt-key', ['dcloudAppid', 'platform', 'openid']) constructor() {
} super('session-key', ['dcloudAppid', 'platform', 'openid'])
}
getKeyString(key) { }
return `${super.getKeyString(key)}-${key.version}`
} class Encryptkey extends Storage {
getExpiresIn(value) { constructor() {
if (value <= 0) { super('encrypt-key', ['dcloudAppid', 'platform', 'openid'])
return 60 }
}
return value getKeyString(key) {
} return `${super.getKeyString(key)}-${key.version}`
}
async fallback(parameters) {
const accessToken = await Factory.Get(AccessToken, parameters) getExpiresIn(value) {
const userSession = await Factory.Get(SessionKey, parameters) if (value <= 0) {
return 60
const responseData = await WeixinServer.GetUserEncryptKeyData({ }
openid: parameters.openid, return value
access_token: accessToken.access_token, }
session_key: userSession.session_key
}) async fallback(parameters) {
const accessToken = await Factory.Get(AccessToken, parameters)
const keyInfo = responseData.key_info_list.find((item) => { const userSession = await Factory.Get(SessionKey, parameters)
return item.version = parameters.version
}) const responseData = await WeixinServer.GetUserEncryptKeyData({
openid: parameters.openid,
const value = { access_token: accessToken.access_token,
encrypt_key: keyInfo.encrypt_key, session_key: userSession.session_key
iv: keyInfo.iv })
}
const keyInfo = responseData.key_info_list.find((item) => {
return { return item.version = parameters.version
value, })
duration: keyInfo.expire_in
} const value = {
} encrypt_key: keyInfo.encrypt_key,
} iv: keyInfo.iv
}
class Ticket extends Storage {
return {
constructor() { value,
super('ticket', ['dcloudAppid', 'platform']) duration: keyInfo.expire_in
} }
}
async fallback(parameters) { }
const accessToken = await Factory.Get(AccessToken, {
dcloudAppid: parameters.dcloudAppid, class Ticket extends Storage {
platform: PlatformType.WEIXIN_H5
}) constructor() {
super('ticket', ['dcloudAppid', 'platform'])
const responseData = await WeixinServer.GetH5TicketData(accessToken) }
const duration = responseData.expires_in async fallback(parameters) {
delete responseData.expires_in const accessToken = await Factory.Get(AccessToken, {
dcloudAppid: parameters.dcloudAppid,
return { platform: PlatformType.WEIXIN_H5
value: responseData, })
duration
} const responseData = await WeixinServer.GetH5TicketData(accessToken)
}
} const duration = responseData.expires_in
delete responseData.expires_in
// exports return {
value: responseData,
async function getAccessToken(key, fallback) { duration
return await Factory.Get(AccessToken, key, fallback) }
} }
}
async function setAccessToken(key, value, expiresIn) {
await Factory.Set(AccessToken, key, value, expiresIn)
} // exports
async function removeAccessToken(key) { async function getAccessToken(key, fallback) {
await Factory.Remove(AccessToken, key) return await Factory.Get(AccessToken, key, fallback)
} }
async function getUserAccessToken(key, fallback) { async function setAccessToken(key, value, expiresIn) {
return await Factory.Get(UserAccessToken, key, fallback) await Factory.Set(AccessToken, key, value, expiresIn)
} }
async function setUserAccessToken(key, value, expiresIn) { async function removeAccessToken(key) {
await Factory.Set(UserAccessToken, key, value, expiresIn) await Factory.Remove(AccessToken, key)
} }
async function removeUserAccessToken(key) { async function getUserAccessToken(key, fallback) {
await Factory.Remove(UserAccessToken, key) return await Factory.Get(UserAccessToken, key, fallback)
} }
async function getSessionKey(key, fallback) { async function setUserAccessToken(key, value, expiresIn) {
return await Factory.Get(SessionKey, key, fallback) await Factory.Set(UserAccessToken, key, value, expiresIn)
} }
async function setSessionKey(key, value, expiresIn) { async function removeUserAccessToken(key) {
await Factory.Set(SessionKey, key, value, expiresIn) await Factory.Remove(UserAccessToken, key)
} }
async function removeSessionKey(key) { async function getSessionKey(key, fallback) {
await Factory.Remove(SessionKey, key) return await Factory.Get(SessionKey, key, fallback)
} }
async function getEncryptKey(key, fallback) { async function setSessionKey(key, value, expiresIn) {
return await Factory.Get(Encryptkey, key, fallback) await Factory.Set(SessionKey, key, value, expiresIn)
} }
async function setEncryptKey(key, value, expiresIn) { async function removeSessionKey(key) {
await Factory.Set(Encryptkey, key, value, expiresIn) await Factory.Remove(SessionKey, key)
} }
async function removeEncryptKey(key) { async function getEncryptKey(key, fallback) {
await Factory.Remove(Encryptkey, key) return await Factory.Get(Encryptkey, key, fallback)
} }
async function getTicket(key, fallback) { async function setEncryptKey(key, value, expiresIn) {
return await Factory.Get(Ticket, key, fallback) await Factory.Set(Encryptkey, key, value, expiresIn)
} }
async function setTicket(key, value, expiresIn) { async function removeEncryptKey(key) {
await Factory.Set(Ticket, key, value, expiresIn) await Factory.Remove(Encryptkey, key)
} }
async function removeTicket(key) { async function getTicket(key, fallback) {
await Factory.Remove(Ticket, key) return await Factory.Get(Ticket, key, fallback)
} }
module.exports = { async function setTicket(key, value, expiresIn) {
getAccessToken, await Factory.Set(Ticket, key, value, expiresIn)
setAccessToken, }
removeAccessToken,
getUserAccessToken, async function removeTicket(key) {
setUserAccessToken, await Factory.Remove(Ticket, key)
removeUserAccessToken, }
getSessionKey,
setSessionKey, module.exports = {
removeSessionKey, getAccessToken,
getEncryptKey, setAccessToken,
setEncryptKey, removeAccessToken,
removeEncryptKey, getUserAccessToken,
getTicket, setUserAccessToken,
setTicket, removeUserAccessToken,
removeTicket, getSessionKey,
PlatformType, setSessionKey,
WeixinServer removeSessionKey,
getEncryptKey,
setEncryptKey,
removeEncryptKey,
getTicket,
setTicket,
removeTicket,
PlatformType,
WeixinServer,
ErrorCodeType
} }
'use strict'; 'use strict';
const { const {
Validator Validator
} = require('./validator.js') } = require('./validator.js')
const { const {
CacheKeyCascade CacheKeyCascade
} = require('./uni-cloud-cache.js') } = require('./uni-cloud-cache.js')
class Storage { class Storage {
constructor(type, keys) { constructor(type, keys) {
this._type = type || null this._type = type || null
this._keys = keys || [] this._keys = keys || []
} }
async get(key, fallback) { async get(key, fallback) {
this.validateKey(key) this.validateKey(key)
const result = await this.create(key, fallback).get() const result = await this.create(key, fallback).get()
return result.value return result.value
} }
async set(key, value, expiresIn) { async set(key, value, expiresIn) {
this.validateKey(key) this.validateKey(key)
this.validateValue(value) this.validateValue(value)
const expires_in = this.getExpiresIn(expiresIn) const expires_in = this.getExpiresIn(expiresIn)
if (expires_in !== 0) { if (expires_in !== 0) {
await this.create(key).set(this.getValue(value), expires_in) await this.create(key).set(this.getValue(value), expires_in)
} }
} }
async remove(key) { async remove(key) {
this.validateKey(key) this.validateKey(key)
await this.create(key).remove() await this.create(key).remove()
} }
async ttl(key) { async ttl(key) {
this.validateKey(key) this.validateKey(key)
// 后续考虑支持 // 后续考虑支持
} }
getKeyString(key) { getKeyString(key) {
const keyArray = [Storage.Prefix] const keyArray = [Storage.Prefix]
this._keys.forEach((name) => { this._keys.forEach((name) => {
keyArray.push(key[name]) keyArray.push(key[name])
}) })
keyArray.push(this._type) keyArray.push(this._type)
return keyArray.join(':') return keyArray.join(':')
} }
getValue(value) { getValue(value) {
return value return value
} }
getExpiresIn(value) { getExpiresIn(value) {
if (value !== undefined) { if (value !== undefined) {
return value return value
} }
return -1 return -1
} }
validateKey(key) { validateKey(key) {
Validator.Key(this._keys, key) Validator.Key(this._keys, key)
} }
validateValue(value) { validateValue(value) {
Validator.Value(value) Validator.Value(value)
} }
create(key, fallback) { create(key, fallback) {
const keyString = this.getKeyString(key) const keyString = this.getKeyString(key)
const options = { const options = {
layers: [{ layers: [{
type: 'database', type: 'database',
key: keyString key: keyString
}, { }, {
type: 'redis', type: 'redis',
key: keyString key: keyString
}] }]
} }
if (fallback !== null) { if (fallback !== null) {
const fallbackFunction = fallback || this.fallback const fallbackFunction = fallback || this.fallback
if (fallbackFunction) { if (fallbackFunction) {
options.fallback = async () => { options.fallback = async () => {
return await fallbackFunction(key) return await fallbackFunction(key)
} }
} }
} }
return new CacheKeyCascade(options) return new CacheKeyCascade(options)
} }
} }
Storage.Prefix = "uni-id" Storage.Prefix = "uni-id"
const Factory = { const Factory = {
async Get(T, key, fallback) { async Get(T, key, fallback) {
return await Factory.MakeUnique(T).get(key, fallback) return await Factory.MakeUnique(T).get(key, fallback)
}, },
async Set(T, key, value, expiresIn) { async Set(T, key, value, expiresIn) {
await Factory.MakeUnique(T).set(key, value, expiresIn) await Factory.MakeUnique(T).set(key, value, expiresIn)
}, },
async Remove(T, key) { async Remove(T, key) {
await Factory.MakeUnique(T).remove(key) await Factory.MakeUnique(T).remove(key)
}, },
MakeUnique(T) { MakeUnique(T) {
return new T() return new T()
} }
} }
module.exports = { module.exports = {
Storage, Storage,
Factory Factory
}; };
const db = uniCloud.database() const db = uniCloud.database()
function getType(value) { function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase() return Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
} }
const validator = { const validator = {
key: function(value) { key: function(value) {
const err = new Error('Invalid key') const err = new Error('Invalid key')
if (typeof value !== 'string') { if (typeof value !== 'string') {
throw err throw err
} }
const valueTrim = value.trim() const valueTrim = value.trim()
if (!valueTrim || valueTrim !== value) { if (!valueTrim || valueTrim !== value) {
throw err throw err
} }
}, },
value: function(value) { value: function(value) {
// 仅作简单校验 // 仅作简单校验
const type = getType(value) const type = getType(value)
const validValueType = ['null', 'number', 'string', 'array', 'object'] const validValueType = ['null', 'number', 'string', 'array', 'object']
if (validValueType.indexOf(type) === -1) { if (validValueType.indexOf(type) === -1) {
throw new Error('Invalid value type') throw new Error('Invalid value type')
} }
}, },
duration: function(value) { duration: function(value) {
const err = new Error('Invalid duration') const err = new Error('Invalid duration')
if (value === undefined) { if (value === undefined) {
return return
} }
if (typeof value !== 'number' || value === 0) { if (typeof value !== 'number' || value === 0) {
throw err throw err
} }
} }
} }
/** /**
* 入库时 expired 为过期时间对应的时间戳,永不过期用-1表示 * 入库时 expired 为过期时间对应的时间戳,永不过期用-1表示
* 返回结果时 与redis对齐,-1表示永不过期,-2表示已过期或不存在 * 返回结果时 与redis对齐,-1表示永不过期,-2表示已过期或不存在
*/ */
class DatabaseCache { class DatabaseCache {
constructor({ constructor({
collection = 'opendb-open-data' collection = 'opendb-open-data'
} = {}) { } = {}) {
this.type = 'db' this.type = 'db'
this.collection = db.collection(collection) this.collection = db.collection(collection)
} }
_serializeValue(value) { _serializeValue(value) {
return value === undefined ? null : JSON.stringify(value) return value === undefined ? null : JSON.stringify(value)
} }
_deserializeValue(value) { _deserializeValue(value) {
return value ? JSON.parse(value) : value return value ? JSON.parse(value) : value
} }
async set(key, value, duration) { async set(key, value, duration) {
validator.key(key) validator.key(key)
validator.value(value) validator.value(value)
validator.duration(duration) validator.duration(duration)
value = this._serializeValue(value) value = this._serializeValue(value)
await this.collection.doc(key).set({ await this.collection.doc(key).set({
value, value,
expired: duration && duration !== -1 ? Date.now() + duration : -1 expired: duration && duration !== -1 ? Date.now() + duration : -1
}) })
} }
async _getWithDuration(key) { async _getWithDuration(key) {
const getKeyRes = await this.collection.doc(key).get() const getKeyRes = await this.collection.doc(key).get()
const record = getKeyRes.data[0] const record = getKeyRes.data[0]
if (!record) { if (!record) {
return { return {
value: null, value: null,
duration: -2 duration: -2
} }
} }
const value = this._deserializeValue(record.value) const value = this._deserializeValue(record.value)
const expired = record.expired const expired = record.expired
if (expired === -1) { if (expired === -1) {
return { return {
value, value,
duration: -1 duration: -1
} }
} }
const duration = expired - Date.now() const duration = expired - Date.now()
if (duration <= 0) { if (duration <= 0) {
await this.remove(key) await this.remove(key)
return { return {
value: null, value: null,
duration: -2 duration: -2
} }
} }
return { return {
value, value,
duration: Math.floor(duration / 1000) duration: Math.floor(duration / 1000)
} }
} }
async get(key, { async get(key, {
withDuration = true withDuration = true
} = {}) { } = {}) {
const result = await this._getWithDuration(key) const result = await this._getWithDuration(key)
if (!withDuration) { if (!withDuration) {
delete result.duration delete result.duration
} }
return result return result
} }
async remove(key) { async remove(key) {
await this.collection.doc(key).remove() await this.collection.doc(key).remove()
} }
} }
class RedisCache { class RedisCache {
constructor() { constructor() {
this.type = 'redis' this.type = 'redis'
this.redis = uniCloud.redis() this.redis = uniCloud.redis()
} }
_serializeValue(value) { _serializeValue(value) {
return value === undefined ? null : JSON.stringify(value) return value === undefined ? null : JSON.stringify(value)
} }
_deserializeValue(value) { _deserializeValue(value) {
return value ? JSON.parse(value) : value return value ? JSON.parse(value) : value
} }
async set(key, value, duration) { async set(key, value, duration) {
validator.key(key) validator.key(key)
validator.value(value) validator.value(value)
validator.duration(duration) validator.duration(duration)
value = this._serializeValue(value) value = this._serializeValue(value)
if (!duration || duration === -1) { if (!duration || duration === -1) {
await this.redis.set(key, value) await this.redis.set(key, value)
} else { } else {
await this.redis.set(key, value, 'EX', duration) await this.redis.set(key, value, 'EX', duration)
} }
} }
async get(key, { async get(key, {
withDuration = false withDuration = false
} = {}) { } = {}) {
let value = await this.redis.get(key) let value = await this.redis.get(key)
value = this._deserializeValue(value) value = this._deserializeValue(value)
if (!withDuration) { if (!withDuration) {
return { return {
value value
} }
} }
const durationSecond = await this.redis.ttl(key) const durationSecond = await this.redis.ttl(key)
let duration let duration
switch (durationSecond) { switch (durationSecond) {
case -1: case -1:
duration = -1 duration = -1
break break
case -2: case -2:
duration = -2 duration = -2
break break
default: default:
duration = durationSecond duration = durationSecond
break break
} }
return { return {
value, value,
duration duration
} }
} }
async remove(key) { async remove(key) {
await this.redis.del(key) await this.redis.del(key)
} }
} }
class Cache { class Cache {
constructor({ constructor({
type, type,
collection collection
} = {}) { } = {}) {
if (type === 'database') { if (type === 'database') {
return new DatabaseCache({ return new DatabaseCache({
collection collection
}) })
} else if (type === 'redis') { } else if (type === 'redis') {
return new RedisCache() return new RedisCache()
} else { } else {
throw new Error('Invalid cache type') throw new Error('Invalid cache type')
} }
} }
} }
class CacheKey { class CacheKey {
constructor({ constructor({
type, type,
collection, collection,
cache, cache,
key, key,
fallback fallback
} = {}) { } = {}) {
this.cache = cache || new Cache({ this.cache = cache || new Cache({
type, type,
collection collection
}) })
this.key = key this.key = key
this.fallback = fallback this.fallback = fallback
} }
async set(value, duration) { async set(value, duration) {
await this.cache.set(this.key, value, duration) await this.cache.set(this.key, value, duration)
} }
async setWithSync(value, duration, syncMethod) { async setWithSync(value, duration, syncMethod) {
await Promise.all([ await Promise.all([
this.set(this.key, value, duration), this.set(this.key, value, duration),
syncMethod(value, duration) syncMethod(value, duration)
]) ])
} }
async get() { async get() {
let { let {
value, value,
duration duration
} = await this.cache.get(this.key) } = await this.cache.get(this.key)
if (value !== null && value !== undefined) { if (value !== null && value !== undefined) {
return { return {
value, value,
duration duration
} }
} }
if (!this.fallback) { if (!this.fallback) {
return { return {
value: null, value: null,
duration: -2 duration: -2
} }
} }
const fallbackResult = await this.fallback() const fallbackResult = await this.fallback()
value = fallbackResult.value value = fallbackResult.value
duration = fallbackResult.duration duration = fallbackResult.duration
if (value !== null && duration !== undefined) { if (value !== null && duration !== undefined) {
await this.cache.set(this.key, value, duration) await this.cache.set(this.key, value, duration)
} }
return { return {
value, value,
duration duration
} }
} }
async remove() { async remove() {
await this.cache.remove(this.key) await this.cache.remove(this.key)
} }
} }
class CacheKeyCascade { class CacheKeyCascade {
constructor({ constructor({
layers, // [{cache, type, collection, key}] 从低级到高级排序,[DbCacheKey, RedisCacheKey] layers, // [{cache, type, collection, key}] 从低级到高级排序,[DbCacheKey, RedisCacheKey]
fallback fallback
} = {}) { } = {}) {
this.layers = layers this.layers = layers
this.cacheLayers = [] this.cacheLayers = []
let lastCacheKey let lastCacheKey
for (let i = 0; i < layers.length; i++) { for (let i = 0; i < layers.length; i++) {
const { const {
type, type,
cache, cache,
collection, collection,
key key
} = layers[i] } = layers[i]
const lastCacheKeyTemp = lastCacheKey const lastCacheKeyTemp = lastCacheKey
try { try {
const currentCacheKey = new CacheKey({ const currentCacheKey = new CacheKey({
type, type,
collection, collection,
cache, cache,
key, key,
fallback: i === 0 ? fallback : function() { fallback: i === 0 ? fallback : function() {
return lastCacheKeyTemp.get() return lastCacheKeyTemp.get()
} }
}) })
this.cacheLayers.push(currentCacheKey) this.cacheLayers.push(currentCacheKey)
lastCacheKey = currentCacheKey lastCacheKey = currentCacheKey
} catch (e) {} } catch (e) {}
} }
this.highLevelCache = lastCacheKey this.highLevelCache = lastCacheKey
} }
async set(value, duration) { async set(value, duration) {
return Promise.all( return Promise.all(
this.cacheLayers.map(item => { this.cacheLayers.map(item => {
return item.set(value, duration) return item.set(value, duration)
}) })
) )
} }
async setWithSync(value, duration, syncMethod) { async setWithSync(value, duration, syncMethod) {
const setPromise = this.cacheLayers.map(item => { const setPromise = this.cacheLayers.map(item => {
return item.set(value, duration) return item.set(value, duration)
}) })
return Promise.all( return Promise.all(
[ [
...setPromise, ...setPromise,
syncMethod(value, duration) syncMethod(value, duration)
] ]
) )
} }
async get() { async get() {
return this.highLevelCache.get() return this.highLevelCache.get()
} }
async remove() { async remove() {
await Promise.all( await Promise.all(
this.cacheLayers.map(cacheKeyItem => { this.cacheLayers.map(cacheKeyItem => {
return cacheKeyItem.remove() return cacheKeyItem.remove()
}) })
) )
} }
} }
module.exports = { module.exports = {
Cache, Cache,
DatabaseCache, DatabaseCache,
RedisCache, RedisCache,
CacheKey, CacheKey,
CacheKeyCascade CacheKeyCascade
} }
const Validator = { const Validator = {
Key(keyArray, parameters) { Key(keyArray, parameters) {
for (let i = 0; i < keyArray.length; i++) { for (let i = 0; i < keyArray.length; i++) {
const keyName = keyArray[i] const keyName = keyArray[i]
if (typeof parameters[keyName] !== 'string') { if (typeof parameters[keyName] !== 'string') {
Validator.ThrowNewError(`Invalid ${keyName}`) Validator.ThrowNewError(`Invalid ${keyName}`)
} }
if (parameters[keyName].length < 1) { if (parameters[keyName].length < 1) {
Validator.ThrowNewError(`Invalid ${keyName}`) Validator.ThrowNewError(`Invalid ${keyName}`)
} }
} }
}, },
Value(value) { Value(value) {
if (value === undefined) { if (value === undefined) {
Validator.ThrowNewError('Invalid Value') Validator.ThrowNewError('Invalid Value')
} }
if (typeof value !== 'object') { if (typeof value !== 'object') {
Validator.ThrowNewError('Invalid Value Type') Validator.ThrowNewError('Invalid Value Type')
} }
}, },
ThrowNewError(message) { ThrowNewError(message) {
throw new Error(message) throw new Error(message)
} }
} }
module.exports = { module.exports = {
Validator Validator
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册