【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'
]