心有阳光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的主要内容,如果未能解决你的问题,请参考以下文章