From 70127592b869f1359b17a4b3e713c70addb16e76 Mon Sep 17 00:00:00 2001 From: qiyue2015 Date: Thu, 25 Apr 2024 16:43:46 +0800 Subject: [PATCH 01/29] feat: Add login page and user auth (#90) --- mock/index.ts | 2 + mock/modules/user.mock.ts | 45 ++++++++++++++++++ src/api/user.ts | 29 ++++++++++++ src/assets/images/default-avatar.svg | 1 + src/components.d.ts | 2 + src/locales/en-US.json | 11 ++++- src/locales/zh-CN.json | 11 ++++- src/pages/login/index.vue | 70 ++++++++++++++++++++++++++++ src/pages/profile/index.vue | 35 +++++++++++++- src/router/index.ts | 19 +++++++- src/stores/index.ts | 4 ++ src/stores/modules/user.ts | 62 ++++++++++++++++++++++++ src/typed-router.d.ts | 1 + src/utils/auth.ts | 19 ++++++++ 14 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 mock/modules/user.mock.ts create mode 100644 src/api/user.ts create mode 100644 src/assets/images/default-avatar.svg create mode 100644 src/pages/login/index.vue create mode 100644 src/stores/modules/user.ts create mode 100644 src/utils/auth.ts diff --git a/mock/index.ts b/mock/index.ts index 81d4952a..b391e4a1 100644 --- a/mock/index.ts +++ b/mock/index.ts @@ -1,5 +1,7 @@ import prose from './modules/prose.mock' +import user from './modules/user.mock' export default { ...prose, + ...user, } diff --git a/mock/modules/user.mock.ts b/mock/modules/user.mock.ts new file mode 100644 index 00000000..98ded722 --- /dev/null +++ b/mock/modules/user.mock.ts @@ -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', + } + }, + }, +]) diff --git a/src/api/user.ts b/src/api/user.ts new file mode 100644 index 00000000..85cf3cba --- /dev/null +++ b/src/api/user.ts @@ -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 { + return request.post('/auth/login', data) +} + +export function logout() { + return request.post('/user/logout') +} + +export function getUserInfo() { + return request('/user/me') +} diff --git a/src/assets/images/default-avatar.svg b/src/assets/images/default-avatar.svg new file mode 100644 index 00000000..947b9b85 --- /dev/null +++ b/src/assets/images/default-avatar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components.d.ts b/src/components.d.ts index 766c0542..b4918d8d 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -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'] diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 2d50d267..2bdad9c4 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -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", @@ -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" } } diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index a9d33c68..cfa4792f 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -7,7 +7,8 @@ "persistPiniaState": "🍍 持久化 Pinia 状态", "404Demo": "🙅 404页 演示", "unocssExample": "🎨 Unocss 示例", - "keepAlive": "🧡 KeepAlive 演示" + "keepAlive": "🧡 KeepAlive 演示", + "login": "🔒 用户登陆" }, "mock": { "fromAsyncData": "来自异步请求的数据", @@ -32,5 +33,13 @@ }, "keepAlive": { "label": "当前组件将会被缓存" + }, + "login": { + "login": "登陆", + "logout": "退出", + "username": "请输入用户名", + "password": "请输入密码", + "pleaseEnterUsername": "请输入用户名", + "pleaseEnterPassword": "请输入密码" } } diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue new file mode 100644 index 00000000..90f4c6a5 --- /dev/null +++ b/src/pages/login/index.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/pages/profile/index.vue b/src/pages/profile/index.vue index 91f0a7c9..aaa9b1e4 100644 --- a/src/pages/profile/index.vue +++ b/src/pages/profile/index.vue @@ -1,4 +1,8 @@ diff --git a/src/router/index.ts b/src/router/index.ts index 04890cc9..c1d900d8 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -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' }) @@ -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) @@ -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(() => { diff --git a/src/stores/index.ts b/src/stores/index.ts index e952ed84..4473ca4f 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -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 diff --git a/src/stores/modules/user.ts b/src/stores/modules/user.ts new file mode 100644 index 00000000..9d9ad869 --- /dev/null +++ b/src/stores/modules/user.ts @@ -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({ ...InitUserInfo }) + + // Set user's information + const setInfo = (partial: Partial) => { + 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 diff --git a/src/typed-router.d.ts b/src/typed-router.d.ts index ac828f85..402b7bc3 100644 --- a/src/typed-router.d.ts +++ b/src/typed-router.d.ts @@ -23,6 +23,7 @@ declare module 'vue-router/auto-routes' { 'charts': RouteRecordInfo<'charts', '/charts', Record, Record>, 'counter': RouteRecordInfo<'counter', '/counter', Record, Record>, 'KeepAlive': RouteRecordInfo<'KeepAlive', '/keepalive', Record, Record>, + 'login': RouteRecordInfo<'login', '/login', Record, Record>, 'mock': RouteRecordInfo<'mock', '/mock', Record, Record>, 'profile': RouteRecordInfo<'profile', '/profile', Record, Record>, 'unocss': RouteRecordInfo<'unocss', '/unocss', Record, Record>, diff --git a/src/utils/auth.ts b/src/utils/auth.ts new file mode 100644 index 00000000..2e97ff51 --- /dev/null +++ b/src/utils/auth.ts @@ -0,0 +1,19 @@ +import { STORAGE_TOKEN_KEY } from '@/stores/mutation-type' + +function isLogin() { + return !!localStorage.getItem(STORAGE_TOKEN_KEY) +} + +function getToken() { + return localStorage.getItem(STORAGE_TOKEN_KEY) +} + +function setToken(token: string) { + localStorage.setItem(STORAGE_TOKEN_KEY, token) +} + +function clearToken() { + localStorage.removeItem(STORAGE_TOKEN_KEY) +} + +export { isLogin, getToken, setToken, clearToken } From d8b8771fbfab876ea9b17600346ca6e555f1d8fc Mon Sep 17 00:00:00 2001 From: CharleeWa <18888351756@163.com> Date: Mon, 3 Jun 2024 18:22:22 +0800 Subject: [PATCH 02/29] refactor: Use SFC `` block for routes (#102) --- src/pages/README.md | 10 +++++----- src/pages/[...all].vue | 16 +++++++++------- src/pages/charts/index.vue | 20 +++++++++++--------- src/pages/counter/index.vue | 20 +++++++++++--------- src/pages/index.vue | 16 +++++++++------- src/pages/keepalive/index.vue | 22 ++++++++++++---------- src/pages/login/index.vue | 18 ++++++++++-------- src/pages/mock/index.vue | 20 +++++++++++--------- src/pages/profile/index.vue | 16 +++++++++------- src/pages/unocss/index.vue | 22 +++++++++++----------- 10 files changed, 98 insertions(+), 82 deletions(-) diff --git a/src/pages/README.md b/src/pages/README.md index 71b17f46..693ac98b 100644 --- a/src/pages/README.md +++ b/src/pages/README.md @@ -1,5 +1,5 @@ -# `definePage` - -We used macro [`definePage()`](https://github.com/posva/unplugin-vue-router?tab=readme-ov-file#extending-existing-routes) to define the route name and meta information for each page, making it easy to control the transition animations for each route. - -我们使用 宏 [`definePage()`](https://github.com/posva/unplugin-vue-router?tab=readme-ov-file#extending-existing-routes) 定义每个页面的路由名称和元信息,可以轻松控制每个路由的过渡动画。 +# SFC `` custom block + +We used SFC [``](https://uvr.esm.is/guide/extending-routes.html#sfc-route-custom-block) custom block to define the route name and meta information for each page, making it easy to control the transition animations for each route. + +我们使用 SFC [``](https://uvr.esm.is/guide/extending-routes.html#sfc-route-custom-block) 自定义块定义每个页面的路由名称和元信息,可以轻松控制每个路由的过渡动画。 diff --git a/src/pages/[...all].vue b/src/pages/[...all].vue index 2355d091..cc336e49 100644 --- a/src/pages/[...all].vue +++ b/src/pages/[...all].vue @@ -1,11 +1,4 @@ @@ -22,3 +12,15 @@ const value = ref(1) + + +{ + "name": "KeepAlive", + "meta": { + "level": 2, + "title": "🧡 KeepAlive", + "i18n": "home.keepAlive", + "keepAlive": true + } +} + diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue index 90f4c6a5..5ba8689d 100644 --- a/src/pages/login/index.vue +++ b/src/pages/login/index.vue @@ -4,14 +4,6 @@ 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() @@ -68,3 +60,13 @@ async function asyncLogin(values: any) { + + +{ + "name": "login", + "meta": { + "level": 2, + "i18n": "home.login" + } +} + diff --git a/src/pages/mock/index.vue b/src/pages/mock/index.vue index dd869f25..3700f883 100644 --- a/src/pages/mock/index.vue +++ b/src/pages/mock/index.vue @@ -1,15 +1,6 @@ - + + +{ + "name": "unocss", + "meta": { + "level": 2, + "title": "🎨 Unocss 示例", + "i18n": "home.unocssExample" + } +} + From 5ccb863fade579c5b0deb5aebb5cbea5020ade92 Mon Sep 17 00:00:00 2001 From: CharleeWa <18888351756@163.com> Date: Tue, 18 Jun 2024 15:43:49 +0800 Subject: [PATCH 03/29] refactor: Remove transition animations (#103) --- src/App.vue | 7 +-- src/components/Container.vue | 16 +------ src/components/NavBar.vue | 6 ++- src/components/TabBar.vue | 4 +- src/pages/[...all].vue | 23 ++++----- src/pages/charts/index.vue | 9 ++-- src/pages/counter/index.vue | 29 ++++++------ src/pages/index.vue | 57 +++++++++++------------ src/pages/keepalive/index.vue | 7 +-- src/pages/login/index.vue | 33 ++++++------- src/pages/mock/index.vue | 35 +++++++------- src/pages/profile/index.vue | 49 +++++++++---------- src/pages/unocss/index.vue | 21 ++++----- src/router/index.ts | 13 +----- src/stores/modules/routeTransitionName.ts | 16 ------- src/styles/app.less | 22 --------- 16 files changed, 127 insertions(+), 220 deletions(-) delete mode 100644 src/stores/modules/routeTransitionName.ts diff --git a/src/App.vue b/src/App.vue index 110f6b5f..0e2118b1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,7 +2,6 @@ import { storeToRefs } from 'pinia' import useAppStore from '@/stores/modules/app' import useRouteCache from '@/stores/modules/routeCache' -import useRouteTransitionNameStore from '@/stores/modules/routeTransitionName' import useAutoThemeSwitcher from '@/hooks/useAutoThemeSwitcher' useHead({ @@ -29,8 +28,6 @@ useHead({ const appStore = useAppStore() const { mode } = storeToRefs(appStore) -const routeTransitionNameStore = useRouteTransitionNameStore() -const { routeTransitionName } = storeToRefs(routeTransitionNameStore) const { initializeThemeSwitcher } = useAutoThemeSwitcher(appStore) const keepAliveRouteNames = computed(() => { @@ -46,11 +43,11 @@ onMounted(() => { - + - + diff --git a/src/components/Container.vue b/src/components/Container.vue index 401237db..6e144cd3 100644 --- a/src/components/Container.vue +++ b/src/components/Container.vue @@ -1,17 +1,5 @@ - - diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index 89dac999..a35bbd14 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -21,10 +21,12 @@ const title = computed(() => { diff --git a/src/components/TabBar.vue b/src/components/TabBar.vue index ccaf02c0..4a5fbb23 100644 --- a/src/components/TabBar.vue +++ b/src/components/TabBar.vue @@ -3,7 +3,7 @@ const { t } = useI18n() const active = ref(0) const route = useRoute() -const display = computed(() => { +const show = computed(() => { if (route.meta.level && route.meta.level !== 2) return true return false @@ -11,7 +11,7 @@ const display = computed(() => {