Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions mock/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import prose from './modules/prose.mock'
import user from './modules/user.mock'

export default {
...prose,
...user,
}
45 changes: 45 additions & 0 deletions mock/modules/user.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { defineMock } from 'vite-plugin-mock-dev-server'
import proses from '../data'

export default defineMock([
{
url: '/api/auth/login',
delay: 500,
body: () => {
return {
code: 0,
data: {
token: 'admin',
},
msg: 'success',
}
},
},
{
url: '/api/user/me',
delay: 100,
body: () => {
const { prose } = proses.value[Math.floor(Math.random() * 8)]
return {
code: 0,
data: {
uid: 1,
name: 'admin',
avatar: 'https://iconfont.alicdn.com/p/user/eZQFvSX6g8f1/f0d9fd95-a5f0-474d-98b0-d51e8450f2cf.png',
prose,
},
msg: 'success',
}
},
},
{
url: '/api/user/logout',
delay: 500,
body: () => {
return {
code: 0,
msg: 'success',
}
},
},
])
29 changes: 29 additions & 0 deletions src/api/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import request from '@/utils/request'

export interface LoginData {
username: string
password: string
}

export interface LoginRes {
token: string
}

export interface UserState {
uid?: number
name?: string
avatar?: string
prose?: string
}

export function login(data: LoginData): Promise<any> {
return request.post<LoginRes>('/auth/login', data)
}

export function logout() {
return request.post('/user/logout')
}

export function getUserInfo() {
return request<UserState>('/user/me')
}
1 change: 1 addition & 0 deletions src/assets/images/default-avatar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ declare module 'vue' {
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
VanEmpty: typeof import('vant/es')['Empty']
VanField: typeof import('vant/es')['Field']
VanForm: typeof import('vant/es')['Form']
VanIcon: typeof import('vant/es')['Icon']
VanImage: typeof import('vant/es')['Image']
VanNavBar: typeof import('vant/es')['NavBar']
VanPicker: typeof import('vant/es')['Picker']
VanPopup: typeof import('vant/es')['Popup']
Expand Down
11 changes: 10 additions & 1 deletion src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"echartsDemo": "📊 Echarts Demo",
"persistPiniaState": "🍍 Persistent Pinia state",
"unocssExample": "🎨 Unocss example",
"keepAlive": "🧡 KeepAlive Demo"
"keepAlive": "🧡 KeepAlive Demo",
"login": "🔒 Login"
},
"mock": {
"fromAsyncData": "Data from asynchronous requests",
Expand All @@ -32,5 +33,13 @@
},
"keepAlive": {
"label": "The current component will be cached"
},
"login": {
"login": "Sign In",
"logout": "Sign Out",
"username": "Username",
"password": "Password",
"pleaseEnterUsername": "Please enter username",
"pleaseEnterPassword": "Please enter password"
}
}
11 changes: 10 additions & 1 deletion src/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"persistPiniaState": "🍍 持久化 Pinia 状态",
"404Demo": "🙅 404页 演示",
"unocssExample": "🎨 Unocss 示例",
"keepAlive": "🧡 KeepAlive 演示"
"keepAlive": "🧡 KeepAlive 演示",
"login": "🔒 用户登陆"
},
"mock": {
"fromAsyncData": "来自异步请求的数据",
Expand All @@ -32,5 +33,13 @@
},
"keepAlive": {
"label": "当前组件将会被缓存"
},
"login": {
"login": "登陆",
"logout": "退出",
"username": "请输入用户名",
"password": "请输入密码",
"pleaseEnterUsername": "请输入用户名",
"pleaseEnterPassword": "请输入密码"
}
}
70 changes: 70 additions & 0 deletions src/pages/login/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores'

import defaultAvatar from '@/assets/images/default-avatar.svg'

definePage({
name: 'login',
meta: {
level: 2,
i18n: 'home.login',
},
})

const { t } = useI18n()
const router = useRouter()
const userStore = useUserStore()

const loading = ref(false)
const postData = reactive({
username: '',
password: '',
})
const rules = reactive({
username: [
{ required: true, message: t('login.pleaseEnterUsername') },
],
password: [
{ required: true, message: t('login.pleaseEnterPassword') },
],
})

async function asyncLogin(values: any) {
try {
loading.value = true
await userStore.login({ ...postData, ...values })
const { redirect, ...othersQuery } = router.currentRoute.value.query
router.push({
name: (redirect as string) || 'home',
query: {
...othersQuery,
},
})
}
finally {
loading.value = false
}
}
</script>

<template>
<Container :padding-x="0">
<div class="m-x-a w-7xl text-center">
<div class="mb-32 mt-64">
<van-image :src="defaultAvatar" round class="h-64 w-64" />
</div>
<van-form :model="postData" :rules="rules" @submit="asyncLogin">
<van-cell-group inset>
<van-field v-model="postData.username" :rules="rules.username" name="username" :placeholder="t('login.username')" left-icon="contact" />
<van-field v-model="postData.password" :rules="rules.password" name="password" :placeholder="t('login.password')" left-icon="lock" type="password" />
</van-cell-group>
<div class="m-16">
<van-button :loading="loading" round block type="primary" native-type="submit">
{{ t('login.logout') }}
</van-button>
</div>
</van-form>
</div>
</Container>
</template>
35 changes: 34 additions & 1 deletion src/pages/profile/index.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<script setup lang="ts">
import router from '@/router'
import { useUserStore } from '@/stores'
import defaultAvatar from '@/assets/images/default-avatar.svg'

definePage({
name: 'profile',
meta: {
Expand All @@ -7,12 +11,41 @@ definePage({
})

const { t } = useI18n()
const userStore = useUserStore()
const userInfo = computed(() => userStore.userInfo)

function goLogin() {
router.push({ name: 'login', query: { redirect: 'profile' } })
}

function logout() {
userStore.logout()
router.push({ name: 'login', query: { redirect: 'profile' } })
}
</script>

<template>
<Container>
<div mx-auto mb-60 pt-15 text-center text-16 text-dark dark:text-white>
{{ t('profile.placeholder') }}
<template v-if="userInfo.uid">
<div class="bg mb-32 mt-64">
<van-image :src="userInfo.avatar || defaultAvatar" round class="h-64 w-64 border" />
</div>
<div class="mb-64">
{{ userInfo.name }}
</div>
<div class="mb-32">
{{ userInfo.prose }}
</div>
<van-button class="w-xl" type="primary" @click="logout()">
{{ t('login.logout') }}
</van-button>
</template>
<template v-else>
<van-button class="w-xl" type="primary" @click="goLogin()">
{{ t('login.login') }}
</van-button>
</template>
</div>
</Container>
</template>
19 changes: 17 additions & 2 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'nprogress/nprogress.css'
import type { EnhancedRouteLocation } from './types'
import useRouteTransitionNameStore from '@/stores/modules/routeTransitionName'
import useRouteCacheStore from '@/stores/modules/routeCache'
import { useUserStore } from '@/stores'
import { isLogin } from '@/utils/auth'

NProgress.configure({ showSpinner: true, parent: '#app' })

Expand All @@ -14,11 +16,12 @@ const router = createRouter({
extendRoutes: routes => routes,
})

router.beforeEach((to: EnhancedRouteLocation, from, next) => {
router.beforeEach(async (to: EnhancedRouteLocation, from, next) => {
NProgress.start()

const routeCacheStore = useRouteCacheStore()
const routeTransitionNameStore = useRouteTransitionNameStore()
const userStore = useUserStore()

// Route cache
routeCacheStore.addRoute(to)
Expand All @@ -32,7 +35,19 @@ router.beforeEach((to: EnhancedRouteLocation, from, next) => {
else
routeTransitionNameStore.setName('')

next()
if (isLogin()) {
if (!userStore.userInfo?.uid) {
await userStore.info()
next()
}

else {
next()
}
}
else {
next()
}
})

router.afterEach(() => {
Expand Down
4 changes: 4 additions & 0 deletions src/stores/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

import useAppStore from './modules/app'
import useUserStore from './modules/user'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

export { useAppStore, useUserStore }
export default pinia
62 changes: 62 additions & 0 deletions src/stores/modules/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { defineStore } from 'pinia'
import type { LoginData, UserState } from '@/api/user'
import { getUserInfo, login as userLogin, logout as userLogout } from '@/api/user'
import { clearToken, setToken } from '@/utils/auth'

const InitUserInfo = {
uid: 0,
nickname: '',
avatar: '',
}

export const useUserStore = defineStore('user', () => {
const userInfo = ref<UserState>({ ...InitUserInfo })

// Set user's information
const setInfo = (partial: Partial<UserState>) => {
userInfo.value = { ...partial }
}

const login = async (loginForm: LoginData) => {
try {
const { data } = await userLogin(loginForm)
setToken(data.token)
}
catch (error) {
clearToken()
throw error
}
}

const info = async () => {
try {
const { data } = await getUserInfo()
setInfo(data)
}
catch (error) {
clearToken()
throw error
}
}

const logout = async () => {
try {
await userLogout()
}
finally {
clearToken()
setInfo({ ...InitUserInfo })
}
}

return {
userInfo,
info,
login,
logout,
}
}, {
persist: true,
})

export default useUserStore
1 change: 1 addition & 0 deletions src/typed-router.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ declare module 'vue-router/auto-routes' {
'charts': RouteRecordInfo<'charts', '/charts', Record<never, never>, Record<never, never>>,
'counter': RouteRecordInfo<'counter', '/counter', Record<never, never>, Record<never, never>>,
'KeepAlive': RouteRecordInfo<'KeepAlive', '/keepalive', Record<never, never>, Record<never, never>>,
'login': RouteRecordInfo<'login', '/login', Record<never, never>, Record<never, never>>,
'mock': RouteRecordInfo<'mock', '/mock', Record<never, never>, Record<never, never>>,
'profile': RouteRecordInfo<'profile', '/profile', Record<never, never>, Record<never, never>>,
'unocss': RouteRecordInfo<'unocss', '/unocss', Record<never, never>, Record<never, never>>,
Expand Down
Loading