Vue+Django 旅游网项目 首页后端实现
这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战
Vue+Django 旅游网项目 首页后端实现
轮播图
配置数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'trip',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': 3306,
}
}
创建应用
startapp system
创建数据库
编写模型
class Slider(models.Model):
name=models.CharField('名称',max_length=32)
desc=models.CharField('描述',max_length=100,null=True,blank=True)
types=models.SmallIntegerField('展现的位置',default=10)
img=models.ImageField('图片地址',max_length=255,upload_to='%Y%m/slider')
reorder=models.SmallIntegerField('排序字段',default=0,help_text="数字越大越靠前")
start_time=models.DateTimeField('生效开始时间',null=True,blank=True)
end_time=models.DateTimeField('生效结束时间',null=True,blank=True)
target_url=models.CharField('跳转的地址',max_length=255,null=True,blank=True)
is_valid=models.BooleanField('是否有效',default=True)
created_at=models.DateTimeField('创建时间',auto_now_add=True)
updated_at=models.DateTimeField('修改时间',auto_now=True)
class Meta:
db_table='system_slider'
ordering=['-reorder']
注册应用
同步数据库
makemigrations
migrate
接口编写
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
# Create your views here.
from system.models import Slider
def slider_list(request):
""" 轮播图接口
{
"meta":[]
"objects":[]
}
"""
data ={
'meta':{},
'objects':[]
}
querset=Slider.objects.filter(is_valid=True)
for item in querset:
data['objects'].append({
'id':item.id,
'img_url': item.img.url,
'target_url':item.target_url,
'name': item.name
})
return JsonResponse(data)
utl设置
system/urls.py
from django.urls import path
from system import views
urlpatterns = [
path('slider/list', views.slider_list),
]
根urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# 系统模块
path('system/',include('system.urls'))
]
测试
测试前需要在数据库中添加数据
景点
新建应用
startapp sight
注册
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'system',
'sight'
]
编写模型
class Sight(models.Model):
name = models.CharField('名称',max_length=64)
desc=models.CharField('描述',max_length=255)
main_img=models.ImageField('主图',upload_to='%Y%m/sight/',max_length=255)
banner_img=models.ImageField('详情主图',upload_to='%Y%m/sight/',max_length=255)
content=models.TextField('详情')
score=models.FloatField('评分',default=5)
min_price=models.FloatField('最低价格',default=0)
province=models.CharField('省份',max_length=32)
city=models.CharField('市区',max_length=32)
area=models.CharField('区/县',max_length=32,null=True)
town=models.CharField('乡镇',max_length=32,null=True)
is_top=models.BooleanField('是否为精选景点',default=False)
is_hot=models.BooleanField('是否为热门景点',default=False)
is_valid=models.BooleanField('是否有效',default=True)
created_at=models.DateTimeField('创建时间',auto_now_add=True)
updated_at=models.DateTimeField('修改时间',auto_now=True)
class Meta:
db_table:'sight'
ordering=['-updated_at']
数据库同步
makemigrations
migrate
接口编写
根utls.py
urlpatterns = [
path('admin/', admin.site.urls),
# 系统模块
path('system/',include('system.urls')),
# 景点
path('sight/',include('sight.urls'))
]
sight urls.py
from django.urls import path
from sight import views
urlpatterns = [
# 景点列表接口
path('sight/list/', views.SightListView.as_view()),
]
views.py
from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import render
# Create your views here.
from django.views.generic import ListView
from sight.models import Sight
class SightListView(ListView):
""" 景点列表 """
# 每页5条数据
paginate_by = 5
def get_queryset(self):
# 重写查询方法
query=Q(is_valid=True)
# 热门景点
is_hot = self.request.GET.get('is_hot',None)
if is_hot:
query=query & Q(is_hot=True)
# 精选景点
is_top = self.request.GET.get('is_top', None)
if is_top:
query = query & Q(is_top=True)
queryset=Sight.objects.filter(query)
return queryset
def render_to_response(self, context, **response_kwargs):
page_obj=context['page_obj']
data = {
'meta': {
'total_count':page_obj.paginator.count,
'page_count':page_obj.paginator.num_pages,
'current_page':page_obj.number,
},
'objects': []
}
for item in page_obj.object_list:
data['objects'].append({
'id':item.id,
'name':item.name,
'main_img':item.main_img.url,
'score':item.score,
'province':item.province,
'min_price':item.min_price,
'city':item.city,
'comment_count':0
})
return JsonResponse(data)
测试
轮播图接口对接
ajax.js
import axios from 'axios'
export const ajax = axios.create({
headers: {
source: 'h5',
icode: 'acbd',
'Content-Type': 'application/x-www-form-urlencoded'
},
withCredentials: true
})
ajax.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
console.log('请求拦截到了')
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
ajax.interceptors.response.use(function (response) {
// 对响应数据做点什么
console.log('响应拦截到了')
return response
}, function (error) {
// 对响应错误做点什么
if (error.response) {
if (error.response.status === 401) {
window.alert('未登录,即将跳转到登录页面')
} else if (error.response.status === 500) {
window.alert('服务器正忙,请稍后重试')
}
}
return Promise.reject(error)
})
apis.js
// 存放项目中所有的接口地址
const apiHost = 'http://localhost:8080/api'
const AccountsApis = {
loginUrl: '/',
logoutUrl: ''
}
/**
* 系统模块接口
*/
const SystemApis = {
sliderListUrl: apiHost + '/system/slider/list/'
}
export {
AccountsApis,
SystemApis
}
vue.config.js
module.exports = {
devServer:{
proxy: {
'/api': {
target:'http://127.0.0.1:8000/',
changeOrigin: true,
pathRewrite: {
'^/api': '' //需要rewrite重写的URL
}
}
}
}
}
banner组件
<template>
<!-- 轮播图 -->
<div class="home-banner-box">
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
<van-swipe-item v-for="item in bannerList"
:key="item.id">
<img :src="item.img_url" alt="">
</van-swipe-item>
</van-swipe>
</div>
</template>
<script>
import { ajax } from '@/utils/ajax'
import { SystemApis } from '@/utils/apis'
export default {
data () {
return {
bannerList: []
}
},
methods: {
/**
* 获取轮播图数据
*/
getDataList () {
ajax.get(SystemApis.sliderListUrl).then(res => {
console.log('res', res)
this.bannerList = res.data.objects
})
}
},
created () {
this.getDataList()
}
}
</script>
<style lang="less">
.home-banner-box {
img {
width: 100%;
height: auto;
}
}
</style>
效果:
景点列表接口对接
apis.js
// 存放项目中所有的接口地址
const apiHost = 'http://localhost:8080/api'
const AccountsApis = {
loginUrl: '/',
logoutUrl: ''
}
/**
* 系统模块接口
*/
const SystemApis = {
// 轮播图列表
sliderListUrl: apiHost + '/system/slider/list/'
}
/**
* 景点模块
*/
const SightApis = {
// 景点列表
sightListUrl: apiHost + '/sight/sight/list/'
}
export {
AccountsApis,
SystemApis,
SightApis
}
组件Fine.vue
<template>
<!-- 精选景点 -->
<div class="home-fine-box">
<!-- 导航 -->
<van-cell
icon="location-o"
title="热门推荐"
title-style="text-align:left"
value="全部榜单"
is-link />
<!-- 列表 -->
<div class="box-main">
<SightItem v-for="item in dataList"
:key="item.id"
:item="item"/>
</div>
</div>
</template>
<script>
import { ajax } from '@/utils/ajax'
import { SightApis } from '@/utils/apis'
import SightItem from '@/components/common/ListSight'
export default {
components: {
SightItem
},
data () {
return {
dataList: []
}
},
methods: {
getDataList () {
ajax.get(SightApis.sightListUrl, {
params: {
is_top: 1
}
}).then(({ data }) => {
this.dataList = data.objects
})
}
},
created () {
this.getDataList()
}
}
</script>
<style lang="less">
.home-fine-box {
padding: 0 10px;
.van-cell {
padding: 10px 0;
}
.box-main {
padding-bottom: 50px;
}
}
</style>
Hot.vue
<template>
<div class="home-hot-box">
<!-- 导航 -->
<van-cell
icon="/static/home/hot/fire.png"
title="热门推荐"
title-style="text-align:left"
value="全部榜单"
is-link />
<!-- 列表 -->
<div class="box-main">
<a href="#" class="hot-item"
v-for="item in dataList"
:key="item.id">
<div class="img">
<span></span>
<img :src="item.main_img" alt="">
</div>
<h5 class="van-ellipsis">{{ item.name }}</h5>
<div class="line-price">
<span class="price">¥{{ item.min_price }}</span>起
</div>
</a>
</div>
</div>
</template>
<script>
import { ajax } from '@/utils/ajax'
import { SightApis } from '@/utils/apis'
export default {
data () {
return {
dataList: []
}
},
methods: {
getDataList () {
ajax.get(SightApis.sightListUrl, {
params: {
is_hot: 1
}
}).then(({ data }) => {
this.dataList = data.objects
})
}
},
created () {
// 查询接口数据
this.getDataList()
}
}
</script>
<style lang="less">
.home-hot-box {
padding: 0 10px;
.van-cell {
padding: 10px 0;
}
.box-main {
width: 100%;
display: flex;
padding-top: 10px;
overflow-x: scroll;
}
.hot-item {
display: flex;
flex-direction: column;
width: 100px;
margin-right: 10px;
padding-bottom: 10px;
.img {
position: relative;
span {
position: absolute;
left: 0;
top: 0;
display: inline-block;
width: 42px;
height: 20px;
z-index: 10;
}
img {
width: 100px;
height: 100px;
}
}
h5 {
color: #212121;
padding: 2px 0;
font-size: 12px;
margin: 0;
}
.line-price {
color: #212121;
font-size: 12px;
.price {
color: #f50;
font-size: 13px;
}
}
&:nth-child(1) .img span {
background: url(/static/home/hot/top1.png) no-repeat;
background-size: 100% auto;
}
&:nth-child(2) .img span {
background: url(/static/home/hot/top2.png) no-repeat;
background-size: 100% auto;
}
&:nth-child(3) .img span {
background: url(/static/home/hot/top3.png) no-repeat;
background-size: 100% auto;
}
}
}
</style>
效果:
转载自:https://juejin.cn/post/7034683692706431012