写完这一篇vue仿豆瓣项目就完结了,在这个项目中有一个问题一直没解决,就是图片会丢失一部分,报错403,现在还没找到解决方法,如果有谁知道怎么解决或者知道原因,请告诉我。
项目地址是:GitHub - Ercyao/VUE-douban: 仿豆瓣vue项目
首页效果图
首页首页home.vue,当数据加载完loading组件显示,通过修改日期beforeDate(),获取到this.next_date的值,然后再次请求数据,将数据添加到AllData数组中,由此实现加载更多数据
<template>
<div class="page">
<headerNav></headerNav>
<div v-show="!showLoading">
<ul class="quick-nav">
<li><a href="https://m.douban.com/movie/nowintheater?loc_id=108288">影院热映</a></li>
<li><a :href="QuickUrl">{{QuickName}}</a></li>
<li><a id="hot-topics" href="https://m.douban.com/time/?dt_time_source=douban-msite_shortcut">豆瓣时间</a></li>
<li><a href="https://www.douban.com/doubanapp/app?channel=card_home&direct_dl=1">使用豆瓣App</a></li>
</ul>
<div class="recommend-box">
<div v-for="item in AllData" :key="item.id">
<p class="time">{{item.time}}</p>
<ul>
<li v-for="feed in item.data" :key="feed.id">
<a :href="feed.target.url">
<div class="recommend-content">
<div class="left">
<p class="title">{{feed.title}}</p>
<p class="desc">{{feed.target.desc}}</p>
</div>
<div class="right">
<img :src="feed.target.cover_url"/>
</div>
</div>
<p class="author"><span>by {{feed.target.author.name}}</span><span>{{feed.source_cn}}</span></p>
</a>
</li>
</ul>
</div>
</div>
<div class="load-more" @click="loaderMore">
<span v-show="downFlag === false">点击加载更多</span>
<span class="active" v-show="downFlag === true">加载中…</span>
</div>
<drown-app></drown-app>
</div>
<loading v-show="showLoading"></loading>
</div>
</template>
<script>
import loading from '@/components/loading'
import headerNav from '@/components/header'
import drownApp from '@/components/drownapp'
import {getBookData,getHotTopic,getFilmLive,getRecTopic,getRecommend,getQuickData} from '@/store/API'
export default{
data(){
return {
isShow:false,
showLoading:true,
downFlag:false,
next_date:'',
AllData:[],
Recommend:null,
QuickData:null,
QuickName:null,
QuickUrl:null,
}
},
components: {
headerNav,
drownApp,
loading,
},
created(){
this.getBookData();
this.beforeDate();
},
methods: {
async getBookData(){
let Recommend = await getRecommend(this.next_date).then(res => res.json());
this.Recommend = Recommend.recommend_feeds;
this.next_date = Recommend.date;
this.AllData.push({time: this.next_date, data: this.Recommend});
this.QuickData = await getQuickData().then(res => res.json());
let i = Math.floor(Math.random()*10);
this.QuickName= this.QuickData[i].name;
this.QuickUrl= this.QuickData[i].url;
this.hideLoading();
},
//加载更多
async loaderMore(){
this.downFlag=true;
this.beforeDate();
let Recommend = await getRecommend(this.next_date).then(res => res.json());
this.Recommend = Recommend.recommend_feeds;
this.AllData.push({time: this.next_date, data: this.Recommend});
},
changeRecData(){
this.rec_start = parseInt(10*Math.random());
this.getBookData();
},
hideLoading(){
this.showLoading = false;
},
beforeDate(){
let d = this.next_date;
d = new Date(d);
d = +d - 1000*60*60*24;
d = new Date(d);
this.next_date = d.getFullYear()+"-"+(d.getMonth()+1)+"-"+d.getDate();
}
}
}
</script>
<style lang="less" scoped>
@import '../../assets/style/less.less';
.quick-nav {
overflow: hidden;
padding: .5rem 0 2rem;
li {
float: left;
width: 50%;
padding: 5px;
box-sizing: border-box;
font-size: 15px;
a {
background-color: #f6f6f6;
color: #494949;
display: block;
text-align: center;
height: 3rem;
line-height: 3rem;
border-radius: 2px;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
}
}
}
.recommend-box{
.time{
width: 100%;
height: 4rem;
line-height: 4rem;
text-align: center;
font-size: 18px;
color: #111;
.border(0,.1rem,0,0);
}
ul{
li{
padding: 2rem 0;
.border(0,.1rem,0,0);
}
}
.author{
display: flex;
justify-content: space-between;
font-size: 12px;
color: #CCCCCC;
padding-top: 1rem;
}
}
.recommend-content{
display: -webkit-flex;
display: flex;
width: 100%;
overflow: hidden;
.left{
-webkit-flex: 2;
flex: 2;
.title{
.ellipsis(2);
text-align: justify;
font-size: 17px;
font-weight: 500;
line-height: 1.41;
color: #494949;
margin-bottom: 6px;
};
.desc{
.ellipsis(3);
text-align: justify;
color: #aaa;
font-size: 12px;
line-height: 1.5;
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
};
}
.right{
height: 7rem;
overflow: hidden;
img{
width: 5rem;
padding-left: 20%;
}
}
}
.load-more {
margin: 1rem 20%;
height: 3rem;
line-height: 3rem;
text-align: center;
color: #fff;
span{
background: #aaa;
width: 100%;
height: 100%;
}
.active{
opacity: 0.5;
}
}
</style>
电影的效果图
电影电影movie.vue,因为电影页和图书页样式相似,所以将写成公共组件columnBox.vue,根据type值来使用哪一种,:items是绑定要渲染的数据
<template>
<div class="page">
<headerNav></headerNav>
<div v-show="!showLoading">
<column-box title="影院热映" type="ImgCover" :items="MovieShowing"></column-box>
<column-box title="免费在线观影" type="ImgCover" :items="MovieFreeStream"></column-box>
<column-box title="新片速递" type="ImgCover" :items="MovieLatest"></column-box>
<column-box title="发现好电影" type="TextCover" :items="movieInterests"></column-box>
<Category :items="MovieClass"></Category>
<drown-app></drown-app>
</div>
<loading v-show="showLoading"></loading>
</div>
</template>
<script>
import columnBox from '@/components/column'
import Category from '@/components/Category'
import loading from '@/components/loading'
import headerNav from '@/components/header'
import drownApp from '@/components/drownapp'
import {getMovieShowing,getMovieFreeStream,getMovieLatest,getInterestsData,getMovieClass} from '@/store/API'
export default{
data(){
return {
showLoading:true,
MovieShowing:null,
MovieFreeStream:null,
MovieLatest:null,
movieInterests:null,
MovieClass:null,
}
},
components: {
headerNav,
drownApp,
loading,
Category,
columnBox,
},
created(){
this.getMovieData();
},
methods: {
async getMovieData(){
//影院热映
let MovieShowing = await getMovieShowing().then(res => res.json());
this.MovieShowing = MovieShowing.subject_collection_items;
//免费在线观影
let MovieFreeStream = await getMovieFreeStream().then(res => res.json());
this.MovieFreeStream = MovieFreeStream.subject_collection_items;
//新片速递
let MovieLatest = await getMovieLatest().then(res => res.json());
this.MovieLatest = MovieLatest.subject_collection_items;
//发现好电影
let InterestsData = await getInterestsData().then(res => res.json());
this.movieInterests = InterestsData[0].movie;
//分类浏览
this.MovieClass = await getMovieClass().then(res => res.json());
this.hideLoading();
},
hideLoading(){
this.showLoading = false;
}
}
}
</script>
<style>
</style>
图书的效果图
图书图书books.vue
<template>
<div class="page">
<headerNav></headerNav>
<div v-show="!showLoading">
<column-box title="最受关注图书 | 虚构类" type="ImgCover" :items="BookFiction"></column-box>
<column-box title="最受关注图书 | 非虚构类" type="ImgCover" :items="BookNoFiction"></column-box>
<column-box title="豆瓣书店" type="ImgCover" :items="ProductBook">
<div class="promItem" slot="promItem">
<a :href="ProductBookHUrl">
<img :src="ProductBookHImg" class="cover">
<div class="content">
<p><span class="name">{{ProductBookHTitle}}</span><span class="price">¥ {{ProductBookHPrice}}</span></p>
<p class="info">{{ProductBookHInfo}}</p>
</div>
</a>
</div>
</column-box>
<column-box title="发现好图书" type="TextCover" :items="booksInterests"></column-box>
<Category :items="BookClass"></Category>
<drown-app></drown-app>
</div>
<loading v-show="showLoading"></loading>
</div>
</template>
<script>
import columnBox from '@/components/column'
import Category from '@/components/Category'
import loading from '@/components/loading'
import headerNav from '@/components/header'
import drownApp from '@/components/drownapp'
import {getBookFiction,getBookNoFiction,getProductBook,getInterestsData,getBookClass} from '@/store/API'
export default{
data(){
return {
showLoading:true,
BookFiction:null,
BookNoFiction:null,
ProductBook:null,
ProductBookHUrl:null,
ProductBookHImg:null,
ProductBookHPrice:null,
ProductBookHTitle:null,
ProductBookHInfo:null,
booksInterests:null,
BookClass:null,
}
},
components: {
headerNav,
drownApp,
loading,
Category,
columnBox,
},
created(){
this.getMovieData();
},
methods: {
async getMovieData(){
//最受关注图书 | 虚构类
let BookFiction = await getBookFiction().then(res => res.json());
this.BookFiction = BookFiction.subject_collection_items;
//最受关注图书 | 非虚构类
let BookNoFiction = await getBookNoFiction().then(res => res.json());
this.BookNoFiction = BookNoFiction.subject_collection_items;
//豆瓣书店
let ProductBook = await getProductBook().then(res => res.json());
this.ProductBook = ProductBook.subject_collection_items;
let ProductBookHeader= ProductBook.header;
this.ProductBookHUrl = ProductBookHeader.url;
this.ProductBookHImg = ProductBookHeader.cover.url;
this.ProductBookHPrice = ProductBookHeader.price;
this.ProductBookHTitle = ProductBookHeader.title;
this.ProductBookHInfo = ProductBookHeader.info;
//发现好电影
let InterestsData = await getInterestsData().then(res => res.json());
this.booksInterests = InterestsData[0].books;
//分类浏览
this.BookClass = await getBookClass().then(res => res.json());
this.hideLoading();
},
hideLoading(){
this.showLoading = false;
}
}
}
</script>
<style lang="less" scoped>
.promItem{
display: block;
overflow: hidden;
margin-top: 15px;
.cover {
float: left;
width: 100px;
margin-right: 15px;
}
.content {
overflow: hidden;
.name {
font-size: 19px;
color: #494949;
margin-right: 40px;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
}
.price {
float: right;
color: #E76648;
font-size: 16px;
line-height: 22px;
}
.info {
color: #AAAAAA;
margin-top: .8rem;
font-size: .9rem;
line-height: 1.4rem;
overflow:hidden;
text-overflow:ellipsis;
display:-webkit-box;
-webkit-box-orient:vertical;
-webkit-line-clamp:4;
}
}
}
</style>
广播的效果图
广播广播broadcast.vue,根据值来显示不同样式
<template>
<div class="page">
<headerNav></headerNav>
<div v-show="!showLoading">
<div class="broadcast-box">
<ul>
<li v-for=" item in Broadcast">
<a :href="item.status.sharing_url">
<div class="broadcast-con">
<div class="left"><img src="../../assets/img/logo.png"/></div>
<div class="right">
<p class="author"><span class="blod">{{item.status.author.name}}</span><span class="gray">{{item.status.activity}}</span></p>
<p class="gray">{{item.status.create_time}}</p>
<template v-if="item.status.text" >
<div class="said_des">{{item.status.text}}</div>
<div class="said_img">
<template v-for="img in item.status.images">
<template v-if="img.normal.height == 460" >
<img :src="img.normal.url" class="smallimg"/>
</template>
<template v-else>
<img :src="img.normal.url" class="bigimg"/>
</template>
</template>
</div>
</template>
<template v-else>
<div class="diary_des">
<p class="diary_title">{{item.status.card.title}}</p>
<div class="diary_content">
<div class="diary_text">{{item.status.card.subtitle}}</div>
<div class="diary_img">
<template v-if="item.status.card.image">
<img :src="item.status.card.image.normal.url"/>
</template>
</div>
</div>
</div>
</template>
</div>
</div>
</a>
</li>
</ul>
</div>
<div class="list-link">显示更多广播</div>
<drown-app></drown-app>
</div>
<loading v-show="showLoading"></loading>
</div>
</template>
<script>
import loading from '@/components/loading'
import headerNav from '@/components/header'
import drownApp from '@/components/drownapp'
import {getBroadcast} from '@/store/API'
export default{
data(){
return {
showLoading:true,
Broadcast:null
}
},
components: {
headerNav,
drownApp,
loading,
},
created(){
this.getMovieData();
},
methods: {
async getMovieData(){
let Broadcast = await getBroadcast().then(res => res.json());
this.Broadcast = Broadcast.items;
this.hideLoading();
},
hideLoading(){
this.showLoading = false;
}
}
}
</script>
<style lang="less" scoped>
.broadcast-box{
ul{
li{
margin: 1rem 0 1.5rem;
}
}
.broadcast-con{
display: flex;
justify-content: space-between;
.left{
width: 40px;
margin-right: 10px;
img{width: 35px;border-radius: 50%;}
}
.right{
width: 97%;
color: #000000;
padding-bottom: 30px;
border-bottom: .1rem solid #E8E8E8;
.author{font-size: 1.3rem;line-height: 2.2rem;}
.blod{font-weight: 600;margin-right: 5px;}
.said_des{
margin-top: 10px;
line-height: 22px;
color: #494949;
}
.said_img{
max-height: 300px;
overflow: hidden;
.smallimg{
width: 32.8%;
height: 6.5em;
margin-right: 0.1em;
margin-bottom: 0.1em;
float: left;
}
.smallimg:after{clear: both;}
.bigimg{
margin-top: 15px;
width: 99%;
}
}
.diary_des{
padding: 15px;
margin: 10px 0 20px;
border-radius: 2px;
background: #f9f9f9;
.diary_title{
font-size: 17px;
font-weight: 500;
line-height: 1.4;
color: #494949;
margin-bottom: 5px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.diary_content{
display: flex;
.diary_text{
flex: 2;
font-size: 12px;
line-height: 16px;
color: #aaa;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
.diary_img{
height: 48px;
overflow: hidden;
img{
width: 75px;
margin-left: 10px;
}
}
}
}
}
.gray{color: #AAAAAA;}
}
}
.list-link{
color: #42bd56;
display: block;
padding: 15px 0;
font-size: 16px;
line-height: 18px;
text-align: center;
cursor: pointer;
}
</style>
小组的效果图
小组小组group.vue
<template>
<div class="page">
<headerNav></headerNav>
<div v-show="!showLoading">
<template v-for="item in Groups">
<team-box :title="item.name" type="groupsCover" :items="item.groups"></team-box>
</template>
<drown-app></drown-app>
</div>
<loading v-show="showLoading"></loading>
</div>
</template>
<script>
import teamBox from '@/components/team'
import loading from '@/components/loading'
import headerNav from '@/components/header'
import drownApp from '@/components/drownapp'
import {getGroups} from '@/store/API'
export default{
data(){
return {
showLoading:true,
Groups:[],
MovieFreeStream:null,
MovieLatest:null,
movieInterests:null,
MovieClass:null,
Groups1:null,
Groups2:null,
Groups3:null,
Groups4:null,
}
},
components: {
headerNav,
drownApp,
loading,
teamBox,
},
created(){
this.getGroupData();
},
methods: {
async getGroupData(){
let Groups = await getGroups().then(res => res.json());
this.Groups = Groups.rec_groups[0].classified_groups;
this.hideLoading();
},
hideLoading(){
this.showLoading = false;
}
}
}
</script>
<style>
</style>
搜索结果的效果图
搜索结果搜索结果search.vue,
<template>
<div class="search_box">
<headerNav></headerNav>
<div class="search_input mtop">
<input type="text" v-model.trim="keyword" placeholder="请输入关键字" @keyup.enter="SearchResult()"/>
<span class="search" @click="SearchResult()">搜索</span>
</div>
<div class="search_team">
<team-box title="音乐" type="searchCover" :items="MusicRoot"></team-box>
<team-box title="读书" type="searchCover" :items="BookRoot"></team-box>
<team-box title="影视" type="searchCover" :items="MovieRoot"></team-box>
</div>
</div>
</template>
<script>
import teamBox from '@/components/team'
import headerNav from '@/components/header'
import {getMusicRoot,getBookRoot,getMovieRoot} from '@/store/API'
export default{
data(){
return {
keyword:'',
SearchClass:null,
MusicRoot:null,
BookRoot:null,
MovieRoot:null,
}
},
components: {
headerNav,
teamBox,
},
created(){
this.getSearchData();
},
methods: {
async getSearchData(){
this.keyword = this.$route.query.q;
if(this.keyword){
let MusicRoot = await getMusicRoot(this.keyword).then(res => res.json());
this.MusicRoot = MusicRoot.musics;
let BookRoot = await getBookRoot(this.keyword).then(res => res.json());
this.BookRoot = BookRoot.books;
let MovieRoot = await getMovieRoot(this.keyword).then(res => res.json());
this.MovieRoot = MovieRoot.subjects;
}
},
SearchResult(){
this.getSearchData();
this.ShowSearchClass = false;
}
}
}
</script>
<style lang="less">
.search_box{
.mtop{margin-top: 3.5rem;}
.search_team{
padding: 0 5%;
}
.search_input{
width: 100%;
height: 2.5rem;
padding: 15px;
border-bottom: 1px solid #f3f3f3;
input{
float: left;
width: 75%;
height: 2.5rem;
font-size: 14px;
background: #F1F1F1;
margin-right: 10px;
border: 0;
border-radius: 5px;
padding:0 10px;
}
.search{
font-size: 16px;
margin-top: 5px;
}
}
}
</style>
网友评论