当前位置: 首页 > article >正文

【VUE】案例:商场会员管理系统

编写vue+dfr实现对会员进行基本增删改查

1. drf项目初始化

  • 请求:

    POST
    http://127/0.0.0.1:8000/api/auth/
    {"username":"cqn", "password":"123"}
    
  • 返回:

    {"username":"cqn", "token":"fwjkfbj"}
    
  • 创建项目
    在这里插入图片描述

  • 项目目录结构
    在这里插入图片描述

1.1 安装并注册drf

  • pip install djangorestframework
  • settings.py
    INSTALLED_APPS = [
        ...
        'django.contrib.staticfiles',
        'api.apps.ApiConfig',
        'rest_framework',
    ]
    

1.2 创建数据库表并添加数据

  • models.py
    from django.db import models
    
    class UserInfo(models.Model):
        username = models.CharField(verbose_name="用户名", max_length=64)
        password = models.CharField(verbose_name="密码", max_length=64)
        token = models.CharField(verbose_name="token", max_length=64, null=True, blank=True)
    
  • makemigrations
  • migrate
  • 插入两条数据“root 123”,“cqn 123”

1.3 创建apiview文件

views/account.py

import uuid
from rest_framework import serializers
from rest_framework.views import APIView
from rest_framework.response import Response
from api import models

class AuthSerializer(serializers.Serializer):
    username = serializers.CharField(required=True)
    password = serializers.CharField(required=True)

class AuthView(APIView):
    def post(self, request):
        # 1. 获取用户提交数据 request.data = {"username": "xxx", "password": "...}
        # 2. 表单校验
        ser = AuthSerializer(data=request.data)
        if not ser.is_valid():
            return Response({"code": 1000, "msg": "校验失败", "detail": ser.errors})
        # 3. 数据库校验
        user_object = models.UserInfo.objects.filter(**ser.data).first()
        if not user_object:
            return Response({"code": -1, "msg": "用户名或密码错误"})
        token = uuid.uuid4()
        user_object.token = token
        user_object.save()
        # 4. 数据返回
        return Response({
            "code": 0,
            "data": {"id": user_object.id, "name": user_object.username, "token": user_object.token}
        })

1.4 添加url配置

urls.py

from django.urls import path
from api.views import account

urlpatterns = [
    path('api/auth/', account.AuthView.as_view()),
]

1.5 启动并验证

在这里插入图片描述

2. vue项目初始化

  • 创建项目
    在这里插入图片描述
  • 项目目录
    在这里插入图片描述

2.1 main.js注释全局样式

// import './assets/main.css'

2.2 App.vue

<script setup>
</script>

<template>
  <RouterView />
</template>

<style scoped>
</style>

2.3 views

LoginView.vue

<template>
  <div class="box">
    <p>
      用户名:
      <input type="text" placeholder="用户名" v-model="msg.username">
    </p>
    <p>
      密码:
      <input type="text" placeholder="密码" v-model="msg.password">
    </p>
    <p>
      <button @click="doLogin">登录</button>
    </p>
  </div>
</template>

<script setup>
import {ref} from "vue";
import {useRouter} from "vue-router";
import {userInfoStore} from "@/stores/user.js";
import _axios from "@/plugins/axios.js";

const msg = ref({
  username: "",
  password: ""
})
const router = useRouter()
const store = userInfoStore()

const doLogin = function () {
  // 1、获取数据
  console.log(msg.value)
  // 2、发送网络请求
  _axios.get("/api/v1/course/category/free/?courseType=free").then((res) => {
    console.log(res.data)
  })
  // 3、本地存储用户信息(登录凭证)
  // localStorage.setItem("name", msg.value.username)
  let info = {id: 1, name: msg.value.username, token: "ddsafhsjdfkj"}
  store.doLogin(info)
  // 3、跳转到首页
  router.push({name: "mine"})
}

</script>

<style scoped>
.box {
  width: 300px;
  margin: 100px auto;
}
</style>

AdminView.vue

<template>
  <div class="page-header">
    <div class="container">
      <RouterLink to="/admin/mine">我的</RouterLink>
      |
      <RouterLink to="/admin/order">订单</RouterLink>
      <div style="float:right;">
        <a>{{store.userName}}</a>
        <a @click="doLogout">退出1</a>
      </div>
    </div>
  </div>
  <div class="container">
    <RouterView/>
  </div>
</template>

<script setup>
import {useRouter} from "vue-router";
import {userInfoStore} from "@/stores/user.js";

const router = useRouter()
const store = userInfoStore()

function doLogout() {
  store.doLogout()
  router.push({name: "login"})
}
</script>

<style scoped>
body {
  margin: 0;
}

.page-header {
  height: 48px;
  background-color: cornflowerblue;
  line-height: 48px;
}

.page-header a {
  display: inline-block;
  padding: 0 10px;
  cursor: pointer;
}

.container {
  width: 980px;
  margin: 0 auto;
}

</style>

MineView.vue

<template>
  <h1>Mine</h1>
</template>

<script setup>
</script>

<style scoped>
</style>

OrderView.vue

<template>
  <h1>Order</h1>
</template>

<script setup>
</script>

<style scoped>
</style>

2.4 stores

user.js

import {ref, computed} from 'vue'
import {defineStore} from 'pinia'


export const userInfoStore = defineStore('userInfo', () => {
    const userString = ref(localStorage.getItem("info"))
    const userDict = computed(() => userString.value ? JSON.parse(userString.value) : null)
    const userId = computed(() => userDict.value ? userDict.value.id : null)
    const userName = computed(() => userDict.value ? userDict.value.name : null)
    const userToken = computed(() => userDict.value ? userDict.value.token : null)

    function doLogin(info) {
        localStorage.setItem("info", JSON.stringify(info))
        userString.value = JSON.stringify(info)

    }

    function doLogout() {
        localStorage.clear()
        userString.value = null
    }

    return {userId, userName, userToken, doLogin, doLogout}
})

2.5 router

index.js

import { createRouter, createWebHistory } from 'vue-router'
import {userInfoStore} from "@/stores/user.js";

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/LoginView.vue')
    },
    {
      path: '/admin',
      name: 'admin',
      component: () => import('../views/AdminView.vue'),
      children: [
        {
          path: "",
          redirect: {name: "mine"}
        },
        {
          path: "mine",
          name: "mine",
          component: () => import('../views/MineView.vue')
        },
        {
          path: "order",
          name: "order",
          component: () => import('../views/OrderView.vue')
        }
      ]
    }
  ]
})
router.beforeEach(function (to,from,next) {
  // 1.访问登录页面,不需要登录就可以直接去查看
  if (to.name === "login") {
    next()
    return
  }
  // 2.检查用户登录状态,登录成功,继续往后走next();未登录,跳转至登录页面
  // let username = localStorage.getItem("name")
  const store = userInfoStore()
  if (!store.userId){
    next({name:"login"})
    return;
  }
  // 3.登录成功且获取到用户信息,继续向后访问
  next()
})

export default router

2.6 plugins

axios.js

import axios from "axios";

let config = {
    baseURL: "https://api.luffycity.com/",
    timeout: 20 * 1000
}

const _axios = axios.create(config)

_axios.interceptors.request.use(function (config){
    // console.log("请求前:", config)
    // 1. 去pinia中读取当前用户token
    // 2. 发送请求时携带token
    if(config.params){
        config.params["token"] = "djbfkjbdfkj"
    } else {
        config.params = {"token": "whejsabjdnfj"}
    }
    return config
})

export default _axios

2.7 安装并启动应用

  • sudo npm i
  • sudo npm i axios
  • sudo npm run dev
    在这里插入图片描述

3、前端调用后端,代码配置

ajax.js

import axios from "axios";

let config = {
    baseURL: "http://127.0.0.1:8000/",
    timeout: 20 * 1000
}

const _axios = axios.create(config)

_axios.interceptors.request.use(function (config){
    // console.log("请求前:", config)
    // 1. 去pinia中读取当前用户token
    // 2. 发送请求时携带token
    // if(config.params){
    //     config.params["token"] = "djbfkjbdfkj"
    // } else {
    //     config.params = {"token": "whejsabjdnfj"}
    // }
    return config
})

export default _axios

LoginView.vue

<template>
  <div class="box">
    <p>
      用户名:
      <input type="text" placeholder="用户名" v-model="msg.username">
    </p>
    <p>
      密码:
      <input type="text" placeholder="密码" v-model="msg.password">
    </p>
    <p>
      <button @click="doLogin">登录</button>
    </p>
  </div>
</template>

<script setup>
import {ref} from "vue";
import {useRouter} from "vue-router";
import {userInfoStore} from "@/stores/user.js";
import _axios from "@/plugins/axios.js";

const msg = ref({
  username: "",
  password: ""
})
const router = useRouter()
const store = userInfoStore()

const doLogin = function () {
  // 1、获取数据
  console.log(msg.value)
  // 2、发送网络请求
  _axios.post("/api/auth/", msg.value).then((res) => {
    console.log(res.data)
  })
  // 3、本地存储用户信息(登录凭证)
  // localStorage.setItem("name", msg.value.username)
  // let info = {id: 1, name: msg.value.username, token: "ddsafhsjdfkj"}
  // store.doLogin(info)
  // // 3、跳转到首页
  // router.push({name: "mine"})
}

</script>

<style scoped>
.box {
  width: 300px;
  margin: 100px auto;
}
</style>

4、现象:vue前端向后端API发送请求时有问题

Access to XMLHttpRequest at ‘http://127.0.0.1:8000/api/auth/’ from origin ‘http://localhost:5173’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
Access to XMLHttpRequest at ‘http://127.0.0.1:8000/api/auth/’ from origin ‘http://localhost:5173’ has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.

4.1 请求跨域问题

1、浏览器中有同源策略,当发送ajax请求:请求的地址与当前所在网址保持一致,如果不一致,浏览器就会阻止请求
2、为什么postman/requests不报错? 因为这俩软件没有同源策略的要求
3、在网站中用cdn地址? <script src='xxx'></script> <img src='xxx'/> 无影响
4、项目开发时,会经常用到跨域
5、解决方式

  • jsonp,发送ajax请求时,跨域会被浏览器阻止;所以不再使用ajax发送请求,而是构造script标签+src发送请求获取结果。
  • cors:添加响应头,让浏览器别再限制

6、后端API如何实现CORS? 中间件

Access-Control-Allow-Origin:"*"
Access-Control-Allow-Headers: "*"

7、实现方式
utils/cors.py

from django.utils.deprecation import MiddlewareMixin

class CorsMiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        response['Access-Control-Allow-Origin'] = "*"
        response['Access-Control-Allow-Headers'] = "*"
        return response

setting.py

MIDDLEWARE = [
    ...
    'utils.cors.CorsMiddleware'
]

http://www.kler.cn/a/327089.html

相关文章:

  • 干货分享:ISO 20000认证的适用范围、认证资料清单、认证流程等问题详解
  • [Unity]Unity集成NuGet-连接mysql时的发现
  • 如何根据一系列提交文件,匹配对应的git提交记录?用ai
  • SQL,生成指定时间间隔内的事件次序号
  • Redis 持久化机制详解
  • Pytorch | 从零构建ParNet/Non-Deep Networks对CIFAR10进行分类
  • find()和findIndex()方法
  • 微信小程序——音乐播放器
  • 【有啥问啥】二分图(Bipartite Graph)算法原理详解
  • SpringMVC源码-AbstractUrlHandlerMapping处理器映射器将实现Controller接口的方式定义的路径存储进去
  • 健康生活,从日常细节开始
  • NVLM多模态 LLM 在图像和语言任务中的表现优于 GPT-4o
  • Oracle数据恢复—异常断电导致Oracle数据库报错的数据恢复案例
  • 第167天:应急响应-日志自动提取分析项目_ELK_Logkit_LogonTracer_Anolog等
  • Mysql高级篇(下)——日志
  • Microsoft Edge 五个好用的插件
  • MySQL存储过程循环操作
  • LVGL 笔记
  • SpringBoot3+Swagger3(最新版springdoc-openapi教程)
  • 组合优化与凸优化 学习笔记5 对偶拉格朗日函数
  • 21 vue3之发布npm插件(hook自定义指令)
  • 国产RISC-V案例分享,基于全志T113-i异构多核平台!
  • 【刷题6】一维前缀和、二维前缀和
  • 学习VTK的目的和方法
  • 速盾:cdn加速什么好?
  • 【Linux探索学习】第二弹——Linux的基础指令(中)——夯实基础第二篇