开始
后端
项目配置
在项目中的settings.py文件中进行配置
数据库配置
DATABASES = { # 配置数据库
'default': {
# 在models.py中调用数据库
# 'ENGINE': 'django.db.backends.sqlite3',#sqlite3数据库
# 'NAME': BASE_DIR / 'db.sqlite3',#默认的文件数据库
'ENGINE': 'django.db.backends.mysql', # mysql数据库
'NAME': 'music1', # 数据库名字
'USER': 'root', # 账号
'PASSWORD': '123456', # 密码
'HOST': '127.0.0.1', # url
'PORT': '3306', # 端口
}
}
中文配置
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
文件路径配置
STATIC_URL = 'static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
跨域配置
MIDDLEWARE = [
# 'django.middleware.csrf.CsrfViewMiddleware',
]
CORS_ORIGIN_ALLOW_ALL = True # 允许任何来源的请求
APP配置
INSTALLED_APPS=[
...
'simpleui',
'APP',
]
模型
模型类
from django.db import models
# Create your models here.
class User(models.Model):
"""
用户模型
"""
GENDER_CHOICES = (
('M', '男'),
('F', '女'),
)
ROLE_CHOICES = (
('0', '管理员'),
('1', '普通用户'),
)
STATUS_CHOICES = (
('0', '正常'),
('1', '封号'),
)
id = models.BigAutoField(primary_key=True) # 用户ID
username = models.CharField(max_length=50, null=True) # 用户名mi
password = models.CharField(max_length=50, null=True) # 密码
role = models.CharField(max_length=2, blank=True, null=True) # 用户角色
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='0') # 用户状态
nickname = models.CharField(blank=True, null=True, max_length=20) # 昵称
avatar = models.FileField(upload_to='static/avatar/', null=True) # 头像
mobile = models.CharField(max_length=13, blank=True, null=True) # 手机号
email = models.CharField(max_length=50, blank=True, null=True) # 邮箱
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, blank=True, null=True) # 性别
description = models.TextField(max_length=200, null=True) # 描述
create_time = models.DateTimeField(auto_now_add=True, null=True) # 创建时间
score = models.IntegerField(default=0, blank=True, null=True) # 积分
push_email = models.CharField(max_length=40, blank=True, null=True) # 推送邮箱
push_switch = models.BooleanField(blank=True, null=True, default=False) # 推送开关
admin_token = models.CharField(max_length=32, blank=True, null=True) # 管理员令牌
token = models.CharField(max_length=32, blank=True, null=True) # 令牌
attention_count = models.IntegerField(default=0, blank=True, null=True, verbose_name='关注数')
followers_count = models.IntegerField(default=0, blank=True, null=True, verbose_name='粉丝数')
likenum_count = models.IntegerField(default=0, blank=True, null=True, verbose_name='点赞数')
def __str__(self):
return self.username
class Meta:
db_table = "b_user"
verbose_name_plural = '用户'
class Tag(models.Model):
"""
标签模型
"""
id = models.BigAutoField(primary_key=True) # 标签ID
title = models.CharField(max_length=100, blank=True, null=True) # 标签名称
create_time = models.DateTimeField(auto_now_add=True, null=True) # 创建时间
class Meta:
db_table = "b_tag"
verbose_name_plural = '标签'
def __str__(self):
return self.title
class Classification(models.Model):
"""
分类模型
"""
list_display = ("title", "id")
id = models.BigAutoField(primary_key=True) # 分类ID
pid = models.IntegerField(blank=True, null=True, default=-1) # 父分类ID
title = models.CharField(max_length=100, blank=True, null=True) # 分类名称
create_time = models.DateTimeField(auto_now_add=True, null=True) # 创建时间
def __str__(self):
return self.title
class Meta:
db_table = "b_classification"
verbose_name_plural = '分类'
class Music(models.Model):
"""
音乐模型
"""
STATUS_CHOICES = (
('0', '启用'),
('1', '禁用'),
)
id = models.BigAutoField(primary_key=True) # ID
name = models.CharField(max_length=20, blank=False, null=False, verbose_name='名字') # 名字
singer = models.CharField(max_length=20, blank=False, null=False, verbose_name='歌手') # 歌手
file = models.FileField(upload_to='static/music/', null=True, verbose_name='文件') # 文件
cover = models.ImageField(upload_to='static/cover/', null=True, verbose_name='封面') # 封面
description = models.TextField(max_length=1000, blank=False, null=False, verbose_name='描述') # 描述
time = models.TextField(max_length=10, blank=True, null=True, verbose_name='时长') # 时长
classification = models.ForeignKey(Classification, on_delete=models.CASCADE, blank=True, null=True,
verbose_name='类别') # 类别
tag = models.ManyToManyField(Tag, blank=True, verbose_name='标签') # 标签
create_time = models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间') # 创建时间
pv = models.IntegerField(default=0, verbose_name='浏览量') # 浏览量
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='0', verbose_name='状态') # 状态
score = models.IntegerField(default=0, verbose_name='评分') # 评分
recommend_count = models.IntegerField(default=0, verbose_name='推荐数') # 推荐数
collect = models.ManyToManyField(User, blank=True, verbose_name='收藏的用户') # 收藏的用户
collect_count = models.IntegerField(default=0, verbose_name='收藏数') # 收藏商品的数量
def __str__(self):
return self.name
class Meta:
db_table = "b_music"
verbose_name_plural = '音乐'
class Banner(models.Model):
"""
轮播图模型
"""
id = models.BigAutoField(primary_key=True) # 图片ID
image = models.ImageField(upload_to='static/banner/', null=True) # 图片
describe = models.CharField(max_length=100, blank=True, null=True, ) # 描述
create_time = models.DateTimeField(auto_now_add=True, null=True) # 创建时间
def __str__(self):
return self.describe
class Meta:
db_table = "b_banner"
verbose_name_plural = '轮播图'
admin后台管理
from django.contrib import admin
from APP import models
admin.site.register(models.User)
admin.site.register(models.Tag)
admin.site.register(models.Classification)
admin.site.register(models.Music)
admin.site.register(models.Banner)
api路径配置
urlpatterns = [
path('', views.main),
path('admin/', admin.site.urls),
path('getBanner/',views.getBanner),
path('getMusic/',views.getMusic),
path('getPage/',views.getPage),
]
视图函数
前端主页
def main(request):
return redirect('http://localhost:5173/main')
轮播图
def getBanner(request):
print('请求轮播图数据')
img_list = []
for i in models.Banner.objects.all()[0:4]:
img = cv2.imread(str(i.image))
img = cv2.imencode('.jpg', img)[1]
image_code = str(base64.b64encode(img))[2:-1] + ','
img_list.append(image_code)
return HttpResponse(img_list)
音乐数据
通过模型类对音乐数据表进行查询
在data列表中添加音乐数据的字典对象
img图片通过cv2读取从数据库中得到的图片文件
更换图片的编码为jpg
将图片转为beast64数据进行传输,前端网页对这种数据进行分析使用
最后通过JsonResponse将数据解析为json后发送
def getMusic(request):
print('请求音乐数据')
model = models.Music.objects.all()
data = []
for i in model:
obj = {}
obj['id'] = i.id
obj['name'] = i.name
obj['singer'] = i.singer
img = cv2.imread(str(i.cover))
img = cv2.imencode('.jpg', img)[1]
image_code = str(base64.b64encode(img))[2:-1]
obj['cover'] = image_code
with open(str(i.file), 'rb') as f:
mp3_data = f.read()
bytes = str(base64.b64encode(mp3_data))[2:-1]
obj['file'] = bytes
obj['time'] = i.time
data.append(obj)
return JsonResponse(data, safe=False)
页面数据
def getPage(request):
print('请求页面数据')
id = request.GET.get('request[id]')
i = models.Music.objects.get(id=id)
obj = {}
obj['id'] = i.id
obj['name'] = i.name
obj['singer'] = i.singer
img = cv2.imread(str(i.cover))
img = cv2.imencode('.jpg', img)[1]
image_code = str(base64.b64encode(img))[2:-1]
obj['cover'] = image_code
time_obj = datetime.datetime.fromisoformat(str(i.create_time))
# 构造只保留年月日的时间字符串
new_time_str = time_obj.strftime("%Y-%m-%d")
obj['create_time'] = new_time_str
return JsonResponse(obj, safe=False)
类别分类
def getPage(request):
print('请求页面数据')
id = request.GET.get('request[id]')
i = models.Music.objects.get(id=id)
obj = {}
obj['id'] = i.id
obj['name'] = i.name
obj['singer'] = i.singer
img = cv2.imread(str(i.cover))
img = cv2.imencode('.jpg', img)[1]
image_code = str(base64.b64encode(img))[2:-1]
obj['cover'] = image_code
time_obj = datetime.datetime.fromisoformat(str(i.create_time))
# 构造只保留年月日的时间字符串
new_time_str = time_obj.strftime("%Y-%m-%d")
obj['create_time'] = new_time_str
return JsonResponse(obj, safe=False)
启动后端服务
py manage runserver
前端
前端采用了Node20.0、Element-ui、Vue、Axios
项目配置
使用Node20.0创建Vue项目,安装所需要的依赖包
npm install element-plus
npm install axios
在main.js文件中配置
此处对element中的组件、css、icon图标进行全局配置
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index' // 引入路由模块
import ElementPlus from 'element-plus'//element
import 'element-plus/dist/index.css'//element的css
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
app.use(router)
app.use(ElementPlus)//挂在element-ui
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {//导入icon图标
app.component(key, component)
}
app.mount('#app')
vite.config,js中配置
在浏览器中有一种安全措施,他会禁止不同程序之间的通讯,我们需要对此进行配置
当我们的axios请求时的url为/api/getPage/时,相当于我们访问了http://127.0.0.1:8000/getPage
这里将url中的'/api'替换为空
import {fileURLToPath, URL} from 'node:url'
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
// 代理
proxy: {
'/api': {
target: 'http://127.0.0.1:8000/', // 代理后台服务器地址
changeOrigin: true, //允许跨域
rewrite: path => path.replace(/^\/api/, '') // 将请求地址中的 /api 替换成空
},
}
}
})
路由配置
在src下创建router/index.js文件
这里对可访问的url进行配置
默认访问的是/main的页面
import {createRouter, createWebHistory} from 'vue-router'
const routes = [
{
path: '/',
redirect: '/main'
},
{
path: '/main',
name: 'main',
component: () => import('@/views/Main.vue') // 渲染该组件
},
{
path: '/home',
name: 'home',
component: () => import('@/views/Page.vue') // 渲染该组件
},
{
path: '/test2',
name: 'test2',
component: () => import('@/views/MyMusic.vue') // 渲染该组件
},
{
path: '/page',
name: 'page',
component: () => import('@/views/Page.vue') // 渲染该组件
},
]
const router = createRouter({
history: createWebHistory(),//采用html5路由模式
routes
})//创建路由对象
export default router
视图
入口视图
此处代码定义了全局语言为中文
顶部的导航栏:搜索框、导航
导航栏中访问的url路径:主页、我的音乐、分类歌单、数字专辑、个人管理
视图位置:使用了Element中的布局方法,将音乐播放器组件放入最底下(绝对定位,防止被其他组件遮挡)
<el-menu>导航栏</el-menu>
<el-container>
<el-main>导航视图</el-main>
<el-footer>页脚</el-footer>
</el-container>
<music/>
主页视图
此处视图会显示在入口视图的<el-main>布局位置中
这个页面使用了Element中的布局方法包含了
轮播图
使用Element中的标签组件,定义了轮播图的数据、切换速度、轮播图类型、轮播图样式、轮播图片、图片张数
<el-carousel>
<el-carousel-item>
<el-image/>
</el-carousel-item>
</el-carousel>
数据切换导航
在中部中我使用了Element的第二种布局方式
<el-row>
<el-col :span="19">主体数据</el-col>
<el-col :span="5">侧边栏</el-col>
</el-row>
数据切换导航使用了Element中的<el-tabs>和<el-tab-pane>标签来达到中部数据的切换
<el-tab-pane label="最新" name="first">
<!--最新的歌曲卡片-->
<el-tab-pane>
<el-tab-pane label="推荐" name="second">
<!--推荐的歌曲卡片-->
</el-tab-pane>
歌曲卡片
使用Element的标签组件使用v-for加载多张卡片数据
<el-space wrap>
<el-card :body-style="{ padding: '0px' }" v-for="(i,j) in name.length" :key="j" class="box-card"
v-loading="loading">
<div>
<!--卡片数据-->
</div>
</el-card>
</el-space>
用户卡片、排行榜
用户卡片和排行榜我单独写了一个组件将它布局到侧边栏中,其中使用aioxs向Django后端发送请求来获取其中的数据
<!--在template中使用-->
<CariyselMin/>
<Latest/>
<!--在script中导入组件-->
import CariyselMin from "@/components/cariyselMin.vue";
import Latest from "@/components/latest.vue";
<!--在export default中注册组件-->
components: {CariyselMin, Latest},
回到顶部的按钮
使用Element中的标签组件,写入距离网页右边的距离,距离底部的距离
<el-backtop :right="15" :bottom="100"/><!--回顶部按钮-->
音乐播放器
图标使用Element中的标签组件el-icon图标
对音乐盒的各个控件使用vue的事件绑定
播放方法
当我们点击播放按钮时执行该方法,对定义的this.startPause变量进行判断,是否为'iconfont icon-bofang'字符串,是则播放,不是则停止
接着使用三元运算符来对this.startPause变量进行判断这个方法可以根据当前按钮的状态对this.startPause变量进行赋值,
startPauseDemo()
{
if (this.startPause === 'iconfont icon-bofang') {
this.$refs.music.play()
console.log('播放')
} else {
this.$refs.music.pause()
console.log('暂停')
}
this.startPause === 'iconfont icon-bofang' ? this.startPause = 'iconfont icon-zanting1' : this.startPause = 'iconfont icon-bofang'
}
,
切歌方法
判断当前歌曲数据的最大长度,判断是否还有下一首歌
如果有则让数据的下标this.i变量移动一位,停止播放,让播放图标变为暂停状态
next()
{
if (this.i < this.musicSrc.length - 1) {
this.i++
this.timeLine = 0
this.$refs.music.pause()
this.startPause = 'iconfont icon-bofang'
}
}
},
音量调节
使用vue的watch监控音量变量,当volume音量变量发生改变时返回发生改变前旧的值和发生改变以后新的值,使用DOM将值赋值到<audio>标签的音量变量上
watch: {
volume(oldValue, newValue)
{
this.$refs.music.volume = newValue/*音量*/
}
,
}
axios数据请求
此处代码使用axios对我们的Django后端服务器中的127.0.0.1:8000/getMuscic/链接发起了get请求
携带参数?=getMusic
使用then..catch错误捕获的方法进行请求,
如果请求成功就会返回正常的res请求数据,反之则会返回错误的error报错信息
我们的数据在res的data属性中将data中的数据添加到变量当中
jpg和mp3数据使用beast64编码进行传输在img的src属性中需要添加前缀
data:image/png;base64,
data:audio/mp3;base64,
使数据能正确的分析
getData() {
console.log('请求音乐盒数据')
axios.get('/api/getMusic/', {
params: {
request: 'getMusic',
},
}).then((res) => {
console.log(res)
console.log('获取音乐')
console.log(res.data)
var data = res.data
for (var i = 0; i <= data.length; i++) {
this.name[i] = data[i].name;
this.musician[i] = data[i].singer;
this.musicSrc[i] = 'data:audio/mp3;base64,' + data[i].file;
const base64Data = data[i].cover;
this.imgSrc[i] = 'data:image/png;base64,' + base64Data;
this.duration[i] = data[i].time;
}
console.log(this.banner)
}
).catch(error => {
// 处理错误
console.log('请求数据未成功')
console.log(error)
})
},
启动前端服务
npm run dev
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://cpxigxs.cn/2024/04/20/vuedjango%e9%9f%b3%e4%b9%90%e7%bd%91%e7%ab%99/
共有 0 条评论