Commit 93352b538617c04c77534cd03168231adb25ad4e
1 parent
671b5d34
完善手机密码登录和Vuex状态管理
Showing
12 changed files
with
183 additions
and
96 deletions
yudao-ui-app/App.vue
| 1 | 1 | <script> |
| 2 | - export default { | |
| 3 | - onLaunch: function() { | |
| 4 | - console.log('App Launch') | |
| 5 | - }, | |
| 6 | - onShow: function() { | |
| 7 | - console.log('App Show') | |
| 8 | - }, | |
| 9 | - onHide: function() { | |
| 10 | - console.log('App Hide') | |
| 11 | - } | |
| 12 | - } | |
| 2 | +export default { | |
| 3 | + onLaunch: function () { | |
| 4 | + console.log('App Launch') | |
| 5 | + // #ifdef H5 | |
| 6 | + //在页面加载时读取sessionStorage里的状态信息 | |
| 7 | + if (sessionStorage.getItem('store')) { | |
| 8 | + this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem('store')))) | |
| 9 | + } | |
| 10 | + //在页面刷新时将vuex里的信息保存到sessionStorage里 | |
| 11 | + window.addEventListener('beforeunload', () => { | |
| 12 | + sessionStorage.setItem('store', JSON.stringify(this.$store.state)) | |
| 13 | + }) | |
| 14 | + // #endif | |
| 15 | + }, | |
| 16 | + onShow: function () { | |
| 17 | + console.log('App Show') | |
| 18 | + }, | |
| 19 | + onHide: function () { | |
| 20 | + console.log('App Hide') | |
| 21 | + } | |
| 22 | +} | |
| 13 | 23 | </script> |
| 14 | 24 | |
| 15 | 25 | <style lang="scss"> |
| 16 | - /* 引入uView基础样式 */ | |
| 17 | - @import "@/uni_modules/uview-ui/index.scss"; | |
| 18 | - | |
| 19 | - /*每个页面公共scss */ | |
| 20 | - @import "app.scss"; | |
| 26 | +/* 引入uView基础样式 */ | |
| 27 | +@import '@/uni_modules/uview-ui/index.scss'; | |
| 21 | 28 | |
| 29 | +/*每个页面公共scss */ | |
| 30 | +@import 'app.scss'; | |
| 22 | 31 | </style> | ... | ... |
yudao-ui-app/common/api.js
| ... | ... | @@ -2,12 +2,15 @@ const { http } = uni.$u |
| 2 | 2 | |
| 3 | 3 | /* login */ |
| 4 | 4 | //使用手机 + 密码登录 |
| 5 | -export const passwordLogin = params => http.post('/app-api/member/user/login', params) | |
| 5 | +export const passwordLogin = params => http.post('/app-api/member/login', params) | |
| 6 | 6 | //发送手机验证码 |
| 7 | 7 | export const sendSmsCode = params => http.post('/app-api/member/send-sms-code', params) |
| 8 | 8 | //使用手机 + 验证码登录 |
| 9 | 9 | export const smsLogin = params => http.post('/app-api/member/sms-login', params) |
| 10 | 10 | |
| 11 | +//获取用户信息 | |
| 12 | +export const getUserInfo = params => http.get('/app-api/member/user/get', params) | |
| 13 | + | |
| 11 | 14 | /* index */ |
| 12 | 15 | // 获取滚动图数据 |
| 13 | 16 | export const getBannerData = params => http.get('/app-api/index', params) | ... | ... |
yudao-ui-app/common/config.js
yudao-ui-app/common/mixin.js
yudao-ui-app/main.js
| 1 | 1 | import Vue from 'vue' |
| 2 | 2 | import App from './App' |
| 3 | 3 | |
| 4 | +// 引入全局uView | |
| 5 | +import uView from '@/uni_modules/uview-ui' | |
| 6 | + | |
| 4 | 7 | // vuex |
| 5 | 8 | import store from './store' |
| 6 | 9 | |
| 7 | 10 | Vue.config.productionTip = false |
| 8 | 11 | Vue.prototype.$store = store |
| 9 | 12 | |
| 10 | -// 引入全局uView | |
| 11 | -import uView from '@/uni_modules/uview-ui' | |
| 13 | + | |
| 12 | 14 | |
| 13 | 15 | App.mpType = 'app' |
| 14 | 16 | Vue.use(uView) | ... | ... |
yudao-ui-app/pages/cart/cart.vue
| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | <u-empty mode="car" width="500rpx" height="500rpx" icon="/static/images/empty/cart.png"></u-empty> |
| 5 | 5 | </view> |
| 6 | 6 | |
| 7 | - <view class="login-tips-box"> | |
| 7 | + <view v-if="!hasLogin" class="login-tips-box"> | |
| 8 | 8 | <view class="login-tips"> |
| 9 | 9 | <navigator url="/pages/login/login" open-type="navigate" hover-class="none"> |
| 10 | 10 | <text class="login-link">登录</text> |
| ... | ... | @@ -23,7 +23,12 @@ export default { |
| 23 | 23 | } |
| 24 | 24 | }, |
| 25 | 25 | onLoad() {}, |
| 26 | - methods: {} | |
| 26 | + methods: {}, | |
| 27 | + computed: { | |
| 28 | + hasLogin() { | |
| 29 | + return this.$store.getters.hasLogin | |
| 30 | + } | |
| 31 | + } | |
| 27 | 32 | } |
| 28 | 33 | </script> |
| 29 | 34 | ... | ... |
yudao-ui-app/pages/login/login.vue
| ... | ... | @@ -53,15 +53,13 @@ import { passwordLogin, sendSmsCode, smsLogin } from '../../common/api' |
| 53 | 53 | export default { |
| 54 | 54 | data() { |
| 55 | 55 | return { |
| 56 | - //租户ID | |
| 57 | - agent: 1, | |
| 58 | 56 | currentModeIndex: 0, |
| 59 | 57 | loginModeList: ['密码登录', '验证码登录'], |
| 60 | 58 | inputType: 'password', |
| 61 | 59 | codeDisabled: false, |
| 62 | 60 | codeTips: '', |
| 63 | 61 | formData: { |
| 64 | - mobile: '15601691234', | |
| 62 | + mobile: '', | |
| 65 | 63 | password: '', |
| 66 | 64 | code: '' |
| 67 | 65 | }, |
| ... | ... | @@ -131,17 +129,17 @@ export default { |
| 131 | 129 | }) |
| 132 | 130 | |
| 133 | 131 | //scene:1登陆获取验证码场景 |
| 134 | - sendSmsCode({ agent: 1, mobile: mobile, scene: 1 }) | |
| 132 | + sendSmsCode({ mobile: mobile, scene: 1 }) | |
| 135 | 133 | .then(res => { |
| 136 | 134 | //console.log(res) |
| 137 | 135 | uni.hideLoading() |
| 138 | - if (res.data.code === 0) { | |
| 136 | + if (res.code === 0) { | |
| 139 | 137 | // 这里此提示会被this.start()方法中的提示覆盖 |
| 140 | 138 | uni.$u.toast('验证码已发送') |
| 141 | 139 | // 通知验证码组件内部开始倒计时 |
| 142 | 140 | this.$refs.uCode.start() |
| 143 | 141 | } else { |
| 144 | - uni.$u.toast(res.data.msg) | |
| 142 | + uni.$u.toast(res.msg) | |
| 145 | 143 | } |
| 146 | 144 | }) |
| 147 | 145 | .catch(err => { |
| ... | ... | @@ -153,26 +151,30 @@ export default { |
| 153 | 151 | }, |
| 154 | 152 | handleSubmit() { |
| 155 | 153 | this.$refs.form.validate().then(res => { |
| 156 | - uni.$u.toast('登录') | |
| 157 | 154 | if (this.currentModeIndex === 0) { |
| 158 | - passwordLogin({ agent: 1, mobile: this.formData.mobile, password: this.formData.password }) | |
| 159 | - .then(res => { | |
| 160 | - if (res.data.code === 0) { | |
| 161 | - uni.$u.toast('登录成功') | |
| 162 | - // TODO 登录成功,保存toke | |
| 163 | - } else { | |
| 164 | - uni.$u.toast(res.data.msg) | |
| 165 | - // TODO 登录失败 | |
| 166 | - } | |
| 167 | - }) | |
| 168 | - .catch(err => { | |
| 169 | - uni.$u.toast('服务器接口请求异常') | |
| 170 | - }) | |
| 155 | + this.handleLoginPromise(passwordLogin({ mobile: this.formData.mobile, password: this.formData.password })) | |
| 171 | 156 | } else if (this.currentModeIndex === 1) { |
| 172 | - smsLogin({ agent: 1, mobile: this.formData.mobile, code: this.formData.code }) | |
| 157 | + this.handleLoginPromise(smsLogin({ mobile: this.formData.mobile, code: this.formData.code })) | |
| 173 | 158 | } |
| 174 | 159 | }) |
| 175 | 160 | }, |
| 161 | + handleLoginPromise(promise) { | |
| 162 | + promise | |
| 163 | + .then(res => { | |
| 164 | + if (res.code === 0) { | |
| 165 | + this.$store.commit('setToken', res.data) | |
| 166 | + uni.$u.toast('登录成功') | |
| 167 | + setTimeout(() => { | |
| 168 | + this.navigateBack() | |
| 169 | + }, 1000) | |
| 170 | + } else { | |
| 171 | + uni.$u.toast(res.msg) | |
| 172 | + } | |
| 173 | + }) | |
| 174 | + .catch(err => { | |
| 175 | + uni.$u.toast('接口请求失败') | |
| 176 | + }) | |
| 177 | + }, | |
| 176 | 178 | navigateBack() { |
| 177 | 179 | uni.navigateBack() |
| 178 | 180 | } |
| ... | ... | @@ -200,8 +202,6 @@ export default { |
| 200 | 202 | } |
| 201 | 203 | } |
| 202 | 204 | |
| 203 | - | |
| 204 | - | |
| 205 | 205 | .lk-group { |
| 206 | 206 | height: 40rpx; |
| 207 | 207 | margin-top: 40rpx; | ... | ... |
yudao-ui-app/pages/user/user.vue
| ... | ... | @@ -2,8 +2,8 @@ |
| 2 | 2 | <view class="container"> |
| 3 | 3 | <view class="user-header"> |
| 4 | 4 | <view class="user-info" @click="loginOrJump('/pages/profile/profile')"> |
| 5 | - <u-avatar size="80" :src="avatar"></u-avatar> | |
| 6 | - <text class="nick-name">{{ nickName }}</text> | |
| 5 | + <u-avatar size="80" :src="userInfo.avatar"></u-avatar> | |
| 6 | + <text class="nick-name">{{ hasLogin ? userInfo.nickname || '游客' : '点击登录' }}</text> | |
| 7 | 7 | </view> |
| 8 | 8 | </view> |
| 9 | 9 | |
| ... | ... | @@ -19,8 +19,8 @@ |
| 19 | 19 | </view> |
| 20 | 20 | |
| 21 | 21 | <view class="order-status-box"> |
| 22 | - <u-grid :border="false" :col="orderStatusList.length" | |
| 23 | - ><u-grid-item v-for="(item, index) in orderStatusList" :key="index"> | |
| 22 | + <u-grid :border="false" :col="orderStatusList.length"> | |
| 23 | + <u-grid-item v-for="(item, index) in orderStatusList" :key="index"> | |
| 24 | 24 | <u-icon :name="item.icon" :size="32"></u-icon> |
| 25 | 25 | <text class="grid-title">{{ item.title }}</text> |
| 26 | 26 | </u-grid-item> |
| ... | ... | @@ -45,13 +45,12 @@ |
| 45 | 45 | <u-cell class="fun-item" :border="false" icon="gift" title="分销中心" isLink></u-cell> |
| 46 | 46 | <u-cell class="fun-item" :border="false" icon="tags" title="领券中心" isLink></u-cell> |
| 47 | 47 | <u-cell class="fun-item" :border="false" icon="coupon" title="我的优惠券" isLink></u-cell> |
| 48 | - <u-cell class="fun-item" :border="false" icon="map" title="收货地址" @click="loginOrJump('/pages/address/list')" isLink></u-cell> | |
| 48 | + <u-cell class="fun-item" :border="false" icon="map" title="收货地址" @click="loginOrJump('/pages/address/list')" isLink></u-cell> | |
| 49 | 49 | </u-cell-group> |
| 50 | 50 | |
| 51 | - <view class="logout-btn"> | |
| 52 | - <u-button type="error" color="#ea322b" text="确定"></u-button> | |
| 51 | + <view v-if="hasLogin" class="logout-btn"> | |
| 52 | + <u-button type="error" color="#ea322b" text="退出登录" @click="logout"></u-button> | |
| 53 | 53 | </view> |
| 54 | - | |
| 55 | 54 | </view> |
| 56 | 55 | </template> |
| 57 | 56 | |
| ... | ... | @@ -59,8 +58,6 @@ |
| 59 | 58 | export default { |
| 60 | 59 | data() { |
| 61 | 60 | return { |
| 62 | - avatar: '', | |
| 63 | - nickName: '点击登录', | |
| 64 | 61 | orderStatusList: [ |
| 65 | 62 | { icon: 'rmb-circle', title: '待支付' }, |
| 66 | 63 | { icon: 'car', title: '代发货' }, |
| ... | ... | @@ -76,13 +73,34 @@ export default { |
| 76 | 73 | }, |
| 77 | 74 | onLoad() {}, |
| 78 | 75 | methods: { |
| 79 | - loginOrJump(pageUrl){ | |
| 80 | - // TODO 判断是否已经登录逻辑 | |
| 81 | - if (!uni.getStorageSync('token')) { | |
| 76 | + loginOrJump(pageUrl) { | |
| 77 | + if (!this.hasLogin) { | |
| 82 | 78 | uni.$u.route('/pages/login/login') |
| 83 | 79 | } else { |
| 84 | 80 | uni.$u.route(pageUrl) |
| 85 | 81 | } |
| 82 | + }, | |
| 83 | + logout() { | |
| 84 | + uni.showModal({ | |
| 85 | + title: '提示', | |
| 86 | + content: '您确定要退出登录吗', | |
| 87 | + success: res => { | |
| 88 | + if (res.confirm) { | |
| 89 | + console.log('用户点击确定') | |
| 90 | + this.$store.commit('logout') | |
| 91 | + } else if (res.cancel) { | |
| 92 | + console.log('用户点击取消') | |
| 93 | + } | |
| 94 | + } | |
| 95 | + }) | |
| 96 | + } | |
| 97 | + }, | |
| 98 | + computed: { | |
| 99 | + userInfo() { | |
| 100 | + return this.$store.state.userInfo | |
| 101 | + }, | |
| 102 | + hasLogin() { | |
| 103 | + return this.$store.getters.hasLogin | |
| 86 | 104 | } |
| 87 | 105 | } |
| 88 | 106 | } | ... | ... |
yudao-ui-app/store/index.js
| 1 | 1 | import Vue from 'vue' |
| 2 | 2 | import Vuex from 'vuex' |
| 3 | +import { getUserInfo } from '@/common/api' | |
| 3 | 4 | |
| 4 | 5 | Vue.use(Vuex) // vue的插件机制 |
| 5 | 6 | |
| 6 | 7 | // Vuex.Store 构造器选项 |
| 7 | 8 | const store = new Vuex.Store({ |
| 8 | - // 为了不和页面或组件的data中的造成混淆,state中的变量前面建议加上$符号 | |
| 9 | - state: { | |
| 10 | - // 用户信息 | |
| 11 | - $userInfo: { | |
| 12 | - id: '' | |
| 9 | + state: { | |
| 10 | + openExamine: false, // 是否开启审核状态。用于小程序、App 等审核时,关闭部分功能。TODO 芋艿:暂时没找到刷新的地方 | |
| 11 | + token: uni.getStorageSync('token'), // 用户身份 Token | |
| 12 | + userInfo: {}, // 用户基本信息 | |
| 13 | + timerIdent: false // 全局 1s 定时器,只在全局开启一个,所有需要定时执行的任务监听该值即可,无需额外开启 TODO 芋艿:需要看看 | |
| 14 | + }, | |
| 15 | + getters: { | |
| 16 | + hasLogin(state) { | |
| 17 | + return !!state.token | |
| 18 | + } | |
| 19 | + }, | |
| 20 | + mutations: { | |
| 21 | + // 更新 state 的通用方法 | |
| 22 | + setStateAttr(state, param) { | |
| 23 | + if (param instanceof Array) { | |
| 24 | + for (let item of param) { | |
| 25 | + state[item.key] = item.val | |
| 13 | 26 | } |
| 27 | + } else { | |
| 28 | + state[param.key] = param.val | |
| 29 | + } | |
| 14 | 30 | }, |
| 15 | - mutations: { | |
| 16 | - | |
| 17 | - }, | |
| 18 | - actions: { | |
| 31 | + // 更新token | |
| 32 | + setToken(state, data) { | |
| 33 | + // 设置 Token | |
| 34 | + const { token } = data | |
| 35 | + state.token = token | |
| 36 | + uni.setStorageSync('token', token) | |
| 19 | 37 | |
| 38 | + // 加载用户信息 | |
| 39 | + this.dispatch('obtainUserInfo') | |
| 20 | 40 | }, |
| 21 | - getters:{ | |
| 22 | - | |
| 41 | + // 退出登录 | |
| 42 | + logout(state) { | |
| 43 | + // 清空 Token | |
| 44 | + state.token = '' | |
| 45 | + uni.removeStorageSync('token') | |
| 46 | + // 清空用户信息 TODO 芋艿:这里 setTimeout 的原因是,上面可能还有一些 request 请求。后续得优化下 | |
| 47 | + setTimeout(() => { | |
| 48 | + state.userInfo = {} | |
| 49 | + }, 1100) | |
| 50 | + } | |
| 51 | + }, | |
| 52 | + actions: { | |
| 53 | + // 获得用户基本信息 | |
| 54 | + async obtainUserInfo({ state, commit }) { | |
| 55 | + const res = await getUserInfo() | |
| 56 | + commit('setStateAttr', { | |
| 57 | + key: 'userInfo', | |
| 58 | + val: res.data | |
| 59 | + }) | |
| 23 | 60 | } |
| 61 | + } | |
| 24 | 62 | }) |
| 25 | 63 | |
| 26 | 64 | export default store | ... | ... |
yudao-ui-app/util/request/index.js
yudao-ui-app/util/request/requestInterceptors.js
| ... | ... | @@ -2,13 +2,21 @@ |
| 2 | 2 | * 请求拦截 |
| 3 | 3 | * @param {Object} http |
| 4 | 4 | */ |
| 5 | -module.exports = (vm) => { | |
| 6 | - uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作 | |
| 7 | - // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{} | |
| 8 | - config.data = config.data || {} | |
| 9 | - // 可以在此通过vm引用vuex中的变量,具体值在vm.$store.state中 | |
| 10 | - // console.log(vm.$store.state); | |
| 11 | - return config | |
| 12 | - }, (config) => // 可使用async await 做异步操作 | |
| 13 | - Promise.reject(config)) | |
| 5 | +module.exports = vm => { | |
| 6 | + uni.$u.http.interceptors.request.use( | |
| 7 | + config => { | |
| 8 | + // 可使用async await 做异步操作 | |
| 9 | + // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{} | |
| 10 | + config.data = config.data || {} | |
| 11 | + // 可以在此通过vm引用vuex中的变量,具体值在vm.$store.state中 | |
| 12 | + // console.log(vm.$store.state) | |
| 13 | + if (vm.$store.getters.hasLogin) { | |
| 14 | + config.header.authorization = 'Bearer ' + vm.$store.state.token | |
| 15 | + } | |
| 16 | + return config | |
| 17 | + }, | |
| 18 | + ( | |
| 19 | + config // 可使用async await 做异步操作 | |
| 20 | + ) => Promise.reject(config) | |
| 21 | + ) | |
| 14 | 22 | } | ... | ... |
yudao-ui-app/util/request/responseInterceptors.js
| ... | ... | @@ -2,16 +2,16 @@ |
| 2 | 2 | * 响应拦截 |
| 3 | 3 | * @param {Object} http |
| 4 | 4 | */ |
| 5 | -module.exports = (vm) => { | |
| 6 | - uni.$u.http.interceptors.response.use((res) => { | |
| 7 | - /* 对响应成功做点什么 可使用async await 做异步操作*/ | |
| 8 | - const data = res.data | |
| 9 | - /* | |
| 10 | - 可以根据业务情况做相应的处理 | |
| 11 | - */ | |
| 12 | - return res | |
| 13 | - }, (err) => { | |
| 14 | - /* 对响应错误做点什么 (statusCode !== 200)*/ | |
| 15 | - return Promise.reject(err) | |
| 16 | - }) | |
| 17 | -} | |
| 18 | 5 | \ No newline at end of file |
| 6 | +module.exports = vm => { | |
| 7 | + uni.$u.http.interceptors.response.use( | |
| 8 | + res => { | |
| 9 | + //对响应成功做点什么 可使用async await 做异步操作 | |
| 10 | + //可以根据业务情况做相应的处理 | |
| 11 | + return res.data | |
| 12 | + }, | |
| 13 | + err => { | |
| 14 | + //对响应错误做点什么 (statusCode !== 200) | |
| 15 | + return Promise.reject(err) | |
| 16 | + } | |
| 17 | + ) | |
| 18 | +} | ... | ... |