zoukankan      html  css  js  c++  java
  • 【心有阳光】xian-yun

    公司有个同事用Nuxt写了个项目,我一想,我还没有用Nuxt写过项目呢
    刚把得,我也要会用nuxt写项目
    看了一下同事项目的效果,他说是用的element-ui,然后是样式覆盖
    ok了
    本宝宝也要会这个
    接下来看下github上面的项目
    先放下作者大大的github地址:https://github.com/zhangliang1013/xian-yun
    我们看下项目运行的效果

    接下来我们看下代码
    看了下package.json应该主要是用的nuxt.js和element-ui

    //package.json
    {
      "name": "xianyun",
      "version": "1.0.0",
      "description": "My supreme Nuxt.js project",
      "author": "liang_ge",
      "private": true,
      "scripts": {
        "dev": "nuxt",
        "build": "nuxt build",
        "start": "nuxt start",
        "generate": "nuxt generate"
      },
      "dependencies": {
        "@nuxtjs/axios": "^5.3.6",
        "element-ui": "^2.4.11",
        "less": "^3.10.3",
        "less-loader": "^5.0.0",
        "moment": "^2.24.0",
        "nuxt": "^2.0.0",
        "vuex-persistedstate": "^2.7.1"
      },
      "devDependencies": {}
    }
    
    


    看项目目录我还比较懵逼的,找不到入口文件,就结合界面UI来看
    目测layout应该是入口

    header和footer是共用的东西
    先看footer,footer中的内容比较简单和固定

    //footer.vue
    <template>
        <div class="footer-wrapper">
            <div class="footer">
                <el-row class="info-list">
                    <el-col :span="6" :offset="1">
                        <h5>闲云旅游旅游网</h5>
                        <p>上亿旅行者共同打造的"旅行神器"</p>
                        <p><span>60,000</span> 多个全球旅游目的地</p>
                        <p><span>600,000</span> 个细分目的地新玩法</p>
                        <p><span>760,000,000</span> 次攻略下载</p>
                        <p><span>38,000</span> 家旅游产品供应商</p>
                    </el-col>
                     <el-col :span="5">
                        <h5>关于我们</h5>
                        <p>隐私政策 商标声明</p>
                        <p>服务协议 游记协议</p>
                        <p>商城平台服务协议</p>
                        <p>网络信息侵权通知指引</p>
                        <p>闲云旅游旅游网服务监督员</p>
                        <p>网站地图加入闲云旅游</p>
                    </el-col>
                    <el-col :span="5">
                        <h5>旅行服务</h5>
                        <p>旅游攻略 酒店预订</p>
                        <p>旅游特价 国际租车</p>
                        <p>旅游问答 旅游保险</p>
                        <p>旅游指南 订火车票</p>
                        <p>旅游资讯 APP下载</p>
                    </el-col>
                    <el-col :span="6" class="scan">
                        <p>
                            <img src="http://157.122.54.189:9093/images/1556522965.png" alt="">
                        </p>
                        关注我们
                    </el-col>
                </el-row>
    
                <div class="licence">
                    京ICP备08001421号 京公网安备110108007702 Copyright © 2016-2019 博学谷 All Rights Reserved
                </div>
            </div>
        </div>
    </template>
    
    <script>
    export default {
        
    }
    </script>
    
    <style scoped lang="less">
        .footer-wrapper{
            background:#333;
            color:#ccc;
            min-1000px;
        }
    
        .footer{
            padding-top:30px;
            margin:0 auto;
            1000px;
        }
    
        .info-list{
            h5{
                font-weight: normal;
                font-size:16px;
                margin-bottom:10px;
            }
    
            p{
                font-size:12px;
                line-height: 1.8;
                span{
                    color:orange;
                }
            }
        }
    
        .scan{
            text-align: center;
    
            img{
                140px;
                height:140px;;
            }
    
            font-size:12px;
        }
    
        .licence{
            border-top:1px #666 solid;
            margin-top:20px;
            padding:50px 0;
            text-align: center;
            font-size:12px;
        }
    </style>
    

    header里面的效果我有想关注的,比如点击变色或者下面出现一条横线那种效果,然后对应页面渲染的

    接下来我们仔细看看header中作者是怎么写的

    不过这个作者写的,额,跟我想的不一样
    header组件中无特殊处理,hover的时候下面出现一条横线那种

    //header.vue
    <template>
      <div class="header">
        <!-- logo部分 -->
        <el-row type="flex" class="row-bg" justify="space-between" align="middle">
          <div class="logo">
            <img src="http://157.122.54.189:9093/images/logo.jpg" alt />
          </div>
          <!-- 导航栏部分 -->
          <el-row type="flex" class="navs">
            <nuxt-link to="/">首页</nuxt-link>
            <nuxt-link to="/post">旅游攻略</nuxt-link>
            <nuxt-link to="/hotel">酒店</nuxt-link>
            <nuxt-link to="/air">国内机票</nuxt-link>
          </el-row>
          <!-- 登录部分 -->
          <div class="login">
            <span>消息</span>
    
            <!-- 如果用户存在则展示用户信息,用户数据来自store -->
            <el-dropdown v-if="$store.state.user.userInfo.token">
              <el-row type="flex" align="middle" class="el-dropdown-link">
                <nuxt-link to="#">
                  <img :src="$axios.defaults.baseURL + $store.state.user.userInfo.user.defaultAvatar" />
                  {{ $store.state.user.userInfo.user.nickname}}
                </nuxt-link>
                <i class="el-icon-caret-bottom el-icon--right"></i>
              </el-row>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item>
                  <nuxt-link to="#">个人中心</nuxt-link>
                </el-dropdown-item>
                <el-dropdown-item>
                  <div @click="handleLogout">退出</div>
                </el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
            <!-- 没登录状态展示的消息 -->
            <nuxt-link to="/user/login" v-else>登录/注册</nuxt-link>
          </div>
        </el-row>
      </div>
    </template>
    
    <script>
    export default {
      methods: {
        handleLogout() {
          this.$store.commit('user/getData',{
            token : '',
            user : {}
          })
          if(this.$store.state.user.userInfo.token == ''){
            this.$message.warning('退出成功!')
          }
        }
      }
    };
    </script>
    
    <style lang='less' scoped>
    .header {
      margin: 0 auto;
      height: 60px;
      line-height: 60px;
       1000px;
      min- 1000px;
      background: #fff;
      border-bottom: 1px solid #eee;
      box-shadow: 0, 3px, 3px, #ccc;
      margin-bottom: 20px;
    }
    .row-bg {
      // margin: 0,auto;
       1000px;
      min- 1000px;
      .logo img {
         156px;
        height: 42px;
        display: block;
      }
      .navs {
        flex: 1;
        a {
          height: 57px;
          display: block;
          padding: 0 20px;
          margin-left: 20px;
          box-sizing: border-box;
          &:hover {
            color: #409eff;
            border-bottom: 3px solid #409eff;
            //    background-color: #409eff;
          }
        }
        .nuxt-link-exact-active {
          background-color: #409eff;
          color: #fff;
          &:hover {
            color: #fff;
          }
        }
      }
      .login {
        span {
          margin-right: 30px;
        }
      }
    }
    .el-dropdown-link {
      margin-left: 20px;
    
      &:hover {
        img {
          border-color: #409eff;
        }
      }
    
      a {
        display: block;
      }
    
      img {
         32px;
        height: 32px;
        vertical-align: middle;
        border: 2px #fff solid;
        border-radius: 50px;
      }
    }
    
    .account-link {
      font-size: 14px;
      margin-left: 10px;
      color: #666;
    
      &:hover {
        color: #409eff;
        text-decoration: underline;
      }
    }
    </style>
    

    我们看下这个页面

    哈哈,我以为会有什么特殊的处理,原来就是这一句话啊

    说明我上面的推测是错的

    这个是控制的样式
    这个直接点击,其实是直接进入到了组件里面,不过比较奇怪的事情是居然不用在一个大的目录里面写router.js写好之类的
    接下来看这个页面


    接下来是这个页面

    air页面里面的东西才比较多,我们可以看下代码
    index页面里面的代码也一般

    //index
    <template>
      <section class="container">
        <h2 class="air-title">
          <span class="iconfont iconfeiji"></span>
          <i>国内机票</i>
        </h2>
    
        <!-- 搜索广告栏 -->
        <el-row type="flex" justify="space-between">
          <!-- 搜索表单 -->
    
          <!-- 组件展示 -->
          <SearchForm></SearchForm>
    
          <!-- banner广告 -->
          <div class="sale-banner">
            <img src="http://157.122.54.189:9093/images/pic_sale.jpeg" />
          </div>
        </el-row>
    
        <!-- 广告 -->
        <el-row type="flex" class="statement">
          <el-col :span="8">
            <i class="iconfont iconweibiaoti-_huabanfuben" style="color:#409EFF;"></i>
            <span>100%航协认证</span>
          </el-col>
          <el-col :span="8">
            <i class="iconfont iconbaozheng" style="color:green;"></i>
            <span>出行保证</span>
          </el-col>
          <el-col :span="8">
            <i class="iconfont icondianhua" style="color:#409EFF;"></i>
            <span>7x24小时服务</span>
          </el-col>
        </el-row>
    
        <h2 class="air-sale-title">
          <span class="iconfont icontejiajipiao"></span>
          <i>特价机票</i>
        </h2>
    
      
        <!-- 特价机票 -->
        <div class="air-sale">
          <el-row type="flex" class="air-sale-pic" justify="space-between">
            <el-col :span="6" v-for="(item, index) in sales" :key="index">
              <nuxt-link
                :to="`/air/flights?departCity=${item.departCity}&departCode=${item.departCode}&destCity=${item.destCity}&destCode=${item.destCode}&departDate=${item.departDate}`"
              >
                <img :src="item.cover" />
                <el-row class="layer-bar" type="flex" justify="space-between">
                  <span>{{item.departCity}}-{{item.destCity}}</span>
                  <span>¥{{item.price}}</span>
                </el-row>
              </nuxt-link>
            </el-col>
          </el-row>
        </div>
      </section>
    </template>
    
    <script>
    import SearchForm from "@/components/air/SearchForm";
    export default {
      components: {
        SearchForm
      },
      data () {
        return {
          sales : []
        }
      },
      mounted(){
        this.$axios({
          url : '/airs/sale'
        }).then(res =>{
          //  console.log(res)
          const {data} = res.data
          this.sales = data
        })
      }
    };
    </script>
    
    <style scoped lang="less">
    .air-sale {
      border: 1px #ddd solid;
      padding: 20px;
      margin-bottom: 50px;
    
      .air-sale-pic {
        > div {
           225px;
          height: 140px;
          position: relative;
          overflow: hidden;
    
          img {
             100%;
          }
    
          .layer-bar {
            position: absolute;
            bottom: 0;
            left: 0;
            background: rgba(0, 0, 0, 0.5);
            color: #fff;
            height: 30px;
            line-height: 30px;
             100%;
            box-sizing: border-box;
            padding: 0 15px;
            font-size: 14px;
    
            span:last-child {
              font-size: 18px;
            }
          }
        }
      }
    }
    
    .air-sale-group {
      margin-top: 20px;
      padding-top: 8px;
      border-right: 1px #eee solid;
    
      &:last-child {
        border-right: none;
      }
    
      .air-sale-row {
        font-size: 12px;
        color: #666;
        margin-bottom: 8px;
    
        .air-sale-price {
          color: orange;
          font-size: 20px;
        }
      }
    }
    
    .container {
       1000px;
      margin: 0 auto;
    }
    
    .air-title {
      margin: 15px 0;
      font-size: 20px;
      font-weight: normal;
      color: orange;
    
      span {
        font-size: 20px;
      }
    }
    
    .statement {
      margin: 15px 0;
      border: 1px #ddd solid;
      background: #f5f5f5;
      height: 58px;
      padding: 10px 0;
      box-sizing: border-box;
    
      > div {
        text-align: center;
        line-height: 38px;
        border-right: 1px #ddd solid;
    
        &:last-child {
          border-right: none;
        }
    
        * {
          vertical-align: middle;
        }
    
        i {
          font-size: 30px;
        }
      }
    }
    
    .air-sale-title {
      margin: 15px 0;
      font-size: 20px;
      font-weight: normal;
      color: #409eff;
    
      span {
        font-size: 20px;
      }
    }
    </style>
    
    //search组件
    <template>
      <div class="search-form">
        <!-- 头部tab切换 -->
        <el-row type="flex" class="search-tab">
          <span
            v-for="(item, index) in tabs"
            :key="index"
            @click="handleSearchTab(item, index)"
            :class="{active: index === currentTab}"
          >
            <i :class="item.icon"></i>
            {{item.name}}
          </span>
        </el-row>
    
        <el-form class="search-form-content" ref="form" label-width="80px">
          <!-- 出发城市输入框 -->
          <el-form-item label="出发城市">
            <!-- fetch-suggestions 返回输入建议的方法 -->
            <!-- select 点击选中建议项时触发 -->
            <el-autocomplete
              v-model="form.departCity"
              :fetch-suggestions="queryDepartSearch"
              placeholder="请搜索出发城市"
              @select="handleDepartSelect"
              @blur="handleDepartCity"
              class="el-autocomplete"
            ></el-autocomplete>
          </el-form-item>
    
          <!-- 到达城市输入框 -->
          <el-form-item label="到达城市">
            <el-autocomplete
              v-model="form.destCity"
              :fetch-suggestions="queryDestSearch"
              placeholder="请搜索到达城市"
              @select="handleDestSelect"
              @blur="handleDestCity"
              class="el-autocomplete"
            ></el-autocomplete>
          </el-form-item>
    
          <!-- 出发时间 -->
          <el-form-item label="出发时间">
            <!-- change 用户确认选择日期时触发 -->
            <el-date-picker type="date" placeholder="请选择日期"
            v-model="form.departData"
             style=" 100%;" @change="handleDate"
               :picker-options="pickerOptions">></el-date-picker>
          </el-form-item>
    
          <!-- 提交按钮 -->
          <el-form-item label>
            <el-button style="100%;" type="primary" icon="el-icon-search" @click="handleSubmit">搜索</el-button>
          </el-form-item>
    
          <!-- 换字点击事件 -->
          <div class="reverse">
            <span @click="handleReverse">换</span>
          </div>
        </el-form>
      </div>
    </template>
    
    <script>
    import moment from "moment";
    export default {
      data() {
        return {
          // 控制日期选择
           pickerOptions: {
              disabledDate(time) {
                return time.getTime() + 3600 * 1000 * 24 < Date.now();
              }
           },
          tabs: [
            { icon: "iconfont icondancheng", name: "单程" },
            { icon: "iconfont iconshuangxiang", name: "往返" }
          ],
          currentTab: 0,
          // 表单的字段
          form: {
            departCity: "",
            departCode: "",
            destCity: "",
            destCode: "",
            departDate: ""
          },
          // 出发城市列表
          departData: [],
          // 到达城市列表
          destData: []
        };
      },
      methods: {
        // tab切换时触发
        handleSearchTab(item, index) {
          if (index === 1) {
            this.$alert("暂时不支持往返操作!", "温馨提示", {
              confirmButtonText: "确定",
              type: "warning"
            });
          }
        },
        
         //封装出发和到达城市value变化的函数
        // querySearch(value){
        //  return    this.$axios({
        //     url: "/cities",
        //     params: {
        //       name: value
        //     }
        //   }).then(res => {
        //     console.log(res)
        //     const { data } = res.data;
        //     const newData = data.map(v => {
        //       v.value = v.name.replace("市", "");
        //       return v;
        //     });
        //     return newData
        //   })
        // },
        // 出发城市输入框获得焦点时触发
        // value 是选中的值,callback是回调函数,接收要展示的列表
        queryDepartSearch(value, callback) {
          if (value === "") {
            callback([]);
            return;
          }
      //  没封装之前的函数
          // this.$axios({
          //   url: "/cities",
          //   params: {
          //     name: value
          //   }
          // }).then(res => {
          //   // console.log(res)
          //   const { data } = res.data;
          //   const nameData = data.map(v => {
          //     v.value = v.name.replace("市", "");
          //     return v;
          //   });
          // });
    
              //函数的封装方式
          //  this.querySearch(value).then(newData => {
          //    this.departData = newData;
          //     callback(newData);
          //  })
         
          // vuex的封装方式
          this.$store.dispatch('user/querySearch',value).then(newData =>{
            this.departData = newData;
            callback(newData);
          })
        },
    
        // 出发城市下拉选择时触发
        handleDepartSelect(item) {
          this.form.departCity = item.value;
          this.form.departCode = item.sort
        },
    
       //出发城市市区焦点是触发
       handleDepartCity(){
    
         if(this.form.departCity === ''){
           this.departData = [];
         }
    
          if(this.departData.length === 0){
              return;
          }
          this.form.departCity = this.departData[0].value;
          this.form.departCode = this.departData[0].sort;
       },
    
        // 目标城市输入框获得焦点时触发
        // value 是选中的值,callback是回调函数,接收要展示的列表
        queryDestSearch(value, callback) {
          if(!value){
            callback([]);
              return;
          }
        //  没封装之前的函数
          // this.$axios({
          //     url :'/cities',
          //     params : {
          //         name : value
          //     }
          // }).then(res =>{
          //     const {data} = res.data
          //     const newData = data.map( v =>{
          //         v.value = v.name.replace('市','');
          //         return v;
          //     })
          //     this.destData = newData;
          //     callback(newData)
          // })
    
          //  函数封装的方式
          // this.querySearch(value).then(newData =>{
          //    this.destData = newData;
          //     callback(newData)
          // }) 
    
          // vuex中store中封装异步操作
          this.$store.dispatch('user/querySearch',value).then(newData =>{
            this.destData = newData;
             callback(newData)
          })
        },
        // 目标城市下拉选择时触发
        handleDestSelect(item) {
            this.form.destCity = item.value;
            this.form.destCode = item.sort;
        },
        
        // 到达城市失去焦点触发
        handleDestCity(){
         if(this.form.destCity === ''){
           this.destData = []
         }
    
          if(this.destData.length=== 0){
              return;
          }
           this.form.destCity = this.destData[0].value;
            this.form.destCode = this.destData[0].sort;
        },
        // 确认选择日期时触发
        handleDate(value) {
            this.form.departDate = moment(value).format("YYYY-MM-DD")
        },
    
        // 触发和目标城市切换时触发
        handleReverse() {
            const {departCity,departCode,destCity, destCode} = this.form
    
            this.form.departCity = destCity;
            this.form.departCode = destCode
    
            this.form.destCity = departCity; 
            this.form.destCode = departCode;
    
            // console.log(123)
            // console.log(this.form)
        },
    
        // 提交表单是触发
        handleSubmit() {
            // console.log(this.form)
            if(this.form.departCity.trim().length === 0){
                this.$message.error('出发城市不能为空!')
                return;
            }
            if(this.form.destCity.trim().length === 0){
                this.$message.error('到达城市不能为空!')
                return;
            }
            if(this.form.departDate.trim().length === 0){
                this.$message.error('出发日期不能为空!')
                return;
            }
            
            
            this.$store.commit('air/getAirList',this.form)
            // console.log(this.form)
    
            this.$message.success('机票搜索成功!')
            this.$router.push({
                path : '/air/flights',
                query : this.form
            })
        }
      }
    };
    </script>
    
    <style scoped lang="less">
    .search-form {
      border: 1px #ddd solid;
      border-top: none;
       360px;
      height: 350px;
      box-sizing: border-box;
    }
    
    .search-tab {
      span {
        display: block;
        flex: 1;
        text-align: center;
        height: 48px;
        line-height: 42px;
        box-sizing: border-box;
        border-top: 3px #eee solid;
        background: #eee;
      }
    
      .active {
        border-top-color: orange;
        background: #fff;
      }
    
      i {
        margin-right: 5px;
        font-size: 18px;
    
        &:first-child {
          font-size: 16px;
        }
      }
    }
    
    .search-form-content {
      padding: 15px 50px 15px 15px;
      position: relative;
    
      .el-autocomplete {
         100%;
      }
    }
    
    .reverse {
      position: absolute;
      top: 35px;
      right: 15px;
    
      &:after,
      &:before {
        display: block;
        content: "";
        position: absolute;
        left: -35px;
         25px;
        height: 1px;
        background: #ccc;
      }
    
      &:after {
        top: 0;
      }
    
      &:before {
        top: 60px;
      }
    
      span {
        display: block;
        position: absolute;
        top: 20px;
        right: 0;
        font-size: 12px;
        background: #999;
        color: #fff;
         20px;
        height: 20px;
        line-height: 18px;
        text-align: center;
        border-radius: 2px;
        cursor: pointer;
    
        &:after,
        &:before {
          display: block;
          content: "";
          position: absolute;
          left: 10px;
           1px;
          height: 20px;
          background: #ccc;
        }
    
        &:after {
          top: -20px;
        }
    
        &:before {
          top: 20px;
        }
      }
    }
    </style>
    
    

    里面还有一些内容

  • 相关阅读:
    USB
    Google
    机型参数
    mac
    反编译
    xcode
    Ios 消息推送
    真机:特殊
    Android
    object-c
  • 原文地址:https://www.cnblogs.com/smart-girl/p/13111136.html
Copyright © 2011-2022 走看看