diff --git a/.editorconfig b/.editorconfig index 3b510aa..8065bf2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ [*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}] charset = utf-8 -indent_size = 2 -indent_style = space +indent_size = 4 +indent_style = tab insert_final_newline = true trim_trailing_whitespace = true end_of_line = lf diff --git a/.prettierrc.json b/.prettierrc.json index 29a2402..91e0224 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,5 +2,7 @@ "$schema": "https://json.schemastore.org/prettierrc", "semi": false, "singleQuote": true, - "printWidth": 100 -} + "printWidth": 260, + "useTabs": true, + "tabWidth": 4 +} \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index 6ec9f60..ffd3b71 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,23 @@ - + diff --git a/src/config/index.js b/src/config/index.js new file mode 100644 index 0000000..56c967e --- /dev/null +++ b/src/config/index.js @@ -0,0 +1,3 @@ +export default { + app_title: 'vueadmin' +} diff --git a/src/layouts/components/Header.vue b/src/layouts/components/Header.vue new file mode 100644 index 0000000..69252bd --- /dev/null +++ b/src/layouts/components/Header.vue @@ -0,0 +1,160 @@ + + + + + diff --git a/src/layouts/components/LayoutSwitcher.vue b/src/layouts/components/LayoutSwitcher.vue new file mode 100644 index 0000000..83d3f1f --- /dev/null +++ b/src/layouts/components/LayoutSwitcher.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/src/layouts/components/Sidebar.vue b/src/layouts/components/Sidebar.vue new file mode 100644 index 0000000..d554aaa --- /dev/null +++ b/src/layouts/components/Sidebar.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/src/layouts/index.vue b/src/layouts/index.vue new file mode 100644 index 0000000..43c03e5 --- /dev/null +++ b/src/layouts/index.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/src/main.js b/src/main.js index fda1e6e..c934821 100644 --- a/src/main.js +++ b/src/main.js @@ -1,12 +1,12 @@ import { createApp } from 'vue' -import { createPinia } from 'pinia' import App from './App.vue' import router from './router' +import pinia from './stores' const app = createApp(App) -app.use(createPinia()) app.use(router) +app.use(pinia) app.mount('#app') diff --git a/src/pages/home/Dashboard.vue b/src/pages/home/Dashboard.vue new file mode 100644 index 0000000..5f6c919 --- /dev/null +++ b/src/pages/home/Dashboard.vue @@ -0,0 +1,328 @@ + + + + + diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue new file mode 100644 index 0000000..5160d2c --- /dev/null +++ b/src/pages/login/index.vue @@ -0,0 +1,383 @@ + + + + + diff --git a/src/router/index.js b/src/router/index.js index e1eab52..0df551a 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,8 +1,57 @@ import { createRouter, createWebHistory } from 'vue-router' +import { useUserStore } from '@/stores/modules/user' +import Layout from '@/layouts/index.vue' + +const routes = [ + { + path: '/login', + name: 'Login', + component: () => import('@/pages/login/index.vue'), + meta: { requiresAuth: false }, + }, + { + path: '/', + name: 'Dashboard', + component: Layout, + redirect: '/home', + meta: { requiresAuth: true }, + children: [ + { + path: '/home', + name: 'Home', + component: () => import('@/pages/home/Dashboard.vue'), + meta: { requiresAuth: true }, + }, + ], + }, +] const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), - routes: [], + history: createWebHistory(import.meta.env.BASE_URL), + routes: routes, +}) + +// 路由拦截器 - 全局前置守卫 +router.beforeEach((to, from, next) => { + const userStore = useUserStore() + const isLoggedIn = userStore.isLoggedIn() + + // 检查路由是否需要认证 + const requiresAuth = to.matched.some((record) => record.meta.requiresAuth !== false) + + if (requiresAuth && !isLoggedIn) { + // 需要认证但未登录,重定向到登录页 + next({ + path: '/login', + query: { redirect: to.fullPath }, // 保存目标路径,登录后可以跳转回去 + }) + } else if (to.path === '/login' && isLoggedIn) { + // 已登录但访问登录页,重定向到首页 + next({ path: '/' }) + } else { + // 其他情况正常跳转 + next() + } }) export default router diff --git a/src/stores/counter.js b/src/stores/counter.js deleted file mode 100644 index b6757ba..0000000 --- a/src/stores/counter.js +++ /dev/null @@ -1,12 +0,0 @@ -import { ref, computed } from 'vue' -import { defineStore } from 'pinia' - -export const useCounterStore = defineStore('counter', () => { - const count = ref(0) - const doubleCount = computed(() => count.value * 2) - function increment() { - count.value++ - } - - return { count, doubleCount, increment } -}) diff --git a/src/stores/index.js b/src/stores/index.js new file mode 100644 index 0000000..8a05980 --- /dev/null +++ b/src/stores/index.js @@ -0,0 +1,5 @@ +import { createPinia } from 'pinia' + +const pinia = createPinia() + +export default pinia diff --git a/src/stores/modules/layout.js b/src/stores/modules/layout.js new file mode 100644 index 0000000..dd18eee --- /dev/null +++ b/src/stores/modules/layout.js @@ -0,0 +1,27 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useLayoutStore = defineStore('layout', () => { + // 布局模式:'sidebar', 'top-nav', 'sidebar-top', 'classic' + const layoutMode = ref('sidebar') + + // 侧边栏折叠状态 + const sidebarCollapsed = ref(false) + + // 切换侧边栏折叠 + const toggleSidebar = () => { + sidebarCollapsed.value = !sidebarCollapsed.value + } + + // 设置布局模式 + const setLayoutMode = (mode) => { + layoutMode.value = mode + } + + return { + layoutMode, + sidebarCollapsed, + toggleSidebar, + setLayoutMode, + } +}) diff --git a/src/stores/modules/user.js b/src/stores/modules/user.js new file mode 100644 index 0000000..5cf583f --- /dev/null +++ b/src/stores/modules/user.js @@ -0,0 +1,41 @@ +import { ref } from 'vue' +import { defineStore } from 'pinia' + +export const useUserStore = defineStore('user', () => { + const token = ref(localStorage.getItem('token') || '') + const userInfo = ref(JSON.parse(localStorage.getItem('userInfo') || 'null')) + + // 设置 token + function setToken(newToken) { + token.value = newToken + localStorage.setItem('token', newToken) + } + + // 设置用户信息 + function setUserInfo(info) { + userInfo.value = info + localStorage.setItem('userInfo', JSON.stringify(info)) + } + + // 登出 + function logout() { + token.value = '' + userInfo.value = null + localStorage.removeItem('token') + localStorage.removeItem('userInfo') + } + + // 检查是否已登录 + function isLoggedIn() { + return !!token.value + } + + return { + token, + userInfo, + setToken, + setUserInfo, + logout, + isLoggedIn, + } +})