• 路飞学城前端Vue


    路飞学城前端Vue

    创建项目

    首先我们要创建项目并安装相关的组件

    复制代码
    1. vue init webpack luffy
    
    2. 安装依赖:
        cd luffy
            npm install vuex --save            用于:多组件之间数据共享
            npm install vue-cookies --save  用于:操作cookie
            npm install axios --save        用于:发送ajax请求
    
    3. 快速使用
        程序入口(manage.py):
            App.vue 
                - 组件名称【组件1(/index ),组件2(/course),组件3】
                - 空容器
            main.js 
            
        路由(urls.py):
            router/index.js 
                /index    Course1.vue
                /course   Course2.vue
        组件(views):
            Course1.vue  (每个py文件)
            Course2.vue  (每个py文件)
            Course3.vue  (每个py文件)
    复制代码

    创建基本的路由和组件

    首先我们要在App.vue中展现一些基本的组件,需要创建路由和相关的组件

    index.js

    复制代码
    import Vue from 'vue'
    import Router from 'vue-router'
    import Index from '@/components/Index'
    import Course from '@/components/Course'
    import Detail from '@/components/Detail'
    import Micro from '@/components/Micro'
    import News from '@/components/News'
    
    Vue.use(Router)
    
    export default new Router({
      routes: [
        {
          path: '/',
          name: 'index',
          component: Index
        },
        {
          path: '/course',
          name: 'course',
          component: Course
        },
        {
          path: '/detail/:cid',
          name: 'detail',
          component: Detail
        },
        {
          path: '/micro',
          name: 'micro',
          component: Micro
        },
        {
          path: '/news',
          name: 'news',
          component: News
        }
      ],
      mode:'history'
    })
    复制代码

    mode:'history'参数可以去掉url中的#号

    App.vue

    复制代码
    <template>
      <div id="app">
        <div>
          <router-link to="/">首页</router-link>
          <router-link to="/course">课程</router-link>
          <router-link to="/micro">学位课</router-link>
          <router-link to="/news">深科技</router-link>
        </div>
        <router-view/>
      </div>
    </template>
    
    <script>
    export default {
      name: 'App'
    }
    </script>
    
    <style>
    
    </style>
    复制代码

    这样我们就可以在页面上通过点击来切换路由和组件了

    展示课程列表

    当我们点击course组件时应该能看到课程列表,所以在course组件中我们需要向后头发送ajax请求,获取课程的数据,并使用v-for显示到页面上

    course.vue

    复制代码
    <template>
      <div>
        <h1>{{msg}}</h1>
        <div v-for="item in courseList">
          <!--<router-link to="/detail">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>-->
          <!--<router-link :to="{path: '/detail/'+item.id }">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>-->
          <router-link :to="{ name:'detail',params:{cid:item.id} }">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>
        </div>
      </div>
    </template>
    
    <script>
      export default {
        name: "Course",
        data(){
          return {
            msg:'课程列表',
            courseList:[]
          }
        },
        mounted(){
          this.init()
        },
        methods:{
          init(){
            // 发送Ajax
            this.courseList = [
              {id:11,name:'21天学会Python',img: require('../assets/logo.png')},
              {id:21,name:'21天学会Java',img:require('../assets/logo.png')},
              {id:31,name:'21天学会Linux',img:require('../assets/logo.png')},
            ]
          }
        }
    
      }
    </script>
    
    <style scoped>
      .img{
         30px;
        height: 30px;
      }
    </style>
    复制代码

    这里我们先不发送ajax请求了,直接模拟了数据,在定义图片的src属性时,可以直接写上图片的路径,但是如果像上面那样使用绑定属性时,可能会看不到图片,这时需要使用require,img: require('../assets/logo.png'),这样就能正常看到图片了

    显示课程详细

    在course组件中我们通过v-for的方式展示了课程列表,现在我们需要通过点击某一门课程来看到它的详细信息,首先我们要在路由中定义一个Detail组件

    {
          path: '/detail/:cid',
          name: 'detail',
          component: Detail
        },

    由于需要某一门课的详细信息,所以需要知道这门课的id,在路径中我们在/detail后加了:cid来区分id,这里的cid可以换成其它的,在course中我们通过router-link来点击跳转

    复制代码
    <div v-for="item in courseList">
          <!--<router-link to="/detail">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>-->
          <!--<router-link :to="{path: '/detail/'+item.id }">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>-->
          <router-link :to="{ name:'detail',params:{cid:item.id} }">{{item.id}} {{item.name}} <img class="img" v-bind:src="item.img" alt=""></router-link>
        </div>
    复制代码

    这里的to属性我们可以通过绑定方法来定义:

    第一种方式是通过:to="{path: '/detail/'+item.id }",通过path再拼接id的形式来实现

    第二种方式是通过:to="{ name:'detail',params:{cid:item.id} }",通过name来确定url,并通过params来确定url的参数

    通过上面的两种方式我们都可以访问到Detail组件

    Detail.vue

    复制代码
    <template>
        <div>
          <h1>课程详细 - {{currentCourseId}}</h1>
          <ul>
            <li>课程名称: {{courseDetail.title}}</li>
            <li>课程简介: {{courseDetail.summary}}</li>
          </ul>
    
          <h1>推荐课程</h1>
          <ul v-for="item in courseDetail.recommends">
              <li v-on:click="recommendCourse(item.id)">{{item.name}}</li>
              <!--<router-link :to="{name:'detail',params:{cid:item.id}}">{{item.name}}</router-link>-->
          </ul>
        </div>
    </template>
    
    <script>
        export default {
            name: "detail",
          data(){
              return {
                currentCourseId:this.$route.params.cid,
                courseDetail:{
                  title: '',
                  summary:'',
                  recommends:[]
                }
              }
          },
          mounted(){
            this.init()
          },
          methods:{
              init(){
                // 根据当前ID发送Ajax请求,获取课程详细信息 this.currentCourseId
                this.courseDetail = {
                  title: '21天学会Python'+this.currentCourseId,
                  summary:'休想休想休想休想休想休想休想休想休想',
                  recommends:[
                    {id:100,name:'rest api'},
                    {id:200,name:'vue'}
                  ]
                }
              },
            recommendCourse(cid){
                // 主动将内容根据最新的课程ID进行更新
                this.currentCourseId = cid
                this.init()
                // URL进行重定向
                this.$router.push({name:'detail',params:{cid:cid}})
            }
          }
        }
    </script>
    
    <style scoped>
    
    </style>
    复制代码

    在详细页中我们可以通过this.$route.params.cid来获取url中的参数,也就是课程的id值,通过这个id我们可以向后端发送ajax请求来获取数据,并在页面上显示,在这里我们要注意一点,在课程详细中包含有推荐课程,点击推荐课程我们应该能看到推荐课程的详细

    信息,但是如果还使用上面的方法,也就是<router-link :to="{name:'detail',params:{cid:item.id}}">{{item.name}}</router-link>,由于都是在Detail组件中,所以这时点击它,页面是不会变化的,只能看到url的变化,所以在这里我们给推荐课程的标签绑定了一个事件,

    点击后会修改this.currentCourseId为推荐课程的id,然后再次进行初始化(发送ajax请求),这样就能获取到推荐课程的数据并显示到页面上,但是这样只是页面内容变了,url没有变,所以我们还要用this.$router.push({name:'detail',params:{cid:cid}})来进行url

    重定向

    相关命令

    this.$route.params  获取url中的参数
    this.$router.push()  url重定向,跳转

    利用vuex和vue-cookies实现用户登录、注销

    登录按钮应该在每个页面都能看到,所以我们将组件放到App.vue中,首先创建路由

    {
          path: '/login',
          name: 'login',
          component: Login
        }

    Login.vue

    复制代码
    <template>
        <div>
          <input type="text" v-model="username" placeholder="用户名">
          <input type="text" v-model="password" placeholder="密码">
          <input type="button" value="登录" v-on:click="doLogin"> {{error}}
        </div>
    </template>
    
    <script>
        export default {
            name: "Login",
          data(){
              return {
                username:'',
                password:'',
                error:''
              }
          },
          methods:{
              doLogin(){
                if(this.username === 'alex' && this.password === '123'){
                    this.$store.commit('saveToken',{username:'alex',token:'sdfsdfsdf'})
                    this.$router.push({name:'index'})
                }else{
                  this.error = '用户名或密码错误'
                }
    
              }
          }
        }
    </script>
    
    <style scoped>
    
    </style>
    复制代码

    在这个组件中我们通过v-model来进行数据的双向绑定,当用户输入了用户名和密码点击登录后,我们应该拿到数据,并发送给后端进行验证,这里我们在前端简单验证一下,就不发送后端了,当验证成功后我们跳转到index首页,验证失败则显示错误信息,这里我们

    其实还调用了$store中的一个方法,这个后面再说

    组件写完后我们就可以把他加到App.vue中了

    复制代码
    <template>
      <div id="app">
        <div>
          <router-link to="/">首页</router-link>
          <router-link to="/course">课程</router-link>
          <router-link to="/micro">学位课</router-link>
          <router-link to="/news">深科技</router-link>
    
          <div v-if="this.$store.state.username">
            <a>{{this.$store.state.username}}</a>
            <a v-on:click="doLogout">注销</a>
          </div>
          <div v-else>
            <router-link to="/login">登录</router-link>
          </div>
    
    
        </div>
        <router-view/>
      </div>
    </template>
    
    <script>
    export default {
      name: 'App',
      methods:{
        doLogout(){
            this.$store.commit('clearToken')
        }
      }
    }
    </script>
    
    <style>
    
    </style>
    复制代码

    这里我们可以看到在App.vue中做了一步判断,其实就是为了实现当用户没有登录时,可以看到登录按钮,如果用户登录了就能看到自己的用户名和注销按钮,要实现这个功能必须要我们的App.vue能使用到Login组件中的用户登录信息,所以这里我们使用vuex来实现

    首先导入vuex并生成store对象

    复制代码
    import Vue from 'vue'
    import Vuex from 'vuex'
    import Cookie from 'vue-cookies'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      // 组件中通过 this.$store.state.username 调用
      state: {
        username: Cookie.get('username'),
        token: Cookie.get('token'),
        apiList: {
          auth: 'http://127.0.0.1:8000/api/v1/auth/',
          courses: 'http://127.0.0.1:8000/api/v1/courses/',
          pricePolicy: 'http://127.0.0.1:8000/api/v1/price_policy/',
          shopCar: 'http://127.0.0.1:8000/api/v1/shop_car/',
        }
      },
      mutations: {
        // 组件中通过 this.$store.commit('saveToken',{username:'alex',token:'sdfsdfsdf'})  调用
        saveToken: function (state, userToken) {
          state.username = userToken.username;
          state.token = userToken.token;
    
          Cookie.set("username", userToken.username, "20min")
          Cookie.set("token", userToken.token, "20min")
    
        },
        clearToken: function (state) {
          state.username = undefined
          state.token = undefined
    
          Cookie.remove('username')
          Cookie.remove('token')
    
        }
      }
    })
    复制代码

    可以看到这里state中定义了username和token,当用户登录后我们应该将这两个信息保存,并写到cookie中,所以在mutations中我们定义了一个方法saveToken,当用户登录成后我们就可以调用这个方法来将username和token写到cookie中

    Cookie.set("username", userToken.username, "20min")
    Cookie.set("token", userToken.token, "20min")

    然后state中的username和token可以直接从cookie中获取

    username: Cookie.get('username'),
    token: Cookie.get('token'),

    注意,这里如果不从cookie中获取,而是写死的话,页面刷新后可能就取不到原来的值了,这样在App.vue中我们就可以判断state.username是否存在,来显示登录按钮或用户信息,当显示用户信息时,还有一个注销按钮,我们给它绑定一个点击事件,当点击时,执行

    mutations中的clearToken方法,注销cookie

    state.username = undefined
    state.token = undefined
    
    Cookie.remove('username')
    Cookie.remove('token')

    课程详细页面:tab切换

    在课程详细页中我们可以通过点击切换一些显示的内容,如下图所示

    这里我们可以给这些标签都绑定一个点击事件,然后通过v-show来实现

    复制代码
    <h1>tab切换</h1>
    <div class="tab-menu">
      <div>
        <a v-on:click="changeTab('detail')">课程概述</a>
        <a v-on:click="changeTab('chapter')">课程章节</a>
        <a v-on:click="changeTab('review')">用户评价</a>
        <a v-on:click="changeTab('question')">常见问题</a>
      </div>
    </div>
    
    <div>
      <div v-show="tabs.detail">课程概述内容</div>
      <div v-show="tabs.chapter">课程章节内容</div>
      <div v-show="tabs.review">用户评价内容</div>
      <div v-show="tabs.question">常见问题内容</div>
    </div>
    复制代码

    这里的详细内容也应该通过ajax请求从后台获取,这里我们直接写死了

    tabs数据

    tabs: {
      detail: true,
      chapter: false, //chapter
      review: false,
      question: false,
    },

    changeTab方法

    复制代码
       changeTab(name) {
         for (let item in this.tabs) {
           if (item === name) {
             this.tabs[item] = true
           } else {
             this.tabs[item] = false
           }
         }
     },
    复制代码

    点击某一个时就将其对应的tabs中的值变为true,其它的变为false,这样通过v-show就能将点击的内容显示出来

    我们用到的式样

    复制代码
    .tab-menu {
        border-bottom: 1px solid #ddd;
        padding-top: 30px;
        text-align: center;
      }
    
      .tab-menu a {
        display: inline-block;
        padding: 20px;
        border-bottom: 2px solid transparent;
        cursor: pointer;
      }
    
      .tab-menu a:hover {
        border-bottom: 2px solid darkseagreen;
      }
    复制代码

    价格策略效果

    在课程详细页中我们还应该显示页面的价格策略

    复制代码
    <h1>价格策略</h1>
          <ul class="price-policy">
            <li v-bind:class="[{active:num==selectCourseIndex} ]" v-on:click="choiceCourse(num)" v-for="(pri,num) in prices">¥{{pri.price}} (有效期 {{pri.period}} )
            </li>
          </ul>
    复制代码

    这里的prices数据我们自己模拟,就不存后端拿了,然后给每个标签绑定一个点击事件,点击时将selectCourseIndex的值改为当前标签的索引,这样当前的标签就能获得一个active的class属性

    复制代码
    prices: [
      {id: 11, price: 100, period: '2个月'},
      {id: 21, price: 200, period: '3个月'},
      {id: 31, price: 300, period: '4个月'},
    ]
    
    
    choiceCourse(index) {
      this.selectCourseIndex = index
    }
    复制代码

    active样式

    .price-policy .active {
        background-color: darkseagreen;
      }

    播放cc视频

    在cc视频上传了视频后cc视频会给我们一段html代码,我们只要把他复制到我们的页面上就可以了

    在课程详细页组件中

    复制代码
    <h1>视频描述</h1>
    <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
            codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0"
            width="400" height="300" v-bind:id="'cc_'+video_brief_link">
      <param name="movie"
             v-bind:value="'https://p.bokecc.com/flash/single/C2401FCB0F73D923_'+video_brief_link + 
      <param name="allowFullScreen" value="true"/>
      <param name="allowScriptAccess" value="always"/>
      <param value="transparent" name="wmode"/>
      <embed
        v-bind:src="'https://p.bokecc.com/flash/single/C2401FCB0F73D923_'+video_brief_link + '_false_35E7C8085202EBC3_1/player.swf'"
        width="400" height="300" name="cc_ECC9954677D8E1079C33DC5901307461" allowFullScreen="true"
        wmode="transparent" allowScriptAccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"
        type="application/x-shockwave-flash"/>
    </object>
    复制代码

    其中的video_brief_link参数就是cc提供的一段字符串,我们可以在data中定义

    video_brief_link: 'ECC9954677D8E1079C33DC5901307461'

    基于router的拦截器实现用户登录成功后才能访问页面

    router有一个功能类似于django中的中间件,可以在访问某一个url前写一些逻辑,这里我们用来实现一些页面的访问控制,对于一些页面,我们只给登录后的用户访问,未登录的则直接跳转到登录页面

    main.js

    复制代码
    router.beforeEach(function (to,from,next) {
    
        if(to.meta.requireAuth){
          // 需要登录的路由
          if (store.state.token) {
            // 已经登录
            next()
          } else {
            // 未登录
            next({
              name: 'login'
            })
          }
        }else{
          // 不需要登录的路由
          next()
        }
      }
    )
    复制代码

    router.beforeEach就是在访问url前会执行的方法,这里的function有3个参数,to from next,to表示要访问的url,from表示从哪个url来,next是接下来实际会访问的url,一般情况下next和to是一样的

    这里我们判断用户是否登录了,但是不能对每个页面都判断,只有一些特定的页面需要做这个判断,这时我们就可以在url中添加一些参数了

    复制代码
    {
          path: '/micro',
          name: 'micro',
          component: Micro,
           meta:{
            requireAuth:true,
          }
        },
    复制代码

    对于需要登录才能访问的url我们可以加一个meta参数,里面有一个requireAuth为true,在router.beforeEach我们就可以先判断to.meta.requireAuth,如果为true说明需要登录后才能访问,然后再判断token是否存在,存在表示已经登录,则直接next(),不存在则跳转到

    登录页面next({ name: 'login' })

    二级路由

    前面我们用的都是一级路由,现在我们来使用一次二级路由

    复制代码
    {
      path: '/help',
      name: 'help',
      component: Help,
      children: [
        {
          path: 'about-us',
          name: 'about-us',
          component: AboutUs
        },
        {
          path: 'user-note',
          name: 'user-note',
          component: UserNote
        },
        {
          path: 'feedback',
          name: 'feedback',
          component: Feedback
        }
      ]
    }
    复制代码

    首先先写一个一级路由help,然后加一个children属性,里面写二级路由,注意,这里二级路由的path前面不要加/

    写一级路由组件

    复制代码
    <template>
        <div>
            <h2>{{msg}}</h2>
            
            预留二级路由的位置
            <router-view></router-view>
        </div>
    </template>
    复制代码

    注意在一级路由组件中需要给二级路由预留位置<router-view></router-view>

    写二级路由

    <template>
        <div>
            <h2>{{msg}}</h2>
        </div>
    </template>

    使用

    <router-link to="/help/about-us">关于我们</router-link>
    <router-link to="/help/feedback">用户反馈</router-link>
    <router-link to="/help/user-note">使用手册</router-link>

    点击后就能看到在页面上有一级路由组件的内容,并且内容中还包含有二级路由组件的内容

    访问登录页面时,添加backurl

    为了提高用户体验,当我们访问登录页面登录成功后,应该跳转回之前的页面,而不应该总是跳转到首页,我们可以通过在访问login的url时添加backurl参数来实现

    首先我们要修改登录按钮的to属性

    <router-link v-else :to="{name:'login',query:{backUrl: this.$route.path}}">登录</router-link>

    这里我们增加了一个query属性,这个属性就是在url后加?k1=v1这类的参数的,然后我们通过this.$route.path取到当前页的url,并添加到backUrl参数中

    在登录组件中,登录成功后我们要取到backUrl属性并判断

    复制代码
    methods: {
      doLogin() {
        if (this.username === 'oldboy' && this.password === '123') {
          let obj = {username: 'oldboy', token: 'jsudfnksdjwe8234sdfsdkf'}
          this.$store.commit('saveToken', obj)
          let backUrl = this.$route.query.backUrl
          if (backUrl) {
            this.$router.push({path: backUrl})
          } else {
            this.$router.push('/index')
          }
        } else {
          this.error = '用户名或秘密错误'
        }
      }
    复制代码

    通过this.$route.query取到url中的参数backUrl,并判断是否存在,如果存在则使用this.$router.push({path: backUrl})跳转回原来的页面,不存在则跳转首页

    在router拦截器中我们也应该实现这个功能

    复制代码
    router.beforeEach(function (to, from, next) {
      if (to.meta.requireAuth) {
        if (store.state.token) {
          next()
        } else {
          next({
            path: '/login',
            query: {backUrl: to.fullPath}
          })
        }
      } else {
        next()
      }
    
    })
    复制代码

    当要跳转登录页面时需要加backUrl参数

  • 相关阅读:
    2060: [Usaco2010 Nov]Visiting Cows 拜访奶牛
    2020: [Usaco2010 Jan]Buying Feed, II
    3396: [Usaco2009 Jan]Total flow 水流
    3403: [Usaco2009 Open]Cow Line 直线上的牛
    2102: [Usaco2010 Dec]The Trough Game
    最小生成树——Kruskal算法
    最短路径——Floyd算法(含证明)
    最短路径——Bellman-Ford算法以及SPFA算法
    最短路径——Dijkstra算法以及二叉堆优化(含证明)
    普通并查集
  • 原文地址:https://www.cnblogs.com/xyhh/p/10861235.html
Copyright © 2020-2023  润新知