Commit 9e4b3e50 by 陈岩

feat: 完成保持登录功能

1 parent 32b9de45
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
"name" : "CVA YAP", "name" : "CVA YAP",
"appid" : "__UNI__C2F3380", "appid" : "__UNI__C2F3380",
"description" : "", "description" : "",
"versionName" : "1.0.20", "versionName" : "1.0.21",
"versionCode" : 30, "versionCode" : 31,
"transformPx" : false, "transformPx" : false,
/* h5代理方式 */ /* h5代理方式 */
"h5" : { "h5" : {
...@@ -220,12 +220,12 @@ ...@@ -220,12 +220,12 @@
"androidStyle" : "default", "androidStyle" : "default",
"useOriginalMsgbox" : false, "useOriginalMsgbox" : false,
"android" : { "android" : {
"hdpi" : "/Users/yuki/vion_app/pmi/android/480x762-1.9.png", "hdpi" : "/Users/yuki/vion_app/CVA YAP/android/480x762-1.9.png",
"xhdpi" : "/Users/yuki/vion_app/pmi/android/720x1242-1.9.png", "xhdpi" : "/Users/yuki/vion_app/CVA YAP/android/720x1242-1.9.png",
"xxhdpi" : "/Users/yuki/vion_app/pmi/android/1080x1882-1.9.png" "xxhdpi" : "/Users/yuki/vion_app/CVA YAP/android/1080x1882-1.9.png"
}, },
"ios" : { "ios" : {
"storyboard" : "/Users/yuki/vion_app/pmi/ios/ios.zip" "storyboard" : "/Users/yuki/vion_app/CVA YAP/ios/ios_screen.zip"
} }
}, },
"icons" : { "icons" : {
......
...@@ -112,6 +112,7 @@ ...@@ -112,6 +112,7 @@
getAppVersionApi, getAppVersionApi,
getUserStoreEquipmentNumApi, getUserStoreEquipmentNumApi,
getMessageListApi, getMessageListApi,
bindClientIdApi
} from '@/api' } from '@/api'
import appUpdate from '@/uni_modules/wczd-app-update/js_sdk/app-update.js' import appUpdate from '@/uni_modules/wczd-app-update/js_sdk/app-update.js'
...@@ -262,7 +263,7 @@ ...@@ -262,7 +263,7 @@
const modalRef = ref(null) const modalRef = ref(null)
onMounted(() => { onMounted(async () => {
// #ifdef APP-PLUS // #ifdef APP-PLUS
checkAppUpdate() checkAppUpdate()
// #endif // #endif
...@@ -277,6 +278,17 @@ ...@@ -277,6 +278,17 @@
}) })
// #endif // #endif
getUserStoreEquipmentNum() 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 @@ ...@@ -90,8 +90,8 @@
<!-- <view v-if="!isLoginSuccess" class="login-info-tips">用户名或密码有误,登录失败!</view> --> <!-- <view v-if="!isLoginSuccess" class="login-info-tips">用户名或密码有误,登录失败!</view> -->
<view class="remember-password"> <view class="remember-password">
<uv-checkbox-group v-model="rememberValue" shape="square"> <uv-checkbox-group v-model="rememberValue" shape="square">
<uv-checkbox shape="circle" activeColor="#387CF5" size="15" labelSize="26rpx" labelColor="#90949D" name="remember" <uv-checkbox shape="circle" activeColor="#387CF5" size="15" labelSize="26rpx" labelColor="#90949D"
:label="t('login.rememberPw')"></uv-checkbox> name="remember" :label="t('login.rememberPw')"></uv-checkbox>
</uv-checkbox-group> </uv-checkbox-group>
</view> </view>
<view class="forget-password-text" @click="handleForgetPwd">{{ t('login.forgotPassword') }}?</view> <view class="forget-password-text" @click="handleForgetPwd">{{ t('login.forgotPassword') }}?</view>
...@@ -397,12 +397,23 @@ ...@@ -397,12 +397,23 @@
if (code === 200) { if (code === 200) {
uni.hideLoading() uni.hideLoading()
} }
const { const {
atoken, atoken,
rtoken,
user user
} = data } = data
uni.setStorageSync('Authorization', atoken) uni.setStorageSync('Authorization', atoken)
uni.setStorageSync('rtoken', rtoken)
uni.setStorageSync('user', JSON.stringify(user)) uni.setStorageSync('user', JSON.stringify(user))
// uni.setStorageSync('Authorization', '165af547-08ba-4ddf-9286-bc8ce37cca39')
// uni.setStorageSync('rtoken', '61b5b0ca-5a0c-40df-8068-d5b390eef072')
const { const {
data: accountList data: accountList
...@@ -720,4 +731,4 @@ ...@@ -720,4 +731,4 @@
gap: 10rpx; gap: 10rpx;
align-items: center; align-items: center;
} }
</style> </style>
\ No newline at end of file \ No newline at end of file
...@@ -317,7 +317,7 @@ function download(updateInfo) { ...@@ -317,7 +317,7 @@ function download(updateInfo) {
let platform = updateInfo.platform || 'android' let platform = updateInfo.platform || 'android'
if (updateInfo.downUrl) { if (updateInfo.downUrl) {
if (platform == 'ios') { if (platform == 'ios') {
plus.runtime.openURL(updateInfo.downUrl) plus.runtime.openURL('https://apps.apple.com/cn/app/id6752882316')
} }
if (platform == 'android') { if (platform == 'android') {
plus.runtime.openURL('https://play.google.com/store/apps/details?id=com.viontech.app.pmistore') 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 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 let requestFlag = true
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') { export default function request(options, ipType = 'ip') {
// 根据不同的类型选择不同的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 = { const ipTypeMap = {
ip: host.ip, ip: host.ip,
assetsIp: host.assetsIp assetsIp: host.assetsIp
} }
// const url = (ipType === 'store.keliuyun' ? host.ip : ipTypeMap[ipType]) + options.url
const url = ipTypeMap[ipType] + options.url const url = ipTypeMap[ipType] + options.url
console.log(url,'=s=s');
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const systemInfo = uni.getSystemInfoSync() const systemInfo = uni.getSystemInfoSync()
// 请求方式
const method = options.method?.toUpperCase() || 'GET'; 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 headerContentType = method === 'GET' ? 'application/x-www-form-urlencoded' : 'application/json';
const _data = {
const _data = Array.isArray(options.data) ? options.data : {
...(options.data || {}), ...(options.data || {}),
}; }
// const _data = {
// ...(options.data || {}),
// // _t:Date.now()
// }
const languageObj = { const languageObj = {
'mall_CN': 'zh-CN,zh;q=0.9', 'mall_CN': 'zh-CN,zh;q=0.9',
'zh_CN': 'zh-CN,zh;q=0.9', 'zh_CN': 'zh-CN,zh;q=0.9',
'en_US': 'en-US,en;q=0.5', 'en_US': 'en-US,en;q=0.5',
'zh_TW': 'zh-TW,zh;q=0.3', 'zh_TW': 'zh-TW,zh;q=0.3',
'ja_JP':'ja-JP,ja;q=0.9'
} }
const lang = uni.getStorageSync('lang') || host.defaultLang const lang = uni.getStorageSync('lang') || host.defaultLang
const setting = {
url, const createRequestConfig = (authorization = null) => {
data: _data, const authToken = authorization || uni.getStorageSync('Authorization') || ''
method,
timeout: 60 * 1000, return {
header: { url,
'content-type': headerContentType, data: _data,
// 请求体中添加设备信息 method,
'Device-OS': systemInfo.platform || 'unknown', // 直接使用同步获取的信息 timeout: 60 * 1000,
'Device-Model': systemInfo.model || 'unknown', header: {
'Authorization': uni.getStorageSync('Authorization') || '', 'content-type': headerContentType,
'Accept-Language': languageObj[lang] || 'en-US,en;q=0.5', 'Device-OS': systemInfo.platform || 'unknown',
}, 'Device-Model': systemInfo.model || 'unknown',
success(res) { 'Authorization': authToken,
// 如果是静态资源,直接返回 'Accept-Language': languageObj[lang] || 'zh-CN,zh;q=0.9',
if (ipType === 'assetsIp') { },
resolve(res.data); }
return; }
}
// 否则判断res.data内部的code // 执行请求
if (res.data.ecode === 401 && ipType !== 'store.keliuyun') { const executeRequest = (authorization = null) => {
if (requestFlag) { let setting = createRequestConfig(authorization)
requestFlag = false
uni.showModal({ if (options.header) {
title: t('message.prompt'), setting.header = Object.assign(setting.header, options.header);
showCancel: false, }
content: t('Message.atokeIsDisabled'),
confirmText: t('app.login.reLogin'), uni.request({
success: function(resModal) { ...setting,
if (resModal.confirm) { success: async (res) => {
requestFlag = true // 如果是静态资源,直接返回
uni.removeStorageSync('Authorization') if (ipType === 'assetsIp' || ipType === 'StoreAssetsIp') {
// uni.clearStorageSync() resolve(res.data);
uni.reLaunch({ return;
url: '/pages/login/index'
})
} else if (resModal.cancel) {
console.log('用户点击取消');
}
}
})
} }
} else if (res.data.code !== 200) { // 处理401状态码
if(!options.hiddenMsg){ if (res.data.ecode === 401 && ipType !== 'store.keliuyun') {
uni.showToast({ // 如果是刷新token的接口本身返回401,直接跳转登录
icon: 'none', if (options.url === '/report/auth/api') {
title: res?.data?.msg || 'Error' 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);
} }
reject(res?.data?.msg || 'Error') },
} else { fail(err) {
resolve(res.data); 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: 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'
})
} else if (resModal.cancel) {
console.log('用户点击取消');
} }
}
})
}
}
}, // 处理请求错误
fail(err) { function handleRequestError(res, options, reject) {
uni.showToast({ const errorMsg = res?.data?.msg || 'Error'
icon: 'none', if (!options.hiddenMsg) {
title: t('app.login.networkError') uni.showToast({
}) icon: 'none',
reject(err); title: errorMsg
}, })
}
// 针对登录接口 单独修改报错返回信息
if (options.url === '/report/users/login') {
if (['用户名/密码错误', 'Username/Password Incorrect', '用戶名/密碼錯誤'].includes(errorMsg)) {
reject('U/P Error')
} }
if (options.header) { // 如果是502 提示默认密码 需要修改 同时传递atoken过去
setting.header = Object.assign(setting.header, options.header); if (res.data.code === 502) {
reject(`P Reset` + '*$*' + `${res?.data?.data?.atoken || ''}`)
} }
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!