8.15提交
This commit is contained in:
parent
fb4e5b6af1
commit
87647c2c84
1
App.vue
1
App.vue
@ -1,7 +1,6 @@
|
||||
<script>
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
console.warn('当前组件仅支持 uni_modules 目录结构 ,请升级 HBuilderX 到 3.1.0 版本以上!')
|
||||
console.log('App Launch')
|
||||
},
|
||||
onShow: function() {
|
||||
|
||||
164
README_API.md
Normal file
164
README_API.md
Normal file
@ -0,0 +1,164 @@
|
||||
# 短信登录API接口说明
|
||||
|
||||
## 接口概述
|
||||
本文档描述了短信验证码登录相关的API接口,包括获取验证码和短信登录功能。
|
||||
|
||||
## 基础配置
|
||||
- 开发环境: `https://dev-app.igandan.com`
|
||||
- 生产环境: `https://app.igandan.com`
|
||||
- 请求头: `Authorization: Bearer {token}` (登录接口不需要)
|
||||
|
||||
## API接口列表
|
||||
|
||||
### 1. 获取短信验证码
|
||||
**接口地址:** `/expertAPI/getSmsCode`
|
||||
**请求方法:** `POST`
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"phone": "13800138000"
|
||||
}
|
||||
```
|
||||
|
||||
**响应格式:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "验证码发送成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应:**
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "手机号格式错误",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 短信验证码登录
|
||||
**接口地址:** `/expertAPI/umSmsLogin`
|
||||
**请求方法:** `POST`
|
||||
**请求参数:**
|
||||
```json
|
||||
{
|
||||
"phone": "13800138000",
|
||||
"code": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
**响应格式:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "登录成功",
|
||||
"data": {
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"userInfo": {
|
||||
"userId": "12345",
|
||||
"phone": "13800138000",
|
||||
"nickname": "用户昵称",
|
||||
"avatar": "头像URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**错误响应:**
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "验证码错误",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 1. 获取验证码
|
||||
1. 用户输入手机号
|
||||
2. 点击"获取验证码"按钮
|
||||
3. 前端验证手机号格式
|
||||
4. 调用获取验证码API
|
||||
5. 开始60秒倒计时
|
||||
|
||||
### 2. 短信登录
|
||||
1. 用户输入手机号和验证码
|
||||
2. 点击"登录"按钮
|
||||
3. 前端验证输入内容
|
||||
4. 调用短信登录API
|
||||
5. 保存token和用户信息
|
||||
6. 跳转到首页
|
||||
|
||||
## 前端实现要点
|
||||
|
||||
### 验证码倒计时
|
||||
```javascript
|
||||
// 开始倒计时
|
||||
isCountingDown.value = true;
|
||||
countdown.value = 60;
|
||||
|
||||
const timer = setInterval(() => {
|
||||
countdown.value--;
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer);
|
||||
isCountingDown.value = false;
|
||||
}
|
||||
}, 1000);
|
||||
```
|
||||
|
||||
### 登录状态管理
|
||||
```javascript
|
||||
// 保存认证信息
|
||||
if (result.data.data && result.data.data.token) {
|
||||
uni.setStorageSync('AUTH_TOKEN_App', result.data.data.token);
|
||||
if (result.data.data.userInfo) {
|
||||
uni.setStorageSync('USER_INFO', result.data.data.userInfo);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 错误处理
|
||||
```javascript
|
||||
try {
|
||||
const result = await api.smsLogin({
|
||||
phone: phoneNumber.value,
|
||||
code: smsCode.value
|
||||
});
|
||||
|
||||
if (result.data.code === 200) {
|
||||
// 登录成功处理
|
||||
} else {
|
||||
// 显示错误信息
|
||||
uni.showToast({
|
||||
title: result.data.message || '登录失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// 网络错误处理
|
||||
uni.showToast({
|
||||
title: '登录失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **手机号验证**: 前端需要验证手机号格式(11位数字,以1开头)
|
||||
2. **验证码长度**: 验证码通常为6位数字
|
||||
3. **倒计时控制**: 防止用户频繁点击获取验证码
|
||||
4. **协议同意**: 登录前必须同意用户协议
|
||||
5. **错误提示**: 提供清晰的错误信息给用户
|
||||
6. **安全考虑**: 验证码有效期限制,防止暴力破解
|
||||
|
||||
## 测试建议
|
||||
|
||||
1. 使用真实手机号测试验证码发送
|
||||
2. 测试各种错误情况(错误验证码、过期验证码等)
|
||||
3. 测试网络异常情况
|
||||
4. 验证登录成功后的跳转逻辑
|
||||
5. 测试token存储和用户信息保存
|
||||
26
api/api.js
26
api/api.js
@ -1,13 +1,33 @@
|
||||
import {request} from '@/utils/request.js'
|
||||
const api = {
|
||||
// 微信登录
|
||||
wxLogin(data) {
|
||||
return request('/login/wechat/mobile', data, 'post', true);
|
||||
},
|
||||
//
|
||||
getTimeStamp(){
|
||||
return request('/manager/getSystemTimeStamp',{}, 'get', false);
|
||||
|
||||
},
|
||||
|
||||
// 手机号登录
|
||||
mobileLogin(data) {
|
||||
return request('/login/mobile', data, 'post', true);
|
||||
},
|
||||
getCode(data) {
|
||||
return request('/code/phone', data, 'post', true);
|
||||
|
||||
// 短信验证码登录
|
||||
smsLogin(data,header){
|
||||
return request('/expertAPI/umSmsLogin', data, 'post', true,'application/json',header);
|
||||
},
|
||||
|
||||
// 密码登录
|
||||
pwdLogin(data){
|
||||
return request('/expertAPI/umPwdLogin', data, 'post', true);
|
||||
},
|
||||
|
||||
// 获取短信验证码
|
||||
getCode(data,header) {
|
||||
return request('/expertAPI/smsSend', data, 'post', true,'application/json',header);
|
||||
},
|
||||
|
||||
// 视频相关API
|
||||
@ -20,8 +40,6 @@ const api = {
|
||||
getBannerVideo(data) {
|
||||
return request('/video/banner', data, 'get', true);
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default api
|
||||
@ -40,13 +40,18 @@
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
],
|
||||
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
|
||||
},
|
||||
"ios" : {},
|
||||
/* ios打包配置 */
|
||||
"sdkConfigs" : {
|
||||
"oauth" : {
|
||||
"univerify" : {}
|
||||
"univerify" : {},
|
||||
"weixin" : {
|
||||
"appid" : "wxbf3658f5e674667c",
|
||||
"UniversalLinks" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,15 +3,18 @@
|
||||
"version": "1.0.0",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development uniapp-cli build --watch",
|
||||
"test": "cross-env NODE_ENV=test uniapp-cli build",
|
||||
"prod": "cross-env NODE_ENV=production uniapp-cli build"
|
||||
"dev": "cross-env NODE_ENV=development uniapp-cli build --watch",
|
||||
"test": "cross-env NODE_ENV=test uniapp-cli build",
|
||||
"prod": "cross-env NODE_ENV=production uniapp-cli build"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"crypto-js": "^4.2.0",
|
||||
"js-base64": "^3.7.8",
|
||||
"js-md5": "^0.8.3",
|
||||
"uview-plus": "^3.4.73"
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<view class="login-button" @click="goPwdLogin" >
|
||||
<view class="login-button" @click="onSmsLogin" :class="{ disabled: !canLogin }">
|
||||
<text class="button-text">登录</text>
|
||||
</view>
|
||||
<view class="sms-login" @click="goPwdLogin">
|
||||
@ -94,7 +94,10 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import auth from "@/utils/auth";
|
||||
import api from "@/api/api.js"
|
||||
import sortObj from "@/utils/sort"
|
||||
import version from "@/utils/version.js"
|
||||
import client_type from "@/utils/client_type.js"
|
||||
import logo from "@/static/icon_login_logo.png"
|
||||
import wxImg from "@/static/weixin_login.png"
|
||||
import checkImg from "@/static/login_new_unselect.png"
|
||||
@ -102,7 +105,8 @@
|
||||
import phoneImg from "@/static/phone.png"
|
||||
import smsImg from "@/static/sms.png"
|
||||
import pwdImg from "@/static/pwd.png"
|
||||
|
||||
import { Base64 } from 'js-base64';
|
||||
import { md5 } from 'js-md5';
|
||||
const customStyle = reactive({
|
||||
height: "100rpx",
|
||||
fontSize: "36rpx",
|
||||
@ -142,9 +146,9 @@
|
||||
icon:'none'
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
// 发送验证码
|
||||
const sendSmsCode = () => {
|
||||
const sendSmsCode = async () => {
|
||||
if (isCountingDown.value) {
|
||||
return;
|
||||
}
|
||||
@ -165,30 +169,76 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 开始倒计时
|
||||
isCountingDown.value = true;
|
||||
countdown.value = 60;
|
||||
|
||||
const timer = setInterval(() => {
|
||||
countdown.value--;
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer);
|
||||
isCountingDown.value = false;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// 模拟发送验证码
|
||||
uni.showToast({
|
||||
title: '验证码已发送',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 这里应该调用实际的发送验证码API
|
||||
console.log('发送验证码到:', phoneNumber.value);
|
||||
try {
|
||||
const timeResult = await api.getTimeStamp();
|
||||
|
||||
let timestamp=timeResult.data.timestamp.toString();
|
||||
let postData={
|
||||
mobile: phoneNumber.value,
|
||||
version:version,
|
||||
user_uuid:'',
|
||||
client_type:'A', //client_type,
|
||||
timestamp:timestamp,
|
||||
type:2,
|
||||
}
|
||||
let obj=sortObj(postData)
|
||||
let str1 = formatString(obj).replace(" ",'');
|
||||
let str2=str1+md5(timestamp);
|
||||
let str3=md5(str2);
|
||||
let sign=Base64.encode(str3).replace('+', '-').replace('/', '_').replaceAll("=", "");
|
||||
// 调用真实API获取验证码
|
||||
const result = await api.getCode(postData,{
|
||||
sign:sign
|
||||
});
|
||||
|
||||
if (result.data.code === 200) {
|
||||
uni.showToast({
|
||||
title: '验证码已发送',
|
||||
icon: 'none'
|
||||
});
|
||||
|
||||
// 开始倒计时
|
||||
isCountingDown.value = true;
|
||||
countdown.value = 60;
|
||||
|
||||
const timer = setInterval(() => {
|
||||
countdown.value--;
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer);
|
||||
isCountingDown.value = false;
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: result.data.message || '发送失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('发送验证码失败:', error);
|
||||
uni.showToast({
|
||||
title: '发送验证码失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const formatString=(obj)=>{
|
||||
let str=''
|
||||
for (var key in obj) {
|
||||
if(str){
|
||||
str+=key+obj[key]
|
||||
}else{
|
||||
str=key+obj[key]
|
||||
}
|
||||
}
|
||||
console.log(str);
|
||||
return str
|
||||
}
|
||||
// 短信验证码登录
|
||||
const onSmsLogin = () => {
|
||||
const onSmsLogin = async () => {
|
||||
if (!isAgreed.value) {
|
||||
uni.showToast({
|
||||
title: '请先同意相关协议',
|
||||
@ -229,26 +279,75 @@
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('短信验证码登录');
|
||||
uni.showLoading({
|
||||
title: '登录中...'
|
||||
});
|
||||
|
||||
// 模拟登录过程
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success'
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '登录中...'
|
||||
});
|
||||
const res = uni.getSystemInfoSync();
|
||||
const timeResult = await api.getTimeStamp();
|
||||
|
||||
let timestamp=timeResult.data.timestamp.toString();
|
||||
|
||||
let postData={
|
||||
mobile: phoneNumber.value,
|
||||
version:version,
|
||||
user_uuid:'',
|
||||
current_spec:res.model,
|
||||
sms:smsCode.value,
|
||||
timestamp:timestamp,
|
||||
client_type:'A'
|
||||
}
|
||||
let obj=sortObj(postData)
|
||||
let str1 = formatString(obj).replace(" ",'');
|
||||
console.log("str1:"+str1)
|
||||
let str2=str1+md5(timestamp);
|
||||
console.log("str2:"+str2)
|
||||
let str3=md5(str2);
|
||||
console.log("str3:"+str3)
|
||||
let sign=Base64.encode(str3).replace('+', '-').replace('/', '_').replaceAll("=", "");
|
||||
console.log("sign:"+sign)
|
||||
|
||||
// 调用真实API进行短信登录
|
||||
const result = await api.smsLogin(postData,{
|
||||
sign:sign
|
||||
});
|
||||
|
||||
// 跳转到首页
|
||||
setTimeout(() => {
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
uni.hideLoading();
|
||||
|
||||
if (result.data.code === 200) {
|
||||
// 保存用户信息到本地存储
|
||||
if (result.data.data && result.data.data.token) {
|
||||
uni.setStorageSync('AUTH_TOKEN_App', result.data.data.token);
|
||||
if (result.data.data.userInfo) {
|
||||
uni.setStorageSync('USER_INFO', result.data.data.userInfo);
|
||||
}
|
||||
}
|
||||
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'none'
|
||||
});
|
||||
}, 1500);
|
||||
}, 2000);
|
||||
|
||||
// 跳转到首页
|
||||
setTimeout(() => {
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
});
|
||||
},0);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: result.data.message || '登录失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('登录失败:', error);
|
||||
uni.showToast({
|
||||
title: '登录失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 微信登录
|
||||
|
||||
10
utils/client_type.js
Normal file
10
utils/client_type.js
Normal file
@ -0,0 +1,10 @@
|
||||
// #ifdef MP-WEIXIN
|
||||
const client_type="M"
|
||||
// #endif
|
||||
// #ifdef APP-IOS
|
||||
const client_type="I"
|
||||
// #endif
|
||||
// #ifdef APP-ANDROID
|
||||
const client_type="A"
|
||||
// #endif
|
||||
export default client_type
|
||||
@ -1,16 +1,18 @@
|
||||
let BASE_URL=''
|
||||
if(process.env.UNI_PLATFORM =="h5"){
|
||||
if (window.location.href.indexOf('//casedata.igandan.com')>-1){
|
||||
BASE_URL='https://prod-casedata.igandan.com/api'
|
||||
BASE_URL='https://wx.igandan.com/gdhome_api'
|
||||
}else{
|
||||
BASE_URL='https://dev-casedata.igandan.com/api'
|
||||
BASE_URL='https://dev-wx.igandan.com/gdhome_api'
|
||||
}
|
||||
}else{
|
||||
}else if(process.env.UNI_PLATFORM =="mp-weixin"){
|
||||
const { envVersion } = uni.getAccountInfoSync().miniProgram;
|
||||
if (envVersion == "release") {
|
||||
BASE_URL='https://prod-casedata.igandan.com/api'
|
||||
BASE_URL='https://app.igandan.com/app'
|
||||
}else{
|
||||
BASE_URL='https://dev-casedata.igandan.com/api'
|
||||
BASE_URL='https://dev-app.igandan.com/app'
|
||||
}
|
||||
}else{
|
||||
BASE_URL='https://dev-app.igandan.com/app'
|
||||
}
|
||||
export default BASE_URL
|
||||
@ -26,7 +26,7 @@ function navTo(obj) {
|
||||
if(process.env.UNI_PLATFORM =="h5"){
|
||||
uni.navigateTo(obj)
|
||||
}else if(process.env.UNI_PLATFORM =="mp-weixin"){
|
||||
const pages = getCurrentPages();
|
||||
const pages = getCurrentPages();
|
||||
let len = pages.length;
|
||||
console.log(len)
|
||||
if (len >=10) {
|
||||
|
||||
@ -8,11 +8,8 @@
|
||||
* @createTime: 2024-7-22 15:05:06
|
||||
*/
|
||||
import BASE_URL from "./config.js";
|
||||
//import host from "@/utils/host";
|
||||
//import {msg} from "./util.js"
|
||||
//const BASE_URL=host+"/api"
|
||||
export const request = (url, data = {}, method = 'post', loading = false, contentType =
|
||||
'application/x-www-form-urlencoded') => {
|
||||
'application/json',extraHeader={}) => {
|
||||
if (loading) {
|
||||
uni.showLoading({
|
||||
title: '加载中',
|
||||
@ -45,11 +42,10 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
|
||||
let timestamp = Date.now();
|
||||
uni.request({
|
||||
data,
|
||||
url: url.indexOf('http') != -1 ? url : encodeURI(BASE_URL + url + "?timestamp=" +
|
||||
timestamp),
|
||||
url: url.indexOf('http') != -1 ? url : encodeURI(BASE_URL + url),
|
||||
method: method,
|
||||
sslVerify: false,
|
||||
header: url.indexOf('/manager/getSignature4bing') == -1 ? header : {},
|
||||
header:{...header,...extraHeader},
|
||||
timeout: 10000,
|
||||
success: async (res) => {
|
||||
var Authorization_token = res.header.Authorization;
|
||||
@ -64,7 +60,9 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
|
||||
if (loading) {
|
||||
uni.hideLoading();
|
||||
};
|
||||
if (res.data.code == 200){
|
||||
if(url.indexOf('manager/getSystemTimeStamp')!=-1){
|
||||
e(res)
|
||||
}else if (res.data.code == 200){
|
||||
e(res)
|
||||
}else if (res.data.code == 401 || res.data.code == 403 || res.data.code ==
|
||||
405 || res.data.code == 406) {
|
||||
|
||||
17
utils/sort.js
Normal file
17
utils/sort.js
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
function sortObj(obj){
|
||||
// 获取所有键并排序
|
||||
var keysSorted = Object.keys(obj).sort();
|
||||
// 创建新的空对象用于保存排序后的结果
|
||||
var sortedObj = {};
|
||||
// 根据排序后的键从原始JSON对象中提取值
|
||||
for (var i = 0; i < keysSorted.length; i++) {
|
||||
var key = keysSorted[i];
|
||||
sortedObj[key] = obj[key];
|
||||
}
|
||||
|
||||
return sortedObj
|
||||
console.log(sortedObj); // 输出排序后的JSON对象
|
||||
}
|
||||
export default sortObj
|
||||
|
||||
2
utils/version.js
Normal file
2
utils/version.js
Normal file
@ -0,0 +1,2 @@
|
||||
const version="3.6.0"
|
||||
export default version
|
||||
Loading…
x
Reference in New Issue
Block a user