Skip to content

Commit

Permalink
feat(login): impl login process
Browse files Browse the repository at this point in the history
  • Loading branch information
lbwa committed Mar 7, 2020
1 parent f243224 commit ad01f80
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 49 deletions.
32 changes: 32 additions & 0 deletions src/components/BaseToast.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<template>
<v-snackbar
:value="visible"
@input="$emit('update:visible', $event)"
right
color="error"
>
{{ message }}
<v-btn dark text @click="$emit('update:message', '')">
Close
</v-btn>
</v-snackbar>
</template>

<script>
export default {
name: 'BaseSnackbar',
props: {
message: {
type: String,
required: true
},
visible: {
type: Boolean,
required: true
}
}
}
</script>

<style lang="sass" scoped></style>
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ export const RECORD_MAX_VAL = 20
* Used to forbid unauthorized access
*/
export const FORBIDDEN_ROUTE = '/forbidden'

export const QUERY_KEY_FOR_LOGIN_TO = '__from'
2 changes: 1 addition & 1 deletion src/plugins/vuetify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default new Vuetify({
},
themes: {
light: {
primary: '#f34b7d'
secondary: '#282c34' // '#f34b7d'
}
}
},
Expand Down
6 changes: 5 additions & 1 deletion src/router/guards.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NavigationGuard } from 'vue-router'
import store from '@/store'
import { QUERY_KEY_FOR_LOGIN_TO } from '../constants'

const WHITE_LIST = ['Login']

Expand All @@ -9,6 +10,9 @@ export const onLogin: NavigationGuard = function(to, from, next) {
}
return next({
name: 'Login',
replace: true
replace: true,
query: {
[QUERY_KEY_FOR_LOGIN_TO]: to.fullPath
}
})
}
5 changes: 4 additions & 1 deletion src/shared/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const errorLog = console.error
// eslint-disable-next-line @typescript-eslint/no-empty-function
function noop() {}

export const errorLog = __DEV__ ? console.error : noop

export function isDef<T>(val: T): val is NonNullable<T> {
return val !== null && val !== undefined
Expand Down
75 changes: 40 additions & 35 deletions src/store/modules/user.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Module } from 'vuex'
import { RootState } from '..'
import { Ability } from 'v-access'
import { errorLog } from '@/shared/utils'

interface UserState {
token: string
Expand Down Expand Up @@ -37,42 +36,48 @@ const user: Module<UserState, RootState> = {
},

actions: {
async fetchUserInfo(
{ commit },
{ username, password }: Record<string, string>
) {
try {
const { token } = await Promise.resolve({
token:
Math.random()
.toString(16)
.slice(2) +
username +
password
})
commit('setToken', token)
} catch (error) {
errorLog(error)
}
async login({ commit }, { username, password }: Record<string, string>) {
const { token } = await new Promise<{
code: number
[key: string]: unknown
}>((resolve, reject) => {
setTimeout(() => {
if (
(username === 'admin' && password === 'admin') ||
(username === 'user' && password === 'user')
) {
return resolve({
code: 200,
token:
Math.random()
.toString(16)
.slice(2) +
username +
password
})
}
reject({
code: 401,
msg: 'account error'
})
}, 2 * 1000)
})
commit('setToken', token)
},
async fetchUserAbilities({ commit }) {
try {
const { abilities } = await Promise.resolve({
abilities: Array(20)
.fill(null)
.map((_, index) => ({
id: `ability.simulator.${index}`,
name: `ability.simulator.${index}`,
createAt: Date.now() + index
}))
})
commit(
'setUserAbilities',
abilities.map(ability => ability.id)
)
} catch (error) {
errorLog(error)
}
const { abilities } = await Promise.resolve({
abilities: Array(20)
.fill(null)
.map((_, index) => ({
id: `ability.simulator.${index}`,
name: `ability.simulator.${index}`,
createAt: Date.now() + index
}))
})
commit(
'setUserAbilities',
abilities.map(ability => ability.id)
)
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/views/Error/index.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="error">
<div class="view-error">
<div class="error__notify">
<h2 class="error__notify__code">{{ code }}</h2>
<div class="error__notify__message">{{ message }}</div>
Expand All @@ -25,10 +25,11 @@ export default {
</script>

<style lang="sass" scoped>
.error
.view-error
position: relative
height: calc(100vh - 60px)
.error
&__notify
display: flex
position: absolute
Expand Down
82 changes: 73 additions & 9 deletions src/views/Login/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,65 @@
<v-container class="fill-height" fluid>
<v-row align="center" justify="center">
<v-col cols="12" sm="8" md="4">
<v-card class="elevation-3">
<v-toolbar color="primary" dark flat>
<v-card class="elevation-1">
<v-toolbar dark color="secondary">
<v-toolbar-title>
<span class="login__title">Adminize console</span>
</v-toolbar-title>
<v-spacer />
<v-btn text small @click="asPreset('user')">as user</v-btn>
<v-btn text small @click="asPreset('admin')">as admin</v-btn>
</v-toolbar>

<v-card-text>
<v-form>
<v-form ref="form" v-model="isValidForm" @submit="onLogin">
<v-text-field
prepend-icon="person"
label="Username"
name="username"
type="text"
clearable
required
v-model="username"
:rules="usernameRules"
:loading="isLoading"
:disabled="isLoading"
/>
<v-text-field
prepend-icon="lock"
id="password"
label="Password"
name="password"
type="password"
v-model="password"
required
:append-icon="
isShowPassword ? 'md-visibility' : 'md-visibility_off'
isShowPassword ? 'visibility_off' : 'visibility'
"
:type="isShowPassword ? 'text' : 'password'"
:rules="passwordRules"
:loading="isLoading"
:disabled="isLoading"
@click:append="isShowPassword = !isShowPassword"
/>
</v-form>
</v-card-text>

<v-card-actions>
<v-spacer />
<v-btn color="primary" @click="onLogin">Login</v-btn>
<v-btn
:disabled="isLoading"
:loading="isLoading"
@click="onLogin"
color="secondary"
class="px-6"
>Login</v-btn
>
</v-card-actions>

<base-toast
:message.sync="toastMessage"
:visible.sync="toastVisibility"
/>
</v-card>
</v-col>
</v-row>
Expand All @@ -47,21 +70,62 @@
</template>

<script>
import { errorLog } from '../../shared/utils'
import { QUERY_KEY_FOR_LOGIN_TO } from '../../constants'
import BaseToast from '../../components/BaseToast'
const ERR_CODE = {
wrongInfo: 'Oops! Wrong username or password.'
}
export default {
name: 'ViewLogin',
data() {
return {
username: '',
password: '',
isShowPassword: false
usernameRules: [v => !!v || 'Please enter your account username.'],
passwordRules: [v => !!v || 'Please enter your account password.'],
isShowPassword: false,
isValidForm: false,
isLoading: false,
toastVisibility: false,
toastMessage: ''
}
},
methods: {
onLogin() {
console.info(true)
async onLogin() {
this.toggleLoading(true)
if (this.isValidForm || this.$refs.form.validate()) {
try {
await this.$store.dispatch('user/login', {
username: this.username,
password: this.password
})
this.$router.replace(this.$route.query[QUERY_KEY_FOR_LOGIN_TO] || '/')
} catch (error) {
errorLog(error)
if (error.code === 401) {
this.toastVisibility = true
this.toastMessage = ERR_CODE.wrongInfo
}
}
}
this.toggleLoading(false)
},
toggleLoading(state) {
this.isLoading = state
},
asPreset(tag) {
this.username = tag
this.password = tag
}
},
components: {
BaseToast
}
}
</script>
Expand Down

0 comments on commit ad01f80

Please sign in to comment.