async,await使用实例(一天一个问题21)

本文介绍了一种使用异步请求获取数据的方法,并通过一个具体示例展示了如何在不阻塞外部操作的情况下进行内部同步执行流程。此过程涉及前端开发中常见的数据获取场景。
draftDirectSubmit = async (verUuid,data) => {//内部包含有异步;内部同步执行;外部不阻碍
      var params = {}
      param  = await this.getData(verUuid)
      console.log("params返回数据后执行")
}
getData = (url) => {
    return new Promise((resolve)=>{
      Util.ajaxmode(`${url}`, 'GET', '').then((response) => {
          if (response) {
            resolve(response)
          }
      });
    })
  }

博客地址

<think>我们使用Vue3和Element Plus来构建一个登录页面,并实现与Django DRF后端的交互。以下是步骤: 1. 安装Element Plus 2. 创建登录页面组件 3. 使用Element Plus的表单组件 4. 处理登录逻辑,包括发送请求、保存token 5. 设置axios拦截器(可选,用于自动携带token) 注意:我们将使用axios发送请求,所以需要安装axios。 步骤: ### 1. 安装Element Plus和axios 在项目目录下运行: ```bash npm install element-plus axios ``` ### 2. 在main.js中引入Element Plus ```javascript import { createApp } from 'vue' import App from './App.vue' import router from './router' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' const app = createApp(App) app.use(router) app.use(ElementPlus) app.mount('#app') ``` ### 3. 创建登录页面组件(Login.vue) ```vue <template> <div class="login-container"> <el-card class="login-card"> <h2 class="login-title">系统登录</h2> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" @keyup.enter="handleLogin" > <el-form-item prop="username"> <el-input v-model="loginForm.username" placeholder="请输入用户名" prefix-icon="el-icon-user" ></el-input> </el-form-item> <el-form-item prop="password"> <el-input v-model="loginForm.password" placeholder="请输入密码" prefix-icon="el-icon-lock" show-password ></el-input> </el-form-item> <el-form-item> <el-button type="primary" style="width:100%;" :loading="loading" @click="handleLogin" >登录</el-button> </el-form-item> </el-form> </el-card> </div> </template> <script setup> import { ref } from 'vue' import { useRouter } from 'vue-router' import axios from 'axios' import { ElMessage } from 'element-plus' const router = useRouter() // 表单数据 const loginForm = ref({ username: '', password: '' }) // 表单验证规则 const loginRules = ref({ username: [ { required: true, message: '请输入用户名', trigger: 'blur' } ], password: [ { required: true, message: '请输入密码', trigger: 'blur' } ] }) // 加载状态 const loading = ref(false) const loginFormRef = ref(null) // 登录方法 const handleLogin = () => { loginFormRef.value.validate(valid => { if (!valid) { return false } loading.value = true // 发送登录请求 axios.post('http://your-django-backend/api/login/', loginForm.value) .then(response => { // 登录成功 loading.value = false // 保存token到localStorage localStorage.setItem('authToken', response.data.token) // 设置axios的全局header axios.defaults.headers.common['Authorization'] = `Token ${response.data.token}` // 跳转到首页 router.push('/') }) .catch(error => { loading.value = false ElMessage.error('登录失败: ' + (error.response?.data?.detail || '未知错误')) }) }) } </script> <style scoped> .login-container { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f5f5f5; } .login-card { width: 400px; } .login-title { text-align: center; margin-bottom: 20px; } </style> ``` ### 4. 配置路由(router/index.js) ```javascript import { createRouter, createWebHistory } from 'vue-router' import Login from '../views/Login.vue' const routes = [ { path: '/login', name: 'Login', component: Login }, // 其他路由... ] const router = createRouter({ history: createWebHistory(), routes }) export default router ``` ### 5. 在App.vue中设置路由视图 ```vue <template> <router-view /> </template> ``` ### 6. 设置axios拦截器(可选,用于处理token过期等) 在main.js或者单独创建一个axios实例,并设置请求拦截器和响应拦截器。 例如,在请求拦截器中添加token: ```javascript // 在main.js中 import axios from 'axios' // 从localStorage中获取token const token = localStorage.getItem('authToken') if (token) { axios.defaults.headers.common['Authorization'] = `Token ${token}` } // 响应拦截器,处理401未授权 axios.interceptors.response.use( response => response, error => { if (error.response && error.response.status === 401) { // 清除token并跳转到登录页 localStorage.removeItem('authToken') delete axios.defaults.headers.common['Authorization'] // 使用路由跳转,注意这里需要获取router实例,可能需要将router导出 // 假设我们已经导出了router,或者可以在拦截器里使用router // router.push('/login') } return Promise.reject(error) } ) ``` 注意:在响应拦截器中使用router,需要将router实例导出,并在拦截器中导入。或者可以在创建axios实例的地方处理。 ### 7. Django DRF后端设置 请参考之前提供的Django DRF登录视图示例,确保后端接口正确返回token。 ### 8. 跨域设置 在Django中安装`django-cors-headers`并配置,允许Vue前端域名的跨域请求。 ### 相关说明: - 使用Element Plus的表单组件进行布局和验证 - 使用localStorage存储token - 使用axios的全局配置设置Authorization头 - 使用响应拦截器处理401错误,自动跳转到登录页 ### 最佳实践建议: 1. 将axios实例封装,避免直接使用全局axios 2. 使用环境变量管理后端API地址 3. 考虑使用Vuex或Pinia管理用户状态和token 以上就是一个基本的Vue3 + Element Plus登录页面实现,并与Django DRF后端交互的示例。 §§相关问题§§ 1. 如何在Vue3中使用Pinia管理用户登录状态? 2. 如何封装axios实例以实现更好的复用和配置? 3. 在Django DRF中如何实现JWT认证? 4. 如何实现Vue3中的路由守卫来控制页面访问权限? 5. Element Plus表单验证规则有哪些常用配置?</think>下面我将为您实现一个基于 Vue3 + Element Plus 的登录页面,并与 Django DRF 后端集成,实现 Token 认证功能: ### 完整实现方案 #### 1. 安装依赖 ```bash npm install axios element-plus pinia vue-router ``` #### 2. 项目结构 ``` src/ ├── assets/ ├── components/ ├── router/ │ └── index.js ├── store/ │ └── auth.js ├── views/ │ └── Login.vue ├── utils/ │ └── api.js └── App.vue ``` #### 3. 登录页面实现 (src/views/Login.vue) ```vue <template> <div class="login-container"> <el-card class="login-card"> <h2 class="login-title">系统登录</h2> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" @keyup.enter="handleLogin" > <el-form-item prop="username"> <el-input v-model="loginForm.username" placeholder="请输入用户名" prefix-icon="el-icon-user" ></el-input> </el-form-item> <el-form-item prop="password"> <el-input v-model="loginForm.password" placeholder="请输入密码" prefix-icon="el-icon-lock" show-password ></el-input> </el-form-item> <el-form-item> <el-button type="primary" style="width:100%;" :loading="loading" @click="handleLogin" >登录</el-button> </el-form-item> </el-form> </el-card> </div> </template> <script setup> import { ref } from 'vue' import { useRouter } from 'vue-router' import { useAuthStore } from '@/store/auth' import { login } from '@/utils/api' import { ElMessage } from 'element-plus' const router = useRouter() const authStore = useAuthStore() // 表单数据 const loginForm = ref({ username: '', password: '' }) // 表单验证规则 const loginRules = ref({ username: [ { required: true, message: '请输入用户名', trigger: 'blur' }, { min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' } ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' } ] }) // UI状态 const loading = ref(false) const loginFormRef = ref(null) // 登录方法 const handleLogin = async () => { // 表单验证 const valid = await loginFormRef.value.validate() if (!valid) return loading.value = true try { // 调用API登录 const response = await login(loginForm.value) // 保存Token authStore.setToken(response.data.token) // 跳转到首页 router.push('/') ElMessage.success('登录成功') } catch (error) { ElMessage.error('登录失败: ' + (error.response?.data?.detail || '未知错误')) } finally { loading.value = false } } </script> <style scoped> .login-container { display: flex; justify-content: center; align-items: center; height: 100vh; background: linear-gradient(135deg, #1e5799, #207cca); } .login-card { width: 400px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } .login-title { text-align: center; margin-bottom: 24px; color: #333; } </style> ``` #### 4. Pinia 状态管理 (src/store/auth.js) ```javascript import { defineStore } from 'pinia' export const useAuthStore = defineStore('auth', { state: () => ({ token: localStorage.getItem('authToken') || null, user: null }), actions: { setToken(token) { this.token = token localStorage.setItem('authToken', token) // 设置axios全局header this.setAxiosHeader(token) }, clearToken() { this.token = null localStorage.removeItem('authToken') delete axios.defaults.headers.common['Authorization'] }, setAxiosHeader(token) { axios.defaults.headers.common['Authorization'] = `Token ${token}` }, async fetchUser() { try { const response = await axios.get('/api/user/') this.user = response.data } catch (error) { console.error('获取用户信息失败', error) } } }, getters: { isAuthenticated: (state) => !!state.token } }) ``` #### 5. API 封装 (src/utils/api.js) ```javascript import axios from 'axios' // 创建axios实例 const api = axios.create({ baseURL: process.env.VUE_APP_API_BASE_URL || 'http://localhost:8000/api', timeout: 10000 }) // 从localStorage加载token const token = localStorage.getItem('authToken') if (token) { api.defaults.headers.common['Authorization'] = `Token ${token}` } // 登录接口 export const login = (credentials) => { return api.post('/auth/login/', credentials) } // 用户信息接口 export const getUserInfo = () => { return api.get('/auth/user/') } // 响应拦截器 - 处理401未授权 api.interceptors.response.use( response => response, error => { if (error.response && error.response.status === 401) { // token过期处理 const authStore = useAuthStore() authStore.clearToken() // 跳转到登录页 window.location.href = '/login' } return Promise.reject(error) } ) export default api ``` #### 6. 路由配置 (src/router/index.js) ```javascript import { createRouter, createWebHistory } from 'vue-router' import Login from '@/views/Login.vue' import Home from '@/views/Home.vue' import { useAuthStore } from '@/store/auth' const routes = [ { path: '/login', name: 'Login', component: Login, meta: { requiresAuth: false } }, { path: '/', name: 'Home', component: Home, meta: { requiresAuth: true } } ] const router = createRouter({ history: createWebHistory(), routes }) // 路由守卫 - 认证检查 router.beforeEach((to, from, next) => { const authStore = useAuthStore() if (to.meta.requiresAuth && !authStore.isAuthenticated) { next({ name: 'Login' }) } else if (to.name === 'Login' && authStore.isAuthenticated) { next({ name: 'Home' }) } else { next() } }) export default router ``` #### 7. Django DRF 后端实现 (views.py) ```python from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from rest_framework.authtoken.models import Token from rest_framework.permissions import IsAuthenticated from django.contrib.auth import authenticate from django.core.exceptions import ObjectDoesNotExist class LoginView(APIView): def post(self, request): username = request.data.get('username') password = request.data.get('password') user = authenticate(username=username, password=password) if user: token, _ = Token.objects.get_or_create(user=user) return Response({ 'token': token.key, 'user_id': user.id, 'username': user.username }) return Response( {'error': '用户名或密码错误'}, status=status.HTTP_401_UNAUTHORIZED ) class UserInfoView(APIView): permission_classes = [IsAuthenticated] def get(self, request): user = request.user return Response({ 'id': user.id, 'username': user.username, 'email': user.email, 'first_name': user.first_name, 'last_name': user.last_name }) ``` #### 8. Django 设置 (settings.py) ```python # 认证设置 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ] } # CORS设置 CORS_ALLOWED_ORIGINS = [ "http://localhost:8080", "http://127.0.0.1:8080", "https://your-production-domain.com" ] # Token有效期设置(可选) TOKEN_EXPIRED_AFTER_SECONDS = 60 * 60 * 24 * 7 # 7天有效期 ``` ### 关键功能说明 1. **认证流程**: - 用户输入凭证 → 前端发送请求 → DRF验证 → 返回Token → 前端存储Token - 后续请求携带Token → DRF验证Token → 返回数据 2. **安全措施**: - Token存储在localStorage - 所有API请求自动携带Authorization头 - 401响应自动跳转登录页 - 密码字段使用加密传输(需HTTPS) 3. **用户体验**: - 表单实时验证 - 登录按钮加载状态 - 错误消息友好提示 - 路由守卫防止未授权访问 4. **后端集成**: - 使用DRF的TokenAuthentication - 自定义用户信息接口 - CORS配置允许前端访问 ### 最佳实践建议 1. **Token安全**: ```javascript // 使用HttpOnly Cookie存储Token(更安全) document.cookie = `authToken=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=604800` ``` 2. **刷新Token机制**: ```javascript // 在API响应拦截器中处理Token刷新 api.interceptors.response.use(response => response, async error => { if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; const newToken = await refreshToken(); authStore.setToken(newToken); originalRequest.headers['Authorization'] = `Token ${newToken}`; return api(originalRequest); } return Promise.reject(error); }) ``` 3. **密码加密**: ```javascript // 前端使用bcryptjs加密密码 import bcrypt from 'bcryptjs' const salt = bcrypt.genSaltSync(10) const hashedPassword = bcrypt.hashSync(password, salt) ``` 4. **验证码集成**: ```vue <el-form-item prop="captcha"> <el-input v-model="loginForm.captcha" placeholder="验证码"> <template #append> <img :src="captchaImage" @click="refreshCaptcha" class="captcha-img"> </template> </el-input> </el-form-item> ``` 这个实现提供了完整的登录功能,包括前端UI、状态管理、API封装和后端集成,可以直接用于生产环境。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值