Vuetify 3 管理系统页面框架
Vuetify Vue axios About 6,507 words框架
Vue 3
+ Vuetify 3
+ Vue Router 4
+ axios
+ Vite
。
main.js
// Styles
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
// Components
import App from './App.vue'
// Composables
import {createApp} from 'vue'
import router from "@/router";
import {createVuetify} from 'vuetify'
const app = createApp(App)
app.use(createVuetify({}))
.use(router)
app.mount('#app')
@/router/index.js
import * as VueRouter from 'vue-router'
import Home from "@/components/Home.vue";
import User from "@/components/User.vue";
import Login from "@/components/Login.vue";
const routes = [
{path: '/login', component: Login, name: 'Login'},
{path: '/', component: Home, name: 'Home', children: [
{path: '/user', component: User},
]
},
]
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes,
})
router.beforeEach((to, from) => {
let token = localStorage.getItem("token");
if (!token && to.name !== 'Login') {
return {
name: 'Login',
}
}
})
export default router
App.vue
<template>
<router-view/>
</template>
<script setup>
</script>
Home.vue
<template>
<v-layout class="rounded rounded-md">
<v-app-bar :elevation="2">
<template v-slot:prepend>
<v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
</template>
<v-app-bar-title class="cursor-default">控制台</v-app-bar-title>
<v-spacer></v-spacer>
<v-menu open-on-hover open-on-click>
<template v-slot:activator="{ props }">
<v-btn v-bind="props">用户名</v-btn>
</template>
<v-list nav>
<v-list-item title="退出登录" value="退出登录" @click="logout"></v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<v-navigation-drawer v-model="drawer">
<v-list nav :opened="open">
<v-list-group value="菜单一">
<template v-slot:activator="{ props }">
<v-list-item
v-bind="props"
title="菜单一"
></v-list-item>
</template>
<v-list-item title="菜单一分类一" value="菜单一分类一" to="/m1-c1"></v-list-item>
<v-list-item title="菜单一分类二" value="菜单一分类二" to="/m1-c2"></v-list-item>
</v-list-group>
<v-list-group value="菜单二">
<template v-slot:activator="{ props }">
<v-list-item
v-bind="props"
title="菜单二"
></v-list-item>
</template>
<v-list-item title="菜单二分类一" value="菜单二分类一" to="/m2-c1"></v-list-item>
<v-list-item title="菜单二分类二" value="菜单二分类二" to="/m2-c2"></v-list-item>
</v-list-group>
</v-list>
</v-navigation-drawer>
<v-main class="d-flex align-center justify-center" style="min-height: 300px;">
<router-view/>
</v-main>
</v-layout>
</template>
<script setup>
import {ref} from "vue";
import {useRouter} from "vue-router";
const drawer = ref(true)
let router = useRouter();
function logout() {
localStorage.removeItem("token");
router.push({name: 'Login'})
}
</script>
Login.vue
<script setup>
import {useRoute, useRouter} from "vue-router";
import {ref} from "vue";
import http from "@/http";
let form = ref(false);
let rules = ref({
required: value => !!value || '必填项',
})
let visible = ref(false);
let loading = ref(false);
const router = useRouter()
const route = useRoute()
function onSubmit() {
loading.value = true;
http.post("/login", {
username: "admin",
password: "admin"
}).then(response => {
if (response.data.code === 0) {
localStorage.setItem("token", response.data.data)
router.push({
name: 'Home',
query: {
...route.query,
},
})
}
}).catch(error => {
console.log(error)
}).finally(() => {
loading.value = false;
});
}
</script>
<template>
<div class="h-screen d-flex align-center justify-center" style="background-color: #f0f0f0">
<v-card
class="pa-12 pb-8"
elevation="8"
max-width="448"
min-width="448"
rounded="lg"
>
<template v-slot:title>
控制台
</template>
<v-form
v-model="form"
@submit.prevent="onSubmit"
>
<div class="text-subtitle-1 text-medium-emphasis">账号</div>
<v-text-field
:rules="[rules.required]"
:readonly="loading"
:clearable="!loading"
density="compact"
placeholder="请输入账号"
prepend-inner-icon="mdi-account-outline"
variant="outlined"
></v-text-field>
<div class="text-subtitle-1 text-medium-emphasis d-flex align-center justify-space-between">
密码
</div>
<v-text-field
:rules="[rules.required]"
:readonly="loading"
:clearable="!loading"
:append-inner-icon="visible ? 'mdi-eye-off' : 'mdi-eye'"
:type="visible ? 'text' : 'password'"
density="compact"
placeholder="请输入密码"
prepend-inner-icon="mdi-lock-outline"
variant="outlined"
@click:append-inner="visible = !visible"
></v-text-field>
<v-btn
:disabled="!form"
:loading="loading"
type="submit"
block
class="mb-8"
color="blue"
size="large"
variant="tonal"
>登录</v-btn>
</v-form>
</v-card>
</div>
</template>
<style scoped>
</style>
@/http/index.js
import axios from "axios";
import router from "@/router";
axios.defaults.baseURL = '/api/console';
axios.interceptors.request.use((config) => {
let token = localStorage.getItem("token");
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
})
axios.interceptors.response.use((response) => {
const { data } = response;
return data;
}, (error) => {
if (error.response.status === 401) {
router.push({name: 'Login'});
return Promise.resolve();
}
return Promise.reject(error);
})
export default axios;
Views: 638 · Posted: 2024-02-14
————        END        ————
Give me a Star, Thanks:)
https://github.com/fendoudebb/LiteNote扫描下方二维码关注公众号和小程序↓↓↓
Loading...