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: 208 · Posted: 2024-02-14

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb/LiteNote

扫描下方二维码关注公众号和小程序↓↓↓

扫描下方二维码关注公众号和小程序↓↓↓


Today On History
Browsing Refresh