心有阳光xian-yun

Posted smart-girl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了心有阳光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-width:1000px;
    }

    .footer{
        padding-top:30px;
        margin:0 auto;
        width: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{
            width: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;
  width: 1000px;
  min-width: 1000px;
  background: #fff;
  border-bottom: 1px solid #eee;
  box-shadow: 0, 3px, 3px, #ccc;
  margin-bottom: 20px;
}
.row-bg {
  // margin: 0,auto;
  width: 1000px;
  min-width: 1000px;
  .logo img {
    width: 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 {
    width: 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 {
      width: 225px;
      height: 140px;
      position: relative;
      overflow: hidden;

      img {
        width: 100%;
      }

      .layer-bar {
        position: absolute;
        bottom: 0;
        left: 0;
        background: rgba(0, 0, 0, 0.5);
        color: #fff;
        height: 30px;
        line-height: 30px;
        width: 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 {
  width: 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="width: 100%;" @change="handleDate"
           :picker-options="pickerOptions">></el-date-picker>
      </el-form-item>

      <!-- 提交按钮 -->
      <el-form-item label>
        <el-button style="width: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;
  width: 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 {
    width: 100%;
  }
}

.reverse {
  position: absolute;
  top: 35px;
  right: 15px;

  &:after,
  &:before {
    display: block;
    content: "";
    position: absolute;
    left: -35px;
    width: 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;
    width: 20px;
    height: 20px;
    line-height: 18px;
    text-align: center;
    border-radius: 2px;
    cursor: pointer;

    &:after,
    &:before {
      display: block;
      content: "";
      position: absolute;
      left: 10px;
      width: 1px;
      height: 20px;
      background: #ccc;
    }

    &:after {
      top: -20px;
    }

    &:before {
      top: 20px;
    }
  }
}
</style>

里面还有一些内容

































以上是关于心有阳光xian-yun的主要内容,如果未能解决你的问题,请参考以下文章

心有猛虎react-pxq

广阔天地始于心Django150中国沿海骑行

技巧阳光网络课堂助手

心有猛虎react-lesson

web之路上,要心有猛虎,强者才能见温柔,轮询以矫正

python3 requests + BeautifulSoup 爬取阳光网投诉贴详情实例代码