景点详情模块

搜索页面初步实现和跳转

在views文件夹下新建 Search.vue

<template>
<!-- 搜索页面 -->
  <div class="page-search">
      <!-- 标题 -->
      <van-nav-bar title="搜索景点" />
      <!-- 搜索框 -->
      <van-search
        v-model="value"
        show-action
        label="景点"
        placeholder="请输入搜索关键词"
        @search="onSearch"
      >
        <template #action>
          <div @click="onSearch">搜索</div>
        </template>
      </van-search>
      <!-- 列表 -->
      <div class="sight-list">
        <SightItem v-for="item in dataList"
          :key="item.id"
          :item="item"/>
      </div>
      <!-- 分页 -->
      <van-pagination v-model="currentPage" :total-items="totalItems" :items-per-page="perPage" />
      <!-- 页脚 -->
      <TripFooter/>
  </div>
</template>
<script>
import SightItem from '@/components/common/ListSight'
import TripFooter from '@/components/common/Footer'
export default {
  data () {
    return {
      // 景点名称
      sightName: '',
      // 景点列表的数据
      dataList: [],
      // 总记录数
      totalItems: 0,
      // 当前的页码
      currentPage: 1,
      // 每页数据的大小
      perPage: 5
    }
  },
  components: {
    SightItem,
    TripFooter
  },
  methods: {
    onSearch () {
      console.log('onSearch')
    }
  }
}
</script>

设置路由

Vue+Django 旅游网项目 景点详情模块前端实现_django

router下的index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Search from '../views/Search.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/search',
    name: 'Search',
    component: Search
  }
]

const router = new VueRouter({
  routes
})

export default router

设置路由的跳转 Footer.vue

Vue+Django 旅游网项目 景点详情模块前端实现_vue.js_02

<template>
<!-- 底部导航 -->
  <div>
    <van-tabbar v-model="active" route>
      <van-tabbar-item name="home" icon="wap-home-o" :to="{name: 'Home'}">首页</van-tabbar-item>
      <van-tabbar-item name="search" icon="search" :to="{name: 'Search'}">搜索</van-tabbar-item>
      <van-tabbar-item name="mine" icon="user-o">我的</van-tabbar-item>
    </van-tabbar>
  </div>
</template>
<script>
export default {
  data () {
    return {
      active: 'home'
    }
  }
}
</script>

去掉App.vue中多余的内容

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<style lang="less">
@import "./assets/style/common.less";
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;

  a {
    font-weight: bold;
    color: #2c3e50;

    &.router-link-exact-active {
      color: #42b983;
    }
  }
}
</style>

效果:

点击搜索按钮跳转到搜索页面

Vue+Django 旅游网项目 景点详情模块前端实现_前端_03

景点详情页

views文件夹下新建sight文件夹

新建五个vue文件

景点列表 SightList.vue

景点详情 SightDeatail.vue

景点介绍 SightInfo.vue

评论列表 SightComment.vue

景点下的图片 SightImage.vue

Vue+Django 旅游网项目 景点详情模块前端实现_ico_04

内容暂时均为

<template>
  <div class="page-sight-info"></div>
</template>

添加路由

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Search from '../views/Search.vue'
import SightList from '../views/sight/SightList.vue'
import SightDeatail from '../views/sight/SightDeatail.vue'
import SightInfo from '../views/sight/SightInfo.vue'
import SightComment from '../views/sight/SightComment.vue'
import SightImage from '../views/sight/SightImage.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/search',
    name: 'Search',
    component: Search
  },
  // 景点列表
  {
    path: '/search/list',
    name: 'SightList',
    component: SightList
  },
  // 景点详情
  {
    path: '/sight/detail/:id',
    name: 'SightDeatail',
    component: SightDeatail
  },
  // 景点介绍
  {
    path: '/sight/info/:id',
    name: 'SightInfo',
    component: SightInfo
  },
  // 评论列表
  {
    path: '/sight/comment/:id',
    name: 'SightComment',
    component: SightComment
  },
  // 景点下的图片
  {
    path: '/sight/image/:id',
    name: 'SightImage',
    component: SightImage
  }
]

const router = new VueRouter({
  routes
})

export default router

修改组件的跳转

common文件夹下的ListSight.vue 的a标签

<template>
  <router-link class="sight-item"
    :to="{ name: 'SightDeatail', params: { id: item.id }}">
    <img :src="item.main_img" alt="">
    <div class="right">
        <h5>{{ item.name }}</h5>
        <van-rate v-model="item.score" readonly/>
        <div class="tips">{{ item.comment_count }}人点评 | 100%满意</div>
        <div class="tips light">{{ item.province }} - {{ item.city }}</div>
        <div class="line-price">{{ item.min_price }}</div>
    </div>
  </router-link>
</template>
<script>
export default {
  props: ['item']
}
</script>
<style lang="less">
.sight-item {
  display: flex;
  margin-top: 10px;
  border-bottom: 1px solid #f6f6f6;
  img {
    width: 100px;
    height: 100px;
  }
  h5 {
    color: #212121;
    font-size: 14px;
    padding: 5px 0;
    margin: 0;
  }
  .right {
    text-align: left;
    flex-grow: 1;
    text-align: left;
    justify-content: left;
    padding-left: 5px;
    position: relative;
  }
  .line-price {
    position: absolute;
    right: 10px;
    top: 20px;
    display: inline-block;
    color: #f50;
    font-size: 16px;
    font-weight: bold;
  }
  .tips {
    font-size: 12px;
    color: #666;
  &.light {
    color: #999;
  }
  }
}
</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
          :to="{name: 'SightList', query: {name:'热门推荐'}}" />
        <!-- 列表 -->
        <div class="box-main">
          <router-link class="hot-item"
            v-for="item in dataList"
            :key="item.id"
            :to="{ name: 'SightDeatail', params: { id: 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>
          </router-link>
        </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>

Fine.vue

<template>
    <!-- 精选景点 -->
    <div class="home-fine-box">
      <!-- 导航 -->
        <van-cell
          icon="location-o"
          title="精选景点"
          title-style="text-align:left"
          value="全部榜单"
          is-link
          :to="{name: 'SightList', query: {name:'精选景点'}}"/>
        <!-- 列表 -->
        <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>

跳转效果

点击热门推荐或精选景点

Vue+Django 旅游网项目 景点详情模块前端实现_搜索_05

Vue+Django 旅游网项目 景点详情模块前端实现_ico_06

编写评论列表组件

components文件夹下

新建sight文件夹

新建 CommentItem.vue

Vue+Django 旅游网项目 景点详情模块前端实现_django_07

<template>
  <!-- 评论列表的每一项 -->
  <div class="comment-item-box">
    <div class="cmt-header">
      <div class="rate">
        <van-rate v-model="value"
        allow-half
        readonly
        void-icon="star"
        void-color="#eee"/>
      </div>
      <div class="user">张三*** 2020-02-02</div>
    </div>
    <div class="cmt-content">
      <p>评论内容</p>
    </div>
    <!-- 图片列表 -->
    <div class="cmt-imgs" @click="show=true">
      <van-image width="100" height="100" src="https://img.yzcdn.cn/vant/cat.jpeg"/>
    </div>
    <van-image-preview v-model="show" :images="images" @change="onChange">
      <template v-slot:index>{{ index+1 }}</template>
    </van-image-preview>
  </div>
</template>
<script>
export default {
  data () {
    return {
      show: false,
      index: 0,
      images: [
        'https://img.yzcdn.cn/vant/apple-1.jpg',
        'https://img.yzcdn.cn/vant/apple-2.jpg'
      ],
      value: 4.5
    }
  },
  methods: {
    onChange (index) {
      this.index = index
    }
  }
}
</script>
<style lang="less">
.comment-item-box {
  padding: 5px 10px;
  border-bottom: 1px solid #f6f6f6;

  .cmt-header {
    display: flex;
    justify-content: space-between;

    .user {
      font-size: 12px;
    }
  }
  .cmt-content {
    color: #616161;
    padding: 5px 0;
    text-align: left;
    font-size: 12px;
    line-height: 2.0;
  }
  .cmt-imgs {
    padding: 5px;
    text-align: left;
    margin-right: 5px;
  }
}
</style>

编写景点详情页内容

SightDeatail.vue

<template>
  <!-- 景点详情 -->
  <div class="pafe-sight-detail">
    <!-- 页面头部 -->
    <van-nav-bar
    left-text="返回"
    left-arrow
    fixed
    @click-left="goBack"
    />
    <!-- 大图 -->
    <div class="sight-banner">
      <van-image src="/static/home/hot/h1_max.jpg" width="100%" height="100%"/>
      <div class="tips">
        <router-link class="pic-sts" :to="{name: 'SightImage', params: {id: 123}}">
          <van-icon name="video-o" />
          <span>10 图片</span>
        </router-link>
        <div class="title">景点标题</div>
      </div>
    </div>
    <!-- 评分、景点介绍 -->
    <div class="sight-info">
      <div class="left">
        <div class="info-title">
          <strong>5</strong>
          <small>很棒</small>
        </div>
        <div class="info-tips">50 评论</div>
        <van-icon name="arrow" />
      </div>
      <div class="right">
        <div class="info-title">
          <span>景点介绍</span>
        </div>
        <div class="info-tips">开放时间、贴士</div>
        <van-icon name="arrow" />
      </div>
    </div>
    <!-- 地址信息 -->
    <van-cell title="广东省广东市番禺区番禺大道" icon="location-o"
      is-link
      :title-style="{'text-align': 'left'}">
      <template #right-icon>
        <van-icon name="arrow" />
      </template>
    </van-cell>
    <!-- 门票列表 -->
    <div class="sight-ticket">
      <van-cell title="门票" icon="bookmark-o" title-style="text-align:left"/>
      <div class="ticket-item" v-for="i in 5" :key="i">
        <div class="left">
          <div class="title">成人票</div>
          <div class="tips">
            <van-icon name="clock-o" />
            <span>7点之前可以预定</span>
          </div>
          <div class="tags">
            <van-tag mark type="primary">标签1</van-tag>
          </div>
        </div>
        <div class="right">
          <div class="price">
            <span></span>
            <strong>65</strong>
          </div>
          <router-link to="#">
            <van-button type="warning" size="small">预定</van-button>
          </router-link>
        </div>
      </div>
    </div>
    <!-- 用户评价 -->
    <div class="sight-comment">
      <van-cell title="热门评论" icon="comment-o" title-style="text-align:left"/>
      <CommentItem/>
      <router-link class="link-more" :to="{name: 'SightComment', params: {id}}">查看更多</router-link>
    </div>
  </div>
</template>
<script>
// 评论项组件
import CommentItem from '@/components/sight/CommentItem'
export default {
  data () {
    return {
      id: ''
    }
  },
  components: {
    CommentItem
  },
  methods: {
    goBack () {
      this.$router.go(-1)
    }
  },
  created () {
    this.id = this.$$route.params.id
  }
}
</script>
<style lang="less">
.pafe-sight-detail {
  .van-nav-bar {
    background-color: transparent;
  }
  // 景点大图
  .sight-banner{
    position: relative;
    .tips {
      position: absolute;
      left: 10px;
      bottom: 10px;
      font-size: 16px;
      color: #fff;

      .pic-sts {
        color: #fff;
        border-radius: 30px;
        font-size: 14px;
        background-color: rgba(0,0,0,0.4);
      }
    }
  }
  // 评分、景点介绍
  .sight-info {
    display: flex;
    background-color: #fff;
    border-bottom: 1px solid #f6f6f6;

    & > div {
      flex: 1;
      position: relative;
    }
    .right {
      border-left: 1px solid #f6f6f6;
    }
    .info-title {
      text-align: left;
      padding: 5px 10px;
      strong {
        color: #ff8300;
      }
    }
    .info-tips {
      color: #999;
      font-size: 12px;
      text-align: left;
      padding: 5px 10px;
    }
    .van-icon {
      position: absolute;
      right: 5px;
      top: 5px;
    }
  }
  // 门票列表
  .sight-ticket {
    margin-top: 10px;
    background-color: #fff;

    .ticket-item {
      display: flex;
      border-bottom: 1px solid #f6f6f6;
      padding-bottom: 10px;

      .left {
        flex: 1;
        text-align: left;
        padding: 5px 10px;

        .title {
          padding: 5px 0;
        }
        .tips {
          font-size: 12px;
        }
      }
      .right {
        width: 100px;
        .price {
          color: #ff9800;
          strong {
            font-size: 20px;
          }
        }
      }
    }
  }
  // 评论列表
  .sight-comment {
    margin-top: 10px;
    background-color: #fff;
  }
  // 查看更多
  .link-more {
    display: block;
    color: #666;
    padding: 10px;
  }
}
</style>

效果:

Vue+Django 旅游网项目 景点详情模块前端实现_vue.js_08

Vue+Django 旅游网项目 景点详情模块前端实现_搜索_09