Commit 9e4b3e50 by 陈岩

feat: 完成保持登录功能

1 parent 32b9de45
......@@ -2,8 +2,8 @@
"name" : "CVA YAP",
"appid" : "__UNI__C2F3380",
"description" : "",
"versionName" : "1.0.20",
"versionCode" : 30,
"versionName" : "1.0.21",
"versionCode" : 31,
"transformPx" : false,
/* h5代理方式 */
"h5" : {
......@@ -220,12 +220,12 @@
"androidStyle" : "default",
"useOriginalMsgbox" : false,
"android" : {
"hdpi" : "/Users/yuki/vion_app/pmi/android/480x762-1.9.png",
"xhdpi" : "/Users/yuki/vion_app/pmi/android/720x1242-1.9.png",
"xxhdpi" : "/Users/yuki/vion_app/pmi/android/1080x1882-1.9.png"
"hdpi" : "/Users/yuki/vion_app/CVA YAP/android/480x762-1.9.png",
"xhdpi" : "/Users/yuki/vion_app/CVA YAP/android/720x1242-1.9.png",
"xxhdpi" : "/Users/yuki/vion_app/CVA YAP/android/1080x1882-1.9.png"
},
"ios" : {
"storyboard" : "/Users/yuki/vion_app/pmi/ios/ios.zip"
"storyboard" : "/Users/yuki/vion_app/CVA YAP/ios/ios_screen.zip"
}
},
"icons" : {
......
......@@ -112,6 +112,7 @@
getAppVersionApi,
getUserStoreEquipmentNumApi,
getMessageListApi,
bindClientIdApi
} from '@/api'
import appUpdate from '@/uni_modules/wczd-app-update/js_sdk/app-update.js'
......@@ -262,7 +263,7 @@
const modalRef = ref(null)
onMounted(() => {
onMounted(async () => {
// #ifdef APP-PLUS
checkAppUpdate()
// #endif
......@@ -277,6 +278,17 @@
})
// #endif
getUserStoreEquipmentNum()
// #ifdef APP-PLUS
const clientInfo = uni.getDeviceInfo()
await bindClientIdApi({
clientId: uni.getStorageSync('clientId'),
clientType: clientInfo.osName, // ios or android
deviceInfo: JSON.stringify(clientInfo), // 备用传值
language: uni.getStorageSync('lang') || '',
status: 1 // 是否有效 1:有效 0: 无效
})
// #endif
})
/********** 统计相关 ***********/
......
......@@ -90,8 +90,8 @@
<!-- <view v-if="!isLoginSuccess" class="login-info-tips">用户名或密码有误,登录失败!</view> -->
<view class="remember-password">
<uv-checkbox-group v-model="rememberValue" shape="square">
<uv-checkbox shape="circle" activeColor="#387CF5" size="15" labelSize="26rpx" labelColor="#90949D" name="remember"
:label="t('login.rememberPw')"></uv-checkbox>
<uv-checkbox shape="circle" activeColor="#387CF5" size="15" labelSize="26rpx" labelColor="#90949D"
name="remember" :label="t('login.rememberPw')"></uv-checkbox>
</uv-checkbox-group>
</view>
<view class="forget-password-text" @click="handleForgetPwd">{{ t('login.forgotPassword') }}?</view>
......@@ -397,13 +397,24 @@
if (code === 200) {
uni.hideLoading()
}
const {
atoken,
rtoken,
user
} = data
uni.setStorageSync('Authorization', atoken)
uni.setStorageSync('rtoken', rtoken)
uni.setStorageSync('user', JSON.stringify(user))
// uni.setStorageSync('Authorization', '165af547-08ba-4ddf-9286-bc8ce37cca39')
// uni.setStorageSync('rtoken', '61b5b0ca-5a0c-40df-8068-d5b390eef072')
const {
data: accountList
} = await getUserAccountsApi({
......
......@@ -317,7 +317,7 @@ function download(updateInfo) {
let platform = updateInfo.platform || 'android'
if (updateInfo.downUrl) {
if (platform == 'ios') {
plus.runtime.openURL(updateInfo.downUrl)
plus.runtime.openURL('https://apps.apple.com/cn/app/id6752882316')
}
if (platform == 'android') {
plus.runtime.openURL('https://play.google.com/store/apps/details?id=com.viontech.app.pmistore')
......
import host from '@/utils/ip-config.js';
import { t } from '@/plugins/index.js'
let requestFlag = true
// 请求封装
export default function request(options, ipType = 'ip') {
// 根据不同的类型选择不同的ip
// // #ifdef APP-PLUS
// const ipTypeMap = {
// ip: uni.getStorageSync('serverIp'),
// assetsIp: `${uni.getStorageSync('serverIp')}/images`
// }
// // #endif
// // #ifndef APP-PLUS
// const ipTypeMap = {
// ip: host.ip,
// assetsIp: host.assetsIp
// }
// // #endif
// 声明部分接口必须要用store.keliuyun.com服务
const ipTypeMap = {
ip: host.ip,
assetsIp: host.assetsIp
}
// const url = (ipType === 'store.keliuyun' ? host.ip : ipTypeMap[ipType]) + options.url
const url = ipTypeMap[ipType] + options.url
return new Promise((resolve, reject) => {
const systemInfo = uni.getSystemInfoSync()
// 请求方式
const method = options.method?.toUpperCase() || 'GET';
// 根据method类型调整header,GET为?a=x&b=y,POST为json
const headerContentType = method === 'GET' ? 'application/x-www-form-urlencoded' : 'application/json';
const _data = Array.isArray(options.data) ? options.data : {
...(options.data || {}),
};
// const _data = {
// ...(options.data || {}),
// // _t:Date.now()
// }
const languageObj = {
'mall_CN': 'zh-CN,zh;q=0.9',
'zh_CN': 'zh-CN,zh;q=0.9',
'en_US': 'en-US,en;q=0.5',
'zh_TW': 'zh-TW,zh;q=0.3',
'ja_JP':'ja-JP,ja;q=0.9'
}
const lang = uni.getStorageSync('lang') || host.defaultLang
const setting = {
url,
data: _data,
method,
timeout: 60 * 1000,
header: {
'content-type': headerContentType,
// 请求体中添加设备信息
'Device-OS': systemInfo.platform || 'unknown', // 直接使用同步获取的信息
'Device-Model': systemInfo.model || 'unknown',
'Authorization': uni.getStorageSync('Authorization') || '',
'Accept-Language': languageObj[lang] || 'en-US,en;q=0.5',
},
success(res) {
// 如果是静态资源,直接返回
if (ipType === 'assetsIp') {
resolve(res.data);
return;
}
// 否则判断res.data内部的code
if (res.data.ecode === 401 && ipType !== 'store.keliuyun') {
console.log(1);
if (requestFlag) {
requestFlag = false
uni.showModal({
title: t('message.prompt'),
showCancel: false,
content: t('Message.atokeIsDisabled'),
confirmText: t('app.login.reLogin'),
success: function(resModal) {
if (resModal.confirm) {
requestFlag = true
uni.removeStorageSync('Authorization')
// uni.clearStorageSync()
uni.reLaunch({
url: '/pages/login/index'
})
} else if (resModal.cancel) {
console.log('用户点击取消');
}
}
})
}
} else if (res.data.code !== 200) {
if(!options.hiddenMsg){
uni.showToast({
icon: 'none',
title: res?.data?.msg || 'Error'
})
}
reject(res?.data?.msg || 'Error')
} else {
resolve(res.data);
}
},
fail(err) {
uni.showToast({
icon: 'none',
title: t('app.login.networkError')
})
reject(err);
},
}
if (options.header) {
setting.header = Object.assign(setting.header, options.header);
}
uni.request(setting);
})
}
import host from '@/utils/ip-config.js';
import { t } from '@/plugins/index.js'
import {
t
} from '@/plugins/index.js'
import {
getStageObj
} from '@/utils/index.js'
let requestFlag = true
// 请求封装
export default function request(options, ipType = 'ip') {
let isRefreshing = false // 是否正在刷新token
let refreshSubscribers = [] // 重试队列
// 添加订阅
function addSubscriber(callback) {
refreshSubscribers.push(callback)
}
// 执行订阅的回调
function executeSubscribers(newToken) {
refreshSubscribers.forEach(callback => callback(newToken))
refreshSubscribers = []
}
// 刷新token的函数
async function refreshToken(hostUrlIp) {
if (isRefreshing) {
// 如果已经在刷新,返回一个Promise等待刷新完成
return new Promise((resolve) => {
addSubscriber(resolve)
})
}
isRefreshing = true
try {
const rtoken = uni.getStorageSync('rtoken')
if (!rtoken) {
throw new Error('No refresh token')
}
const userInfoStage = getStageObj('user')
// 使用原生uni.request,避免使用封装的request导致循环调用
const refreshResult = await new Promise((resolve, reject) => {
uni.request({
url: hostUrlIp + `/report/auth/api/v1/auth/users/${userInfoStage.unid}/atoken`, // 根据你的实际接口调整
method: 'GET',
header: {
'Authorization': rtoken
},
success: (res) => {
if (res.data.atoken) {
resolve(res.data.atoken)
} else {
handleAuthFailure('登录已失效,请重新登录')
reject(new Error(res.data.msg || '刷新token失败'))
}
},
fail: (err) => {
reject(err)
}
})
})
// 存储新的Authorization
uni.setStorageSync('Authorization', refreshResult)
isRefreshing = false
// 执行所有等待的回调
executeSubscribers(refreshResult)
return refreshResult
} catch (error) {
console.error('刷新token失败:', error)
isRefreshing = false
refreshSubscribers = [] // 清空队列
throw error
}
}
// 请求封装
export default function request(options, ipType = 'ip') {
// 根据不同的类型选择不同的ip
// // #ifdef APP-PLUS
// const ipTypeMap = {
// ip: uni.getStorageSync('serverIp'),
// assetsIp: `${uni.getStorageSync('serverIp')}/images`
// }
// // #endif
// // #ifndef APP-PLUS
// const ipTypeMap = {
// ip: host.ip,
// assetsIp: host.assetsIp
// }
// // #endif
// 声明部分接口必须要用store.keliuyun.com服务
const ipTypeMap = {
ip: host.ip,
assetsIp: host.assetsIp
}
// const url = (ipType === 'store.keliuyun' ? host.ip : ipTypeMap[ipType]) + options.url
const url = ipTypeMap[ipType] + options.url
console.log(url,'=s=s');
return new Promise((resolve, reject) => {
const systemInfo = uni.getSystemInfoSync()
// 请求方式
const method = options.method?.toUpperCase() || 'GET';
// 根据method类型调整header,GET为?a=x&b=y,POST为json
const headerContentType = method === 'GET' ? 'application/x-www-form-urlencoded' : 'application/json';
const _data = Array.isArray(options.data) ? options.data : {
const _data = {
...(options.data || {}),
};
// const _data = {
// ...(options.data || {}),
// // _t:Date.now()
// }
}
const languageObj = {
'mall_CN': 'zh-CN,zh;q=0.9',
'zh_CN': 'zh-CN,zh;q=0.9',
'en_US': 'en-US,en;q=0.5',
'zh_TW': 'zh-TW,zh;q=0.3',
'ja_JP':'ja-JP,ja;q=0.9'
}
const lang = uni.getStorageSync('lang') || host.defaultLang
const setting = {
const createRequestConfig = (authorization = null) => {
const authToken = authorization || uni.getStorageSync('Authorization') || ''
return {
url,
data: _data,
method,
timeout: 60 * 1000,
header: {
'content-type': headerContentType,
// 请求体中添加设备信息
'Device-OS': systemInfo.platform || 'unknown', // 直接使用同步获取的信息
'Device-OS': systemInfo.platform || 'unknown',
'Device-Model': systemInfo.model || 'unknown',
'Authorization': uni.getStorageSync('Authorization') || '',
'Accept-Language': languageObj[lang] || 'en-US,en;q=0.5',
'Authorization': authToken,
'Accept-Language': languageObj[lang] || 'zh-CN,zh;q=0.9',
},
success(res) {
}
}
// 执行请求
const executeRequest = (authorization = null) => {
let setting = createRequestConfig(authorization)
if (options.header) {
setting.header = Object.assign(setting.header, options.header);
}
uni.request({
...setting,
success: async (res) => {
// 如果是静态资源,直接返回
if (ipType === 'assetsIp') {
if (ipType === 'assetsIp' || ipType === 'StoreAssetsIp') {
resolve(res.data);
return;
}
// 否则判断res.data内部的code
// 处理401状态码
if (res.data.ecode === 401 && ipType !== 'store.keliuyun') {
// 如果是刷新token的接口本身返回401,直接跳转登录
if (options.url === '/report/auth/api') {
handleAuthFailure(res?.data?.enote || t('Message.authLoginError'));
return;
}
try {
// 尝试刷新token
const newToken = await refreshToken(host.ip)
console.log(newToken);
// 使用新token重试当前请求
executeRequest(newToken)
} catch (error) {
// 刷新token失败,跳转登录
handleAuthFailure(t('Message.authLoginError'));
}
return;
} else if (res.data.code !== 200) {
handleRequestError(res, options, reject);
} else {
resolve(res.data);
}
},
fail(err) {
console.log('err', err);
uni.showToast({
icon: 'none',
title: t(err.errMsg || 'app.login.networkError')
})
reject(err);
},
});
}
// 开始第一次请求
executeRequest();
})
}
// 处理认证失败
function handleAuthFailure(message) {
if (requestFlag) {
requestFlag = false
uni.showModal({
title: t('message.prompt'),
showCancel: false,
content: t('Message.atokeIsDisabled'),
content: message,
confirmText: t('app.login.reLogin'),
success: function(resModal) {
if (resModal.confirm) {
requestFlag = true
uni.removeStorageSync('Authorization')
uni.removeStorageSync('rtoken')
// uni.clearStorageSync()
uni.reLaunch({
url: '/pages/login/index'
......@@ -92,31 +204,27 @@ export default function request(options, ipType = 'ip') {
}
})
}
}
} else if (res.data.code !== 200) {
if(!options.hiddenMsg){
// 处理请求错误
function handleRequestError(res, options, reject) {
const errorMsg = res?.data?.msg || 'Error'
if (!options.hiddenMsg) {
uni.showToast({
icon: 'none',
title: res?.data?.msg || 'Error'
title: errorMsg
})
}
reject(res?.data?.msg || 'Error')
} else {
resolve(res.data);
// 针对登录接口 单独修改报错返回信息
if (options.url === '/report/users/login') {
if (['用户名/密码错误', 'Username/Password Incorrect', '用戶名/密碼錯誤'].includes(errorMsg)) {
reject('U/P Error')
}
},
fail(err) {
uni.showToast({
icon: 'none',
title: t('app.login.networkError')
})
reject(err);
},
// 如果是502 提示默认密码 需要修改 同时传递atoken过去
if (res.data.code === 502) {
reject(`P Reset` + '*$*' + `${res?.data?.data?.atoken || ''}`)
}
if (options.header) {
setting.header = Object.assign(setting.header, options.header);
}
uni.request(setting);
})
reject(errorMsg)
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!