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,
    }
}
复制代码

image-20211125094633589

创建应用

startapp system
复制代码

创建数据库

image-20211125095802797

编写模型

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

复制代码

注册应用

image-20211125095934479

同步数据库

makemigrations
migrate
复制代码

image-20211125100030673

image-20211125100107211

接口编写

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

复制代码

测试

测试前需要在数据库中添加数据

image-20211125103826854

景点

新建应用

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
复制代码

image-20211125120526773

接口编写

根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)
复制代码

测试

image-20211125141816024

轮播图接口对接

image-20211125153442001

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
}

复制代码

image-20211125153555373

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>

复制代码

效果:

image-20211125153925697

景点列表接口对接

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>

复制代码

效果:

image-20211125194438312

猜你喜欢

转载自juejin.im/post/7034683692706431012