From 0ee0294ed6f92af2113b6016db01978f7148fe71 Mon Sep 17 00:00:00 2001 From: "liujw155@outlook.com" Date: Wed, 22 Jan 2025 15:02:22 +0800 Subject: [PATCH] reconstruction index authorization for tid1807 --- src/App.vue | 3 +- src/js/server.js | 12 + src/main.js | 3 +- src/pages.json | 14 +- src/pages/index/index.vue | 275 +++------------------ src/store/actions.js | 43 +++- src/store/index.js | 9 +- src/store/mutations.js | 13 + src/subpackage/authorization/components/login.vue | 190 ++++++++++++++ src/subpackage/authorization/js/api.js | 7 + src/subpackage/authorization/js/server.js | 10 + src/subpackage/authorization/pages/index.vue | 16 ++ .../authorization/static/images/author_modal.png | Bin 0 -> 14649 bytes 13 files changed, 342 insertions(+), 253 deletions(-) create mode 100644 src/subpackage/authorization/components/login.vue create mode 100644 src/subpackage/authorization/js/api.js create mode 100644 src/subpackage/authorization/js/server.js create mode 100644 src/subpackage/authorization/pages/index.vue create mode 100644 src/subpackage/authorization/static/images/author_modal.png diff --git a/src/App.vue b/src/App.vue index 5335f6f..65974ff 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,9 +3,8 @@ import util from './utils/util'; export default { onLaunch: function() { - // #ifndef H5 + this.$store.commit('setLoginState', { loginState: false }); this.updateManager(); - // #endif }, methods: { isLogin(){ diff --git a/src/js/server.js b/src/js/server.js index c671b75..dc191c7 100644 --- a/src/js/server.js +++ b/src/js/server.js @@ -1,5 +1,6 @@ import util from '../utils/util'; // import { app as vm } from '../main'; +import { app as vm } from '../main'; const islog = true; @@ -27,6 +28,9 @@ export class Server { method: method, data: res, }) + + handleUserExceptions({ res }); + if(isDefaultGet){ if(failMsg == '')throw Error('默认回调,失败提示不能为空 key -> failMsg'); defaultGet({ @@ -70,6 +74,14 @@ export class Server { reject({url,res,data}); } } + // 用户信息异常 + function handleUserExceptions({ res }){ + if(res?.data?.code === 401&&res?.data?.message?.indexOf('用户') !== -1){ + console.log('用户信息异常,请重新登录'); + const _store = vm.$store; + _store.commit('setLoginState', { loginState: false }); + } + } } get({url,data={},header={},isDefaultGet=true,failMsg=''}){ return this.request(url,data,'GET',header,isDefaultGet,failMsg); diff --git a/src/main.js b/src/main.js index 38f2407..049f478 100644 --- a/src/main.js +++ b/src/main.js @@ -6,8 +6,9 @@ Vue.config.productionTip = false App.mpType = 'app' -const app = new Vue({ +export const app = new Vue({ ...App, store }) app.$mount() + diff --git a/src/pages.json b/src/pages.json index 5e5e3a7..e1bf9d5 100644 --- a/src/pages.json +++ b/src/pages.json @@ -4,7 +4,8 @@ "path": "pages/index/index", "style": { "componentPlaceholder": { - "bottom-logo": "view" + "bottom-logo": "view", + "authorization-login": "view" } } }, @@ -969,6 +970,17 @@ } } ] + }, + { + "root": "subpackage/authorization", + "pages": [ + { + "path": "pages/index", + "style" : { + "navigationBarTitleText": "" + } + } + ] } ], "globalStyle": { diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue index 848b883..6751c16 100644 --- a/src/pages/index/index.vue +++ b/src/pages/index/index.vue @@ -2,22 +2,22 @@ - + {{indexData.brand_name || '-'}}(共{{indexData.stadium_num || '0'}}家) 点击登陆 今日总收入 - {{loginStatus?'¥':''}}{{loginStatus?(indexData.amount || '0'):'***'}} + {{loginState?'¥':''}}{{loginState?(indexData.amount || '0'):'***'}} 收款笔数 - {{loginStatus?(indexData.in_count || '0'):'**'}} + {{loginState?(indexData.in_count || '0'):'**'}} 退款笔数 - {{loginStatus?(indexData.out_count || '0'):'**'}} + {{loginState?(indexData.out_count || '0'):'**'}} @@ -52,33 +52,9 @@ - - - 微信授权 - 您的信息和数据将受到保护 - - - + - - - - - - - + 核销 @@ -91,7 +67,9 @@ import util from '../../utils/util'; import { servers } from '../../js/server'; import { API } from '../../js/api'; + import { mapGetters } from 'vuex'; import bottomLogo from "@/subpackage/menu/components/bottom_logo.vue"; + import authorizationLogin from "@/subpackage/authorization/components/login.vue"; const tabList = [ { id: 0, @@ -176,57 +154,34 @@ ]; const app = getApp(); - - // #ifndef H5 - - const uniGetSetting = util.promisify(uni.getSetting); - const uniLogin = util.promisify(uni.login); - const uniGetUserInfo= util.promisify(uni.getUserInfo); const APPID = uni.getAccountInfoSync().miniProgram.appId; - - // #endif - - // #ifdef H5 - const APPID = "wx7106e84614cf0060" //TODO - // #endif export default { - components: { bottomLogo }, + components: { bottomLogo, authorizationLogin }, computed: { - isProfile: _=>util.isProfile(), - // loginStatus(){ - // return app.isLogin(); - // } + ...mapGetters([ 'loginState' ]), }, data() { return { tabList, - isShowAuthorModal: false, indexData: {}, - loginStatus: false, } }, async onLoad(options) { - try{ - util.showLoad(); + let _loginRes = {}; // 20230829 新增平台账户不绑定,不检验,每次进来走重新登陆绑定流程 - if(options.role !== 'ADMIN-PLATFORM')await this.checkUserAuthor(); - - util.hideLoad(); - this.loginStatus = app.isLogin(); - if(!!app.isLogin())this.getIndexInfo(); + if(options.role !== 'ADMIN-PLATFORM')_loginRes = await this.$store.dispatch('checkUserAuthor'); + // 20240325 新增openid绑定账号检测 + if(_loginRes?.data === '')this.webBrandUserCheck(); + if(_loginRes?.data)this.getIndexInfo(); }catch(err){ - util.hideLoad(); - uni.removeStorageSync('token'); - // util.showNone(err.message || '身份校验失败!'); - console.log('onLoad err',err); - this.loginStatus = app.isLogin(); + console.warn('pages index onLoad err--->', err); } }, onShow(){ - let { indexData } = this; - if(JSON.stringify(indexData)!='{}'&&!!app.isLogin())this.getIndexInfo(); + let { indexData, loginState, getIndexInfo } = this; + if(JSON.stringify(indexData)!='{}'&&loginState)getIndexInfo(); }, methods: { // 账号有开启了权限的才显示菜单,否则不显示【ID1000840】 @@ -238,13 +193,13 @@ return false; }, toNoticeList(){ - if(!app.isLogin())return this.showAuthor(); + if(!this.loginState)return this.showAuthor(); util.routeTo(`/pages/message/list/list`,'nT'); }, toPageInfo(tabInfo){ - let { indexData } = this; + let { indexData, loginState } = this; let _permission = indexData.permission || {}; - if(!app.isLogin())return this.showAuthor(); + if(!loginState)return this.showAuthor(); if(!tabInfo.path)return util.showNone('暂未开放!'); //if(tabInfo.id == 2)return util.routeTo(tabInfo.path,'nT'); //测试 if(!_permission[tabInfo.serverKey])return util.showNone('暂无权限,请联系管理员开启!') @@ -259,7 +214,6 @@ if([ 10, 11, 4 ].includes(tabInfo.id))return util.routeTo(tabInfo.path + `?brand_id=${indexData.brand.id}`,'nT'); if(tabInfo.id === 12){ - // if(!indexData.brand.mini_wechat_appid)return util.showNone('暂无appid!') let url = tabInfo.path + `?appid=${indexData.brand.mini_wechat_appid}` console.log(222,url) return util.routeTo(url,'nT'); @@ -280,137 +234,15 @@ this.$store.commit('setBrandInfo',res); }) }, - // 看是否授权,清除token - async checkUserAuthor(){ - try{ - let loginRes ="" - // #ifndef H5 - loginRes = await uniLogin(); - // #endif - // #ifdef H5 - loginRes = {errMsg: "login:ok", code: "0c3xEi0w3kr1t23zcU3w3ZQR3w3xEi0h"} //TODO 设置H5测试数据 - // #endif - console.log(loginRes) - return servers.get({ - url: API.wechatMiniAppLoginAndSync, - data: { - code: loginRes.code, - appid: APPID, - // #ifdef H5 - token:"3d2b0092-e761-11ee-8a66-5254005df464", //TODO - // #endif - }, - isDefaultGet: false, - }) - .then(res=>{ - util.hideLoad(); - let _data = res.data; - if(_data.code == 0){ - // let _data = res.data.data; - - if(_data.data == ''){ - uni.removeStorageSync('token'); - this.loginStatus = app.isLogin(); - // 20240325 新增openid绑定账号检测 - this.webBrandUserCheck(); - - // return util.routeTo(`/pages/login/login`,'rL'); - } - uni.setStorageSync('token',_data.data); - return _data; - }else{ - util.showNone(_data.message || '校验身份失败!'); - throw res.data || {}; - } - - }) - .catch(err=>{ - throw err; - }) - }catch(err){ - throw err; - } - }, showAuthor(){ - this.isShowAuthorModal = true - }, - closeAuthor(){ - this.isShowAuthorModal = false - }, - cancelAuthor(){ - this.closeAuthor(); - }, - getLoginQuery({ - userInfo, - loginRes - }){ - return { - appid: APPID, - code: loginRes.code, - encryptedData: userInfo.encryptedData, - // is_details: 1, - // 后端解密错误,直接传用户信息 - user_info: userInfo.userInfo, - user_raw_data: userInfo.rawData, - ...userInfo.userInfo, - } - }, - // 新获取用户信息 - profileConfirm(){ - uni.getUserProfile({ - lang: 'zh_CN', desc: '授权登陆', - success: res => { - this.confirmAuthor({detail: {...res}}); + this.$refs?.authorizationLogin?.alert?.({ + success: ()=>{ + setTimeout(this.getIndexInfo, 1000); }, - fail: function(err) { - util.showNone('获取用户信息失败!请重试'); - console.warn('getUserProfile Err', err) + fail: err =>{ + // console.warn('pages index showAuthor authorizationLogin Err ->', err) } }) - }, - - async confirmAuthor(userRes){ - if(!userRes.detail.userInfo){ - this.closeAuthor(); - return util.showNone('获取用户信息失败!请稍后重试'); - } - let loginRes = await uniLogin(); - - if(!loginRes.code){ - this.closeAuthor(); - return util.showNone('获取登陆凭证失败!稍后重试'); - } - - servers.post({ - url: API.wechatMiniAppLoginAndSync, - data: this.getLoginQuery({ - userInfo: userRes.detail, - loginRes - }), - isDefaultGet: false, - }) - .then(res=>{ - util.hideLoad(); - let _data = res.data || {}; - if(_data.code == 0){ - if(_data.data == '')return util.routeTo(`/pages/login/login`,'rL'); - util.showNone(_data.message || '登陆成功!'); - // let _data = res.data.data; - // if(_data.user.role == '')return util.routeTo(`/pages/merchant_login/merchant_login`,'rL'); - - uni.setStorageSync('token',_data.data); - setTimeout(_=>{ - this.getIndexInfo(); - this.closeAuthor(); - this.loginStatus = app.isLogin(); - }, 1200); - }else{ - util.showNone(_data.message || '后台登陆失败!'); - setTimeout(_=>this.closeAuthor(), 1200); - } - - }).catch(util.hideLoad) - }, webBrandUserCheck(){ @@ -456,8 +288,8 @@ }) }, toStoreList(){ - if(!app.isLogin())return this.showAuthor(); - let { indexData } = this; + let { indexData, loginState } = this; + if(!loginState)return this.showAuthor(); util.routeTo(`/pages/store_list/store_list?brand_id=${indexData.brand.id}`,'nT'); }, bindUserOpenid(){ @@ -687,57 +519,6 @@ } } - .ic-author-modal{ - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - padding-top: 60upx; - width: 662upx; - height: 884upx; - border-radius: 10upx; - background-color: #fff; - .iam-title{ - margin-bottom: 22upx; - text-align: center; - line-height: 60upx; - font-size: 44upx; - font-weight: 500; - color: #1a1a1a; - } - .iam-tip{ - margin-bottom: 52upx; - line-height: 40upx; - text-align: center; - font-size: 28upx; - color: #9c9c9f; - } - .iam-pic{ - margin: 0 auto 62upx; - display: block; - width: 488upx; - height: 416upx; - } - .iam-btns{ - @include centerFlex(center); - >button{ - margin: 0 20upx; - width: 240upx; - height: 92upx; - line-height: 88upx; - text-align: center; - border-radius: 46upx; - border: 2upx solid $themeColor; - font-size: 32upx; - color: $themeColor; - &+button{ - background-color: $themeColor; - color: #fff; - } - } - } - - } // fly_btn .fly_btn{ diff --git a/src/store/actions.js b/src/store/actions.js index 2babf68..c5ebddc 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -1,7 +1,7 @@ // 异步方法 import { servers } from '../js/server'; import { API } from '../js/api'; -import { showLoad, hideLoad, showModal } from '../utils/util'; +import { showLoad, hideLoad, showModal, promisify } from '../utils/util'; export default { getBrandInfo({commit, state}){ @@ -56,4 +56,45 @@ export default { // return Promise.reject(err); }) }, + // 看是否授权,清除token 也可以理解自动登陆 + async checkUserAuthor({ commit, state }){ + let loginRes = {}; + try{ + const uniLogin = promisify(uni.login); + loginRes = await uniLogin(); + }catch(err){ + console.warn('store actions checkUserAuthor login err', err); + showModal({ content: '获取登陆凭证失败!请稍后重新登录!' }); + return Promise.reject(err); + } + showLoad(); + return servers.post({ + url: API.wechatMiniAppLoginAndSync, + data: { + code: loginRes.code, + appid: state.APPID, + }, + isDefaultGet: false, + }) + .then(res => { + hideLoad(); + let _data = res?.data || {}; + if(_data.code === 0){ + if(_data.data === ''){ + commit('setLoginState', { loginState: false, token: '' }); + return _data; + } + commit('setLoginState', { loginState: true, token: _data.data }); + return _data; + }else{ + return Promise.reject(_data); + } + }) + .catch(err=>{ + console.warn('store actions checkUserAuthor wechatMiniAppLoginAndSync err --->', err); + hideLoad(); + showModal({ content: err?.message || '静默登录失败!请稍后重试!' }); + return Promise.reject(err); + }) + }, } \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index 38ef2f5..6c1f777 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -54,7 +54,8 @@ export default new Vuex.Store({ typeInfo: {}, // 球场类型 venueList: [], // 选择场地列表 orderInfo: {}, // 订单信息 // 20230620 新增挂账需求,用于回显确认订单 - } + }, + isLogin: false, // 登陆状态 }, mutations, actions, @@ -69,6 +70,12 @@ export default new Vuex.Store({ } console.warn(_arr.length) return _arr; + }, + loginState: state =>{ + let _isLogin = state.isLogin; + let _token = uni.getStorageSync('token'); + // 只是想获得一个响应的token状态 + if(_isLogin || !_isLogin)return !!_token; } } }); diff --git a/src/store/mutations.js b/src/store/mutations.js index 0918706..4d9a91d 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -10,5 +10,18 @@ export default { // 场地占用信息 setOccupyInfo(state, _occupyInfo){ state.occupyInfo = _occupyInfo; + }, + + // + setLoginState(state, { loginState = false, token = '' }){ + console.log(loginState, token); + if(loginState&&token){ + uni.setStorageSync('token', token); + state.isLogin = loginState; + }else{ + uni.removeStorageSync('token', token); + state.isLogin = false; + } + } } \ No newline at end of file diff --git a/src/subpackage/authorization/components/login.vue b/src/subpackage/authorization/components/login.vue new file mode 100644 index 0000000..635810e --- /dev/null +++ b/src/subpackage/authorization/components/login.vue @@ -0,0 +1,190 @@ + + + + + \ No newline at end of file diff --git a/src/subpackage/authorization/js/api.js b/src/subpackage/authorization/js/api.js new file mode 100644 index 0000000..5b8b559 --- /dev/null +++ b/src/subpackage/authorization/js/api.js @@ -0,0 +1,7 @@ +import { ORIGIN } from '@/js/api'; + +export const AUTHOR_API = { + wechatMiniAppLoginAndSync: `${ORIGIN}/assistant/WechatMiniAppGetToken`, // 小程序授权获取token,为空就登录 +} + +export default AUTHOR_API; \ No newline at end of file diff --git a/src/subpackage/authorization/js/server.js b/src/subpackage/authorization/js/server.js new file mode 100644 index 0000000..ac1b071 --- /dev/null +++ b/src/subpackage/authorization/js/server.js @@ -0,0 +1,10 @@ +import { Server } from '@/js/server'; + +class _Server extends Server { + constructor(props){ + super(props) + } +} + + +export default new _Server(); \ No newline at end of file diff --git a/src/subpackage/authorization/pages/index.vue b/src/subpackage/authorization/pages/index.vue new file mode 100644 index 0000000..b3f8694 --- /dev/null +++ b/src/subpackage/authorization/pages/index.vue @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/src/subpackage/authorization/static/images/author_modal.png b/src/subpackage/authorization/static/images/author_modal.png new file mode 100644 index 0000000000000000000000000000000000000000..8e3612314f6f3e6f628de1ea7b9493c670bec96e GIT binary patch literal 14649 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 ZF=3YyEyDvK{{39TP*>Ig)GFFU{U5=)GzI_w literal 0 HcmV?d00001