8.15提交

This commit is contained in:
zoujiandong 2025-08-15 17:50:07 +08:00
parent fb4e5b6af1
commit 87647c2c84
12 changed files with 385 additions and 68 deletions

View File

@ -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
View 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存储和用户信息保存

View File

@ -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

View File

@ -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" : ""
}
}
}
}

View File

@ -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"
}
}

View File

@ -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
View 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

View File

@ -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

View File

@ -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) {

View File

@ -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
View 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
View File

@ -0,0 +1,2 @@
const version="3.6.0"
export default version