Commit cbce2ba9 authored by Facius's avatar Facius

feat: 手机验证吗保护

parent 7451f6ec
......@@ -4,29 +4,25 @@
"version" : "0.0",
"configurations" : [
{
"app-plus" :
{
"launchtype" : "local"
},
"default" :
{
"launchtype" : "local"
},
"h5" :
{
"launchtype" : "local"
},
"mp-toutiao" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
"app-plus" : {
"launchtype" : "local"
},
"default" : {
"launchtype" : "local"
},
"h5" : {
"launchtype" : "local"
},
"mp-toutiao" : {
"launchtype" : "local"
},
"mp-weixin" : {
"launchtype" : "local"
},
"type" : "uniCloud"
},
{
"customPlaygroundType" : "local",
"playground" : "custom",
"type" : "uni-app:app-android"
}
......
......@@ -156,7 +156,7 @@ import {
@import '@/static/styles/base.css';
/* 优化ios微信小程序tabber文字和图片太近问题 */
/deep/.u-tabbar__content__item__text {
::v-deep.u-tabbar__content__item__text {
bottom: 0rpx !important;
top: 60rpx !important;
}
......
......@@ -11,7 +11,7 @@ export function userAuthLogin(data) {
export function sendCode(data) {
return request({
url: `/anno/mobile/sendCode`,
url: `/anno/mobile/sendCode/v2`,
method: 'GET',
data
})
......@@ -24,3 +24,12 @@ export function userBindMobile(data) {
noLoading:true
})
}
export async function getCaptcha (data) {
return request({
url: `/anno/mobile/captcha`,
method: 'GET',
data,
noLoading: true,
})
}
......@@ -494,7 +494,7 @@
display: flex;
align-items: center;
/deep/.u-input__textarea {
::v-deep.u-input__textarea {
padding: 2rpx 0 !important;
min-height: 40rpx !important;
font-size: 28rpx;
......
......@@ -103,7 +103,9 @@
},
sourceTypeChange(source) {
this.sourceType = [source]
this.$nextTick(()=>{ this.$refs['upload-images'].appStratChoose(this.sourceType) })
this.$nextTick(()=>{
this.$refs['upload-images'].appStratChoose(this.sourceType)
})
},
handleListChange(lists, name) {
if (!lists || !lists.length) {
......@@ -128,9 +130,9 @@
}
};
</script>
::v-deep
<style lang="scss" scoped>
/deep/.u-add-tips {
::v-deep.u-add-tips {
height: 0 !important;
margin-top: 0 !important;
overflow: hidden;
......
......@@ -116,7 +116,9 @@
},
sourceTypeChange(source) {
this.sourceType = [source]
this.$nextTick(()=>{ this.$refs['upload-images'].appStratChoose(this.sourceType) })
this.$nextTick(()=>{
this.$refs['upload-images'].appStratChoose(this.sourceType)
})
},
onChooseFail(error) {
console.log("onChooseFail:", error);
......@@ -181,9 +183,9 @@
},
}
</script>
::v-deep
<style lang="scss" scoped>
/deep/.u-add-tips {
::v-deep.u-add-tips {
height: 0 !important;
margin-top: 0 !important;
overflow: hidden;
......
......@@ -138,7 +138,9 @@
sourceTypeChange(source) {
console.log("sourceTypeChange", source)
this.sourceType = [source]
this.$nextTick(()=>{ this.$refs['upload-images'].appStratChoose(this.sourceType) })
this.$nextTick(()=>{
this.$refs['upload-images'].appStratChoose(this.sourceType)
})
},
onChooseFail(error) {
console.log("onChooseFail:", error);
......@@ -201,9 +203,9 @@
}
};
</script>
::v-deep
<style lang="scss" scoped>
/deep/.u-add-tips {
::v-deep.u-add-tips {
height: 0 !important;
margin-top: 0 !important;
overflow: hidden;
......
......@@ -2,8 +2,8 @@
"name" : "扳手记账",
"appid" : "__UNI__EDB8C7C",
"description" : "",
"versionName" : "1.3.6",
"versionCode" : 136,
"versionName" : "1.3.7",
"versionCode" : 137,
"transformPx" : false,
"sassImplementationName" : "node-sass",
/* 5+App特有相关 */
......
......@@ -4,11 +4,11 @@
"description": "",
"main": "main.js",
"dependencies": {
"crypto-js": "^4.2.0",
"uni-read-pages": "^1.0.5",
"uni-simple-router": "^2.0.6",
"uview-ui": "^1.8.4"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"serve": "npm run dev:h5",
......
......@@ -204,7 +204,7 @@ export default {
margin-right: -64rpx;
}
.confirm-button {
/deep/ .u-default-hover {
::v-deep .u-default-hover {
// border: 2rpx solid red !important;
color: transparent !important;
background-color: transparent !important;
......
......@@ -162,7 +162,7 @@ export default {
.navbar-view {
width: 100%;
padding: 0 32rpx 0 8rpx;
/deep/.u-icon__img {
::v-deep.u-icon__img {
width: 36rpx !important;
height: 32rpx !important;
}
......
......@@ -357,13 +357,13 @@ export default {
}
</script>
<style scoped lang="scss">
/deep/.u-mode-center-box {
::v-deep.u-mode-center-box {
background-color: transparent !important;
}
/deep/.u-border-bottom:after {
::v-deep.u-border-bottom:after {
border: 2 solid #f6f7fa !important;
}
/deep/.u-form-item__message {
::v-deep.u-form-item__message {
text-align: right !important;
}
......
......@@ -301,7 +301,7 @@
</script>
<style scoped lang="scss">
.card-share {
/deep/.u-btn {
::v-deep.u-btn {
padding: 0 !important;
}
......
......@@ -5,26 +5,44 @@
<view class="phone-view">
<view class="left-view">+86</view>
<view class="middle-view"></view>
<view class="right-view"><input placeholder-style="color:#BDBDBD" placeholder="请输入手机号" v-model="phone" type="number" width="100%" maxlength="20" clearable /></view>
<view class="right-view">
<input placeholder-style="color:#BDBDBD" placeholder="请输入手机号" class="input-view" v-model.trim="phone"
type="number" width="100%" maxlength="11" clearable />
</view>
</view>
<view class="graphic-verification">
<view class="left-view">
<input placeholder-style="color:#BDBDBD" placeholder="请输入计算结果" class="input-view" v-model.trim="graphicsCode"
type="numeric" maxlength="6" clearable />
</view>
<view class="graphics" hover-class="u-hover-class" @click="$u.throttle(handleGraphics, 500)">
<image :src="base64Url" mode=""></image>
</view>
</view>
<view class="code-view">
<view class="left-view"><input placeholder-style="color:#BDBDBD" placeholder="请输入验证码" v-model="code" type="number" maxlength="4" clearable /></view>
<!-- <view :class="['right-view', { active: active }]" hover-class="u-hover-class" @click="$u.throttle(handleClick, 500)">{{ text }}</view> -->
<view class="left-view">
<input placeholder-style="color:#BDBDBD" placeholder="请输入手机验证码" class="input-view" v-model.trim="code"
type="number" maxlength="4" clearable :focus="codeFocus" />
</view>
<view class="right-view">
<u-toast ref="uToast"></u-toast>
<u-verification-code keep-running unique-key="binding-phone" :seconds="seconds" @end="end" @start="start" ref="uCode" @change="codeChange"></u-verification-code>
<u-verification-code keep-running unique-key="binding-phone" :seconds="seconds" @end="end" @start="start"
ref="uCode" @change="codeChange"></u-verification-code>
<text :class="{ active: canGetCode }" @tap="$u.throttle(handleClick, 500)">{{ tips }}</text>
</view>
</view>
<view class="bottom-vew" hover-class="u-hover-class">
<u-button :disabled="disabled" :customStyle="buttonStyle" :loading="loading" @click="handleClickSave"><view class="button-text">立即绑定</view></u-button>
<u-button :disabled="disabled" :customStyle="buttonStyle" :loading="loading" @click="handleClickSave">
<view class="button-text">立即绑定</view>
</u-button>
</view>
</view>
</template>
<script>
import { sendCode } from '@/api/login/index.js';
import { sendCode, getCaptcha } from '@/api/login/index.js';
import { bindPhone } from '@/utils/bind-mobile.js';
import { aesEncrypt, aesDecrypt, getRandom } from '@/utils/aki-cryptojs.js';
export default {
data() {
......@@ -45,12 +63,16 @@ export default {
backgroundColor: '#02A074',
color: 'transparent'
},
type: 'login'
type: 'login',
randomKey: '',
graphicsCode: '',
base64Url: '',
codeFocus: false
};
},
computed: {
disabled() {
return this.code && this.phone ? false : true;
return this.code && this.phone && this.graphicsCode ? false : true;
},
isPhone() {
return this.type == 'phone';
......@@ -70,8 +92,23 @@ export default {
this.params = options;
}
}
this.handleGraphics();
},
methods: {
handleGraphics() {
getCaptcha({
key: getRandom(20),
value: getRandom(20)
}).then((res) => {
if (res.code == 0) {
this.randomKey = res.data.key;
this.base64Url = res.data.image;
}
}).catch((error) => {
console.log('error', error);
})
},
beginCountdown() {
if (!this.timer) {
this.timer = setInterval(() => {
......@@ -104,6 +141,13 @@ export default {
});
return false;
}
if (this.graphicsCode.trim().length == 0) {
uni.showToast({
icon: 'none',
title: '请输入计算结果'
});
return false;
}
this.startCode();
},
startCode() {
......@@ -121,7 +165,14 @@ export default {
},
async getCode() {
try {
let res = await sendCode({ mobile: this.phone });
let res = await sendCode({
// mobile: this.phone
a: getRandom(20),
b: this.randomKey,
c: aesEncrypt(this.phone, `${this.randomKey}${this.graphicsCode}`),
d: this.graphicsCode,
e: getRandom(20)
});
if (uni.$u.platform !== '3') {
uni.hideLoading();
}
......@@ -129,9 +180,10 @@ export default {
this.$u.toast('验证码已发送');
this.$refs.uCode.start();
console.log('--------发送成功----------');
this.codeFocus = true;
} else {
uni.showToast({
title: '获取验证码失败',
title: res.msg || '获取验证码失败',
icon: 'none'
});
}
......@@ -139,7 +191,7 @@ export default {
console.log(e);
if (e.msg) {
uni.showToast({
title: e.msg,
title: e.msg || '获取验证码失败',
icon: 'none'
});
}
......@@ -167,9 +219,9 @@ export default {
},
handleBindPhone() {
bindPhone({
password: this.code,
account: this.phone
})
password: this.code,
account: this.phone
})
.then(res => {
console.log('------绑定手机接口:-------', res);
uni.showToast({
......@@ -186,15 +238,15 @@ export default {
});
});
},
async getUserInfo () {
try {
await this.$store.dispatch('Get_UserInfo');
async getUserInfo() {
try {
await this.$store.dispatch('Get_UserInfo');
this.goBackPage();
} catch (e) {
//TODO handle the exception
}
} catch (e) {
//TODO handle the exception
}
},
},
goBackPage() {
console.log('---------绑定成功跳转------');
let pages = getCurrentPages();
......@@ -261,12 +313,12 @@ export default {
...this.params,
password: this.code,
account: this.phone,
authType: this.params.authType?this.params.authType:'AC'
authType: this.params.authType ? this.params.authType : 'AC'
};
try {
let res = await this.$store.dispatch('Auto_login', params);
if (res.code === 0) {
this.showToast('登录成功',true);
this.showToast('登录成功', true);
setTimeout(() => {
if (this.returnUrl) {
// 需要跳转到特定页面
......@@ -330,30 +382,30 @@ export default {
var self = this;
this.weixinAuthService.authorize(
function(res) {
function (res) {
console.log('authorize:', res);
//支持微信、qq和微博等
uni.login({
provider: 'weixin',
onlyAuthorize: true,
success: function(loginRes) {
success: function (loginRes) {
console.log('uni.login:', loginRes);
// 微信用户信息存入本地,后期备用
var auth = null;
plus.oauth.getServices(
function(services) {
function (services) {
auth = services.find(service => {
return service.id === 'weixin';
});
try {
uni.setStorageSync('auth_service', auth);
} catch {}
} catch { }
},
function(e) {
function (e) {
console.log('e:', e);
}
);
......@@ -371,7 +423,7 @@ export default {
}
});
},
function(error) {
function (error) {
console.log('error:', error);
},
{
......@@ -453,6 +505,49 @@ export default {
margin-bottom: 88rpx;
}
.graphic-verification {
display: flex;
align-items: center;
justify-content: space-between;
font-weight: bold;
font-size: 28rpx;
color: #333333;
height: 104rpx;
position: relative;
margin-bottom: 48rpx;
.left-view {
padding: 0 32rpx;
display: flex;
align-items: center;
height: 100%;
width: calc(100% - 220rpx);
background-color: #f6f6f6;
border-radius: 16rpx;
}
.graphics {
position: absolute;
height: 100%;
top: 0;
right: 0;
bottom: 0;
z-index: 99;
font-weight: 400;
font-size: 26rpx;
display: flex;
justify-content: center;
align-items: center;
border: 1rpx solid #eeeeee;
border-radius: 16rpx;
image {
width: 200rpx;
height: 100%;
}
}
}
.phone-view {
display: flex;
align-items: center;
......
......@@ -146,7 +146,7 @@ export default {
</script>
<style scoped lang="scss">
.maintain-particulars {
/deep/.u-number-input {
::v-deep.u-number-input {
color: #333333 !important;
font-weight: bold;
}
......
......@@ -974,11 +974,11 @@ export default {
};
</script>
<style scoped lang="scss">
/deep/.u-border-bottom:after {
::v-deep.u-border-bottom:after {
border: 2 solid #f6f7fa !important;
}
/deep/.u-form-item__message {
::v-deep.u-form-item__message {
text-align: right !important;
}
......@@ -1009,7 +1009,7 @@ export default {
.address-view {
flex: 1;
/deep/.u-input__textarea {
::v-deep.u-input__textarea {
padding: 2rpx 0 !important;
min-height: 40rpx !important;
}
......@@ -1232,7 +1232,7 @@ export default {
.address-view {
flex: 1;
/deep/.u-input__textarea {
::v-deep.u-input__textarea {
padding: 2rpx 0 !important;
min-height: 40rpx !important;
}
......
......@@ -660,7 +660,7 @@ export default {
</script>
<style scoped lang="scss">
.maintain-detail {
/deep/ .u-time-axis::before {
::v-deep .u-time-axis::before {
border-left-style: dashed !important;
border-left-color: #02a074 !important;
}
......@@ -917,7 +917,7 @@ export default {
margin-right: -64rpx;
}
.confirm-button {
/deep/ .u-default-hover {
::v-deep .u-default-hover {
// border: 2rpx solid red !important;
color: transparent !important;
background-color: transparent !important;
......
......@@ -344,7 +344,7 @@ export default {
border-radius: 40rpx;
height: 80rpx;
/deep/.u-icon__icon {
::v-deep.u-icon__icon {
color: #bdbdbd !important;
font-size: 34rpx !important;
}
......
......@@ -583,7 +583,7 @@
.address-view {
flex: 1;
/deep/.u-input__textarea {
::v-deep.u-input__textarea {
padding: 2rpx 0 !important;
min-height: 40rpx !important;
}
......
......@@ -402,7 +402,7 @@ export default {
</script>
<style scoped lang="scss">
.maintain-particulars {
/deep/.u-number-input {
::v-deep.u-number-input {
color: #333333 !important;
font-weight: bold;
}
......
......@@ -247,7 +247,7 @@ export default {
</script>
<style lang="scss" scoped>
/deep/ .u-swipe-action {
::v-deep .u-swipe-action {
// width: 351px !important;
}
.list {
......
......@@ -462,7 +462,7 @@ export default {
border-radius: 40rpx;
height: 80rpx;
/deep/.u-icon__icon {
::v-deep.u-icon__icon {
color: #bdbdbd !important;
font-size: 34rpx !important;
}
......
......@@ -1267,11 +1267,11 @@
};
</script>
<style scoped lang="scss">
/deep/.u-border-bottom:after {
::v-deep.u-border-bottom:after {
border: 2 solid #f6f7fa !important;
}
/deep/.u-form-item__message {
::v-deep.u-form-item__message {
text-align: right !important;
}
......
......@@ -82,11 +82,11 @@ $uni-font-size-paragraph:15px;
display: none;
}
/deep/.u-default-hover {
::v-deep.u-default-hover {
opacity: 0.6 !important;
}
/deep/.u-success-hover {
::v-deep.u-success-hover {
opacity: 0.6 !important;
}
......
import CryptoJS from 'crypto-js';
// AES加密
export const aesEncrypt = (data, key) => {
const keyHex = CryptoJS.enc.Utf8.parse(key);
const encrypted = CryptoJS.AES.encrypt(data, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
};
// AES解密
export const aesDecrypt = (encryptedData, key) => {
const keyHex = CryptoJS.enc.Utf8.parse(key);
const decrypted = CryptoJS.AES.decrypt(encryptedData, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8);
};
/**
* 生成指定长度的英文字符和标点随机数
* @param {number} num - 需要生成的字符长度
* @returns {string} 指定长度的随机英文字符和标点字符串
*/
export const getRandom = (num) => {
// 参数验证
if (typeof num !== 'number' || num <= 0 || !Number.isInteger(num)) {
throw new Error('长度必须是一个大于0的整数');
}
// 字符集
const chars = 'abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + '!#$%&*?@^{|}+' + '0123456789';
const charsLength = chars.length;
let result = '';
// 生成随机字符串
for (let i = 0; i < num; i++) {
const randomIndex = Math.floor(Math.random() * charsLength);
result += chars[randomIndex];
}
return result;
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment