欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Vite+JavaScript+Vue+Yarn實現(xiàn)登錄頁面實戰(zhàn)

 更新時間:2025年07月16日 09:13:28   作者:alden_ygq  
本文主要介紹了Vite+JavaScript+Vue+Yarn實現(xiàn)登錄頁面實戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

本文將實現(xiàn)一個完整的登錄系統(tǒng)前端,包含登錄、注冊、登出和用戶管理功能。后端接口使用Mock.js模擬,不包含實際后端代碼。

項目結(jié)構(gòu)

login-demo/
├── public/
├── src/
│   ├── api/             # 接口定義
│   │   └── auth.js
│   ├── components/      # 組件
│   │   ├── LoginForm.vue
│   │   ├── RegisterForm.vue
│   │   └── UserTable.vue
│   ├── router/          # 路由
│   │   └── index.js
│   ├── store/           # 狀態(tài)管理
│   │   └── auth.js
│   ├── views/           # 頁面視圖
│   │   ├── Dashboard.vue
│   │   ├── Login.vue
│   │   └── Register.vue
│   ├── App.vue
│   ├── main.js
│   └── mock.js          # Mock數(shù)據(jù)
├── .gitignore
├── index.html
├── package.json
├── vite.config.js
└── yarn.lock

完整代碼實現(xiàn)

1. 安裝依賴 (終端執(zhí)行)

yarn add vue-router@4 pinia axios mockjs

2. src/main.js

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import './mock' // 引入Mock數(shù)據(jù)

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')

3. src/mock.js (模擬后端接口)

import Mock from 'mockjs'

// 模擬用戶數(shù)據(jù)
const users = [
  { id: 1, username: 'admin', password: 'admin123', email: 'admin@example.com', role: 'admin' },
  { id: 2, username: 'user1', password: 'user123', email: 'user1@example.com', role: 'user' }
]

// 登錄接口
Mock.mock('/api/login', 'post', (options) => {
  const { username, password } = JSON.parse(options.body)
  const user = users.find(u => u.username === username && u.password === password)
  
  if (user) {
    return Mock.mock({
      code: 200,
      message: '登錄成功',
      data: {
        token: '@guid',
        user: {
          id: user.id,
          username: user.username,
          email: user.email,
          role: user.role
        }
      }
    })
  }
  
  return Mock.mock({
    code: 401,
    message: '用戶名或密碼錯誤'
  })
})

// 登出接口
Mock.mock('/api/logout', 'post', {
  code: 200,
  message: '登出成功'
})

// 注冊接口
Mock.mock('/api/register', 'post', (options) => {
  const data = JSON.parse(options.body)
  const existingUser = users.find(u => u.username === data.username || u.email === data.email)
  
  if (existingUser) {
    return Mock.mock({
      code: 400,
      message: '用戶名或郵箱已存在'
    })
  }
  
  const newUser = {
    id: users.length + 1,
    username: data.username,
    password: data.password,
    email: data.email,
    role: 'user'
  }
  
  users.push(newUser)
  
  return Mock.mock({
    code: 200,
    message: '注冊成功',
    data: {
      user: {
        id: newUser.id,
        username: newUser.username,
        email: newUser.email,
        role: newUser.role
      }
    }
  })
})

// 獲取用戶列表
Mock.mock('/api/users', 'get', () => {
  return Mock.mock({
    code: 200,
    message: '獲取用戶列表成功',
    data: {
      users: users.map(u => ({
        id: u.id,
        username: u.username,
        email: u.email,
        role: u.role
      }))
    }
  })
})

// 刪除用戶
Mock.mock(/\/api\/users\/\d+/, 'delete', (options) => {
  const id = options.url.split('/').pop()
  const index = users.findIndex(u => u.id == id)
  
  if (index !== -1) {
    users.splice(index, 1)
    return Mock.mock({
      code: 200,
      message: '用戶刪除成功'
    })
  }
  
  return Mock.mock({
    code: 404,
    message: '用戶不存在'
  })
})

4. src/api/auth.js (接口封裝)

import axios from 'axios'

const api = axios.create({
  baseURL: '/api',
  timeout: 5000
})

export default {
  // 登錄
  login(credentials) {
    return api.post('/login', credentials)
  },
  
  // 登出
  logout() {
    return api.post('/logout')
  },
  
  // 注冊
  register(userData) {
    return api.post('/register', userData)
  },
  
  // 獲取用戶列表
  getUsers() {
    return api.get('/users')
  },
  
  // 刪除用戶
  deleteUser(id) {
    return api.delete(`/users/${id}`)
  }
}

5. src/store/auth.js (Pinia狀態(tài)管理)

import { defineStore } from 'pinia'
import authApi from '@/api/auth'

export const useAuthStore = defineStore('auth', {
  state: () => ({
    user: JSON.parse(localStorage.getItem('user')) || null,
    token: localStorage.getItem('token') || null,
    isAuthenticated: !!localStorage.getItem('token'),
    users: []
  }),
  
  actions: {
    async login(credentials) {
      try {
        const response = await authApi.login(credentials)
        if (response.data.code === 200) {
          this.user = response.data.data.user
          this.token = response.data.data.token
          this.isAuthenticated = true
          
          // 保存到本地存儲
          localStorage.setItem('user', JSON.stringify(this.user))
          localStorage.setItem('token', this.token)
          
          return true
        }
        return false
      } catch (error) {
        console.error('登錄失敗:', error)
        return false
      }
    },
    
    async logout() {
      try {
        await authApi.logout()
        this.user = null
        this.token = null
        this.isAuthenticated = false
        
        // 清除本地存儲
        localStorage.removeItem('user')
        localStorage.removeItem('token')
        
        return true
      } catch (error) {
        console.error('登出失敗:', error)
        return false
      }
    },
    
    async register(userData) {
      try {
        const response = await authApi.register(userData)
        return response.data.code === 200
      } catch (error) {
        console.error('注冊失敗:', error)
        return false
      }
    },
    
    async fetchUsers() {
      try {
        const response = await authApi.getUsers()
        if (response.data.code === 200) {
          this.users = response.data.data.users
          return true
        }
        return false
      } catch (error) {
        console.error('獲取用戶列表失敗:', error)
        return false
      }
    },
    
    async deleteUser(id) {
      try {
        const response = await authApi.deleteUser(id)
        if (response.data.code === 200) {
          // 重新獲取用戶列表
          await this.fetchUsers()
          return true
        }
        return false
      } catch (error) {
        console.error('刪除用戶失敗:', error)
        return false
      }
    }
  }
})

6. src/router/index.js (路由配置)

import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/store/auth'

const routes = [
  {
    path: '/',
    redirect: '/login'
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/Login.vue'),
    meta: { requiresGuest: true }
  },
  {
    path: '/register',
    name: 'Register',
    component: () => import('@/views/Register.vue'),
    meta: { requiresGuest: true }
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('@/views/Dashboard.vue'),
    meta: { requiresAuth: true }
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

router.beforeEach((to, from, next) => {
  const authStore = useAuthStore()
  
  // 檢查路由是否需要認(rèn)證
  if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    return next({ name: 'Login' })
  }
  
  // 檢查路由是否需要未登錄狀態(tài)
  if (to.meta.requiresGuest && authStore.isAuthenticated) {
    return next({ name: 'Dashboard' })
  }
  
  next()
})

export default router

7. src/views/Login.vue

<template>
  <div class="login-container">
    <div class="login-card">
      <h1>用戶登錄</h1>
      <LoginForm @login="handleLogin" />
      <div class="links">
        <router-link to="/register">注冊新賬號</router-link>
      </div>
    </div>
  </div>
</template>

<script>
import LoginForm from '@/components/LoginForm.vue'
import { useAuthStore } from '@/store/auth'
import { useRouter } from 'vue-router'

export default {
  components: {
    LoginForm
  },
  setup() {
    const authStore = useAuthStore()
    const router = useRouter()
    
    const handleLogin = async (credentials) => {
      const success = await authStore.login(credentials)
      if (success) {
        router.push({ name: 'Dashboard' })
      } else {
        alert('登錄失敗,請檢查用戶名和密碼')
      }
    }
    
    return {
      handleLogin
    }
  }
}
</script>

<style scoped>
.login-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
}

.login-card {
  background: white;
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
  padding: 30px;
  width: 100%;
  max-width: 400px;
  text-align: center;
}

h1 {
  margin-bottom: 24px;
  color: #333;
  font-size: 24px;
}

.links {
  margin-top: 20px;
  font-size: 14px;
}

.links a {
  color: #2575fc;
  text-decoration: none;
}

.links a:hover {
  text-decoration: underline;
}
</style>

8. src/components/LoginForm.vue

<template>
  <form @submit.prevent="submitForm">
    <div class="form-group">
      <label for="username">用戶名</label>
      <input
        type="text"
        id="username"
        v-model="form.username"
        placeholder="請輸入用戶名"
        required
      />
    </div>
    
    <div class="form-group">
      <label for="password">密碼</label>
      <input
        type="password"
        id="password"
        v-model="form.password"
        placeholder="請輸入密碼"
        required
      />
    </div>
    
    <button type="submit" class="login-btn">登錄</button>
  </form>
</template>

<script>
import { ref } from 'vue'

export default {
  emits: ['login'],
  
  setup(props, { emit }) {
    const form = ref({
      username: '',
      password: ''
    })
    
    const submitForm = () => {
      emit('login', {
        username: form.value.username,
        password: form.value.password
      })
    }
    
    return {
      form,
      submitForm
    }
  }
}
</script>

<style scoped>
.form-group {
  margin-bottom: 20px;
  text-align: left;
}

label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
  color: #555;
}

input {
  width: 100%;
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 5px;
  font-size: 16px;
  transition: border-color 0.3s;
}

input:focus {
  border-color: #2575fc;
  outline: none;
}

.login-btn {
  width: 100%;
  padding: 12px;
  background: #2575fc;
  color: white;
  border: none;
  border-radius: 5px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.3s;
}

.login-btn:hover {
  background: #1c65e0;
}
</style>

9. src/views/Register.vue

<template>
  <div class="register-container">
    <div class="register-card">
      <h1>用戶注冊</h1>
      <RegisterForm @register="handleRegister" />
      <div class="links">
        <router-link to="/login">已有賬號?立即登錄</router-link>
      </div>
    </div>
  </div>
</template>

<script>
import RegisterForm from '@/components/RegisterForm.vue'
import { useAuthStore } from '@/store/auth'
import { useRouter } from 'vue-router'

export default {
  components: {
    RegisterForm
  },
  setup() {
    const authStore = useAuthStore()
    const router = useRouter()
    
    const handleRegister = async (userData) => {
      const success = await authStore.register(userData)
      if (success) {
        alert('注冊成功!請登錄')
        router.push({ name: 'Login' })
      } else {
        alert('注冊失敗,用戶名或郵箱可能已被使用')
      }
    }
    
    return {
      handleRegister
    }
  }
}
</script>

<style scoped>
.register-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: linear-gradient(135deg, #ff6b6b 0%, #ffa502 100%);
}

.register-card {
  background: white;
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
  padding: 30px;
  width: 100%;
  max-width: 400px;
  text-align: center;
}

h1 {
  margin-bottom: 24px;
  color: #333;
  font-size: 24px;
}

.links {
  margin-top: 20px;
  font-size: 14px;
}

.links a {
  color: #ff6b6b;
  text-decoration: none;
}

.links a:hover {
  text-decoration: underline;
}
</style>

10. src/components/RegisterForm.vue

<template>
  <form @submit.prevent="submitForm">
    <div class="form-group">
      <label for="username">用戶名</label>
      <input
        type="text"
        id="username"
        v-model="form.username"
        placeholder="請輸入用戶名"
        required
      />
    </div>
    
    <div class="form-group">
      <label for="email">郵箱</label>
      <input
        type="email"
        id="email"
        v-model="form.email"
        placeholder="請輸入郵箱"
        required
      />
    </div>
    
    <div class="form-group">
      <label for="password">密碼</label>
      <input
        type="password"
        id="password"
        v-model="form.password"
        placeholder="請輸入密碼"
        required
      />
    </div>
    
    <div class="form-group">
      <label for="confirmPassword">確認(rèn)密碼</label>
      <input
        type="password"
        id="confirmPassword"
        v-model="form.confirmPassword"
        placeholder="請再次輸入密碼"
        required
      />
    </div>
    
    <button type="submit" class="register-btn">注冊</button>
  </form>
</template>

<script>
import { ref } from 'vue'

export default {
  emits: ['register'],
  
  setup(props, { emit }) {
    const form = ref({
      username: '',
      email: '',
      password: '',
      confirmPassword: ''
    })
    
    const submitForm = () => {
      if (form.value.password !== form.value.confirmPassword) {
        alert('兩次輸入的密碼不一致')
        return
      }
      
      emit('register', {
        username: form.value.username,
        email: form.value.email,
        password: form.value.password
      })
    }
    
    return {
      form,
      submitForm
    }
  }
}
</script>

<style scoped>
.form-group {
  margin-bottom: 20px;
  text-align: left;
}

label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
  color: #555;
}

input {
  width: 100%;
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 5px;
  font-size: 16px;
  transition: border-color 0.3s;
}

input:focus {
  border-color: #ff6b6b;
  outline: none;
}

.register-btn {
  width: 100%;
  padding: 12px;
  background: #ff6b6b;
  color: white;
  border: none;
  border-radius: 5px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.3s;
}

.register-btn:hover {
  background: #ff5252;
}
</style>

11. src/views/Dashboard.vue

<template>
  <div class="dashboard-container">
    <header class="header">
      <div class="user-info">
        <span>歡迎, {{ user.username }} ({{ user.role }})</span>
      </div>
      <button class="logout-btn" @click="handleLogout">登出</button>
    </header>
    
    <main class="main-content">
      <h1>用戶管理系統(tǒng)</h1>
      <div class="actions">
        <button class="refresh-btn" @click="fetchUsers">刷新用戶列表</button>
      </div>
      <UserTable :users="users" @delete-user="deleteUser" />
    </main>
  </div>
</template>

<script>
import UserTable from '@/components/UserTable.vue'
import { useAuthStore } from '@/store/auth'
import { useRouter } from 'vue-router'
import { computed, onMounted, ref } from 'vue'

export default {
  components: {
    UserTable
  },
  
  setup() {
    const authStore = useAuthStore()
    const router = useRouter()
    const users = ref([])
    
    const user = computed(() => authStore.user)
    
    const fetchUsers = async () => {
      await authStore.fetchUsers()
      users.value = authStore.users
    }
    
    const deleteUser = async (id) => {
      const confirmDelete = confirm('確定要刪除這個用戶嗎?')
      if (confirmDelete) {
        const success = await authStore.deleteUser(id)
        if (success) {
          await fetchUsers()
        }
      }
    }
    
    const handleLogout = async () => {
      await authStore.logout()
      router.push({ name: 'Login' })
    }
    
    onMounted(() => {
      fetchUsers()
    })
    
    return {
      user,
      users,
      fetchUsers,
      deleteUser,
      handleLogout
    }
  }
}
</script>

<style scoped>
.dashboard-container {
  min-height: 100vh;
  background-color: #f5f7fa;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 40px;
  background-color: white;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.user-info {
  font-weight: 500;
  color: #333;
}

.logout-btn {
  padding: 8px 16px;
  background-color: #ff4757;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.logout-btn:hover {
  background-color: #ff2e43;
}

.main-content {
  max-width: 1200px;
  margin: 30px auto;
  padding: 0 20px;
}

h1 {
  text-align: center;
  margin-bottom: 30px;
  color: #2c3e50;
}

.actions {
  margin-bottom: 20px;
  display: flex;
  justify-content: flex-end;
}

.refresh-btn {
  padding: 8px 16px;
  background-color: #3498db;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.refresh-btn:hover {
  background-color: #2980b9;
}
</style>

12. src/components/UserTable.vue

<template>
  <div class="user-table">
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>用戶名</th>
          <th>郵箱</th>
          <th>角色</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="user in users" :key="user.id">
          <td>{{ user.id }}</td>
          <td>{{ user.username }}</td>
          <td>{{ user.email }}</td>
          <td>{{ user.role }}</td>
          <td>
            <button 
              class="delete-btn" 
              @click="$emit('delete-user', user.id)"
              :disabled="user.id === currentUserId"
            >
              刪除
            </button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
import { computed } from 'vue'
import { useAuthStore } from '@/store/auth'

export default {
  props: {
    users: {
      type: Array,
      required: true
    }
  },
  
  emits: ['delete-user'],
  
  setup() {
    const authStore = useAuthStore()
    const currentUserId = computed(() => authStore.user?.id || null)
    
    return {
      currentUserId
    }
  }
}
</script>

<style scoped>
.user-table {
  overflow-x: auto;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

table {
  width: 100%;
  border-collapse: collapse;
}

th, td {
  padding: 16px;
  text-align: left;
  border-bottom: 1px solid #eee;
}

th {
  background-color: #f8f9fa;
  font-weight: 600;
  color: #495057;
}

tbody tr:hover {
  background-color: #f8f9fa;
}

.delete-btn {
  padding: 6px 12px;
  background-color: #ff6b6b;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.delete-btn:hover:not(:disabled) {
  background-color: #ff5252;
}

.delete-btn:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}
</style>

13. src/App.vue

<template>
  <router-view />
</template>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  line-height: 1.6;
  color: #333;
  background-color: #f8f9fa;
}

#app {
  min-height: 100vh;
}
</style>

項目啟動步驟

1)創(chuàng)建項目

yarn create vite login-demo --template vue
cd login-demo

2)安裝依賴

yarn add vue-router@4 pinia axios mockjs

3)創(chuàng)建項目結(jié)構(gòu)

創(chuàng)建上述所有文件和目錄

4)啟動開發(fā)服務(wù)器

yarn dev

5)訪問應(yīng)用

打開瀏覽器訪問 http://localhost:5173

功能說明

1)登錄功能

  • 使用用戶名和密碼登錄
  • 模擬后端驗證
  • 登錄成功后跳轉(zhuǎn)到儀表盤

2)注冊功能

  • 創(chuàng)建新用戶賬號
  • 驗證用戶名和郵箱唯一性
  • 注冊成功后跳轉(zhuǎn)到登錄頁面

3)登出功能

  • 清除用戶會話
  • 重定向到登錄頁面

4)用戶管理

  • 查看所有用戶列表
  • 刪除用戶(不能刪除當(dāng)前登錄用戶)
  • 刷新用戶列表

接口定義

  • POST /api/login - 用戶登錄
  • POST /api/logout - 用戶登出
  • POST /api/register - 用戶注冊
  • GET /api/users - 獲取用戶列表
  • DELETE /api/users/:id - 刪除用戶

這個登錄系統(tǒng)前端包含了完整的用戶認(rèn)證流程和簡單的用戶管理功能,使用了Vite作為構(gòu)建工具,Vue3作為前端框架,Pinia進(jìn)行狀態(tài)管理,Vue Router處理路由,Mock.js模擬后端接口。

到此這篇關(guān)于詳解Vite+JavaScript+Vue+Yarn實現(xiàn)登錄頁面實戰(zhàn)的文章就介紹到這了,更多相關(guān)Vue  Yarn登錄頁內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論