Authored by 张文文

社区详情页面 review by lea.guo

@@ -24,12 +24,12 @@ @@ -24,12 +24,12 @@
24 </template> 24 </template>
25 25
26 <script> 26 <script>
27 -import { get } from "lodash";  
28 -import videojs from "video.js";  
29 -import { getArticleImageSize, processImage } from "utils/image-handler"; 27 +import { get } from 'lodash';
  28 +import videojs from 'video.js';
  29 +import { getArticleImageSize, processImage } from 'utils/image-handler';
30 30
31 export default { 31 export default {
32 - name: "VideoPlayer", 32 + name: 'VideoPlayer',
33 props: { 33 props: {
34 source: String, 34 source: String,
35 cover: String, 35 cover: String,
@@ -41,7 +41,7 @@ export default { @@ -41,7 +41,7 @@ export default {
41 return { 41 return {
42 muted: true, 42 muted: true,
43 controls: true, 43 controls: true,
44 - aspectRatio: "1:1" 44 + aspectRatio: '1:1'
45 }; 45 };
46 } 46 }
47 } 47 }
@@ -68,45 +68,45 @@ export default { @@ -68,45 +68,45 @@ export default {
68 2, 68 2,
69 imgSize.width, 69 imgSize.width,
70 imgSize.height, 70 imgSize.height,
71 - get(this.yoho, "window.supportWebp") 71 + get(this.yoho, 'window.supportWebp')
72 ); 72 );
73 } else { 73 } else {
74 - return this.cover.split("?")[0]; 74 + return this.cover.split('?')[0];
75 } 75 }
76 } else { 76 } else {
77 - return ""; 77 + return '';
78 } 78 }
79 }, 79 },
80 sourceType() { 80 sourceType() {
81 - let type = "video/mp4"; 81 + let type = 'video/mp4';
82 82
83 if (this.source) { 83 if (this.source) {
84 - let source = this.source.split("?")[0]; 84 + let source = this.source.split('?')[0];
85 85
86 - source = source.split("."); 86 + source = source.split('.');
87 87
88 switch (source[source.length - 1]) { 88 switch (source[source.length - 1]) {
89 - case "opus":  
90 - case "ogv":  
91 - type = "video/ogg"; 89 + case 'opus':
  90 + case 'ogv':
  91 + type = 'video/ogg';
92 break; 92 break;
93 - case "mkv":  
94 - type = "video/x-matroska"; 93 + case 'mkv':
  94 + type = 'video/x-matroska';
95 break; 95 break;
96 - case "m3u8":  
97 - type = "application/x-mpegURL"; 96 + case 'm3u8':
  97 + type = 'application/x-mpegURL';
98 break; 98 break;
99 - case "m4a":  
100 - type = "audio/mp4"; 99 + case 'm4a':
  100 + type = 'audio/mp4';
101 break; 101 break;
102 - case "mp3":  
103 - type = "audio/mpeg"; 102 + case 'mp3':
  103 + type = 'audio/mpeg';
104 break; 104 break;
105 - case "aac":  
106 - type = "audio/aac"; 105 + case 'aac':
  106 + type = 'audio/aac';
107 break; 107 break;
108 - case "oga":  
109 - type = "audio/ogg"; 108 + case 'oga':
  109 + type = 'audio/ogg';
110 break; 110 break;
111 default: 111 default:
112 break; 112 break;
@@ -131,7 +131,6 @@ export default { @@ -131,7 +131,6 @@ export default {
131 }, 131 },
132 methods: { 132 methods: {
133 async parentHandleclick() { 133 async parentHandleclick() {
134 - // await this.delay(1000);  
135 this.player.play(); 134 this.player.play();
136 135
137 const timeId = setTimeout(() => { 136 const timeId = setTimeout(() => {
@@ -155,37 +154,37 @@ export default { @@ -155,37 +154,37 @@ export default {
155 }); 154 });
156 }, 155 },
157 initPlayer() { 156 initPlayer() {
158 - const noVioceClass = "vjs-yoho-novoice"; 157 + const noVioceClass = 'vjs-yoho-novoice';
159 158
160 this.player = videojs(this.$refs.videoPlayer, this.options); 159 this.player = videojs(this.$refs.videoPlayer, this.options);
161 160
162 - this.voiceBtn = this.player.addChild("button");  
163 - this.voiceBtn.addClass("vjs-yoho-voice"); 161 + this.voiceBtn = this.player.addChild('button');
  162 + this.voiceBtn.addClass('vjs-yoho-voice');
164 this.voiceBtn.addClass(noVioceClass); 163 this.voiceBtn.addClass(noVioceClass);
165 - this.backBtn = this.player.addChild("button");  
166 - this.backBtn.addClass("vjs-yoho-back");  
167 - this.voiceBtn.on("touchend", () => { 164 + this.backBtn = this.player.addChild('button');
  165 + this.backBtn.addClass('vjs-yoho-back');
  166 + this.voiceBtn.on('touchend', () => {
168 this.player.muted(!this.voiceBtn.hasClass(noVioceClass)); 167 this.player.muted(!this.voiceBtn.hasClass(noVioceClass));
169 }); 168 });
170 - this.backBtn.on("touchend", () => { 169 + this.backBtn.on('touchend', () => {
171 this.player.exitFullscreen(); 170 this.player.exitFullscreen();
172 }); 171 });
173 172
174 - this.player.on("play", () => { 173 + this.player.on('play', () => {
175 this.player._yohoPlayTime = this.getTime(); 174 this.player._yohoPlayTime = this.getTime();
176 }); 175 });
177 - this.player.on("pause", () => { 176 + this.player.on('pause', () => {
178 this.player._yohoPauseTime = this.getTime(); 177 this.player._yohoPauseTime = this.getTime();
179 }); 178 });
180 - this.player.on("ended", () => { 179 + this.player.on('ended', () => {
181 this.player._yohoEndedTime = this.getTime(); 180 this.player._yohoEndedTime = this.getTime();
182 }); 181 });
183 182
184 - this.player.on("fullscreenchange", () => { 183 + this.player.on('fullscreenchange', () => {
185 this.player._yohoPlayTime = this.getTime(); 184 this.player._yohoPlayTime = this.getTime();
186 }); 185 });
187 186
188 - this.player.on("volumechange", () => { 187 + this.player.on('volumechange', () => {
189 const soundOff = this.player.muted() || this.player.volume() === 0; 188 const soundOff = this.player.muted() || this.player.volume() === 0;
190 189
191 if (soundOff) { 190 if (soundOff) {
@@ -205,7 +204,7 @@ export default { @@ -205,7 +204,7 @@ export default {
205 <style lang="scss" scoped> 204 <style lang="scss" scoped>
206 .video-js { 205 .video-js {
207 width: 100%; 206 width: 100%;
208 - height: auto; 207 + height: 750px;
209 208
210 video { 209 video {
211 width: 100%; 210 width: 100%;
  1 +<template>
  2 + <div class="avatar-wrap">
  3 + <ImageFormat class="img-avatar" :lazy="true" :src="imageSrc" :width="width" :height="height" @error="errorHandle"></ImageFormat>
  4 + </div>
  5 +</template>
  6 +
  7 +<script>
  8 +export default {
  9 + name: 'WidgetAvatar',
  10 + props: {
  11 + src: {
  12 + type: String,
  13 + default: ''
  14 + },
  15 + width: {
  16 + type: Number,
  17 + default: 35
  18 + },
  19 + height: {
  20 + type: Number,
  21 + default: 35
  22 + },
  23 + lazy: {
  24 + type: Boolean,
  25 + default: false
  26 + },
  27 + small: {
  28 + type: Boolean,
  29 + default: false
  30 + },
  31 + group: [Number, String]
  32 + },
  33 + data() {
  34 + return {
  35 + imgSrc: this.src,
  36 + defaultSrc: 'https://img12.static.yhbimg.com/article/2019/02/26/16/02456ade977d8dfdbc4ca548b196c1d62b.png?imageView/2/w/{width}/h/{height}'
  37 + };
  38 + },
  39 + computed: {
  40 + imageSrc() {
  41 + return this.imgSrc || this.defaultSrc;
  42 + }
  43 + },
  44 + watch: {
  45 + src(val) {
  46 + this.imgSrc = val;
  47 + }
  48 + },
  49 + methods: {
  50 + errorHandle() {
  51 + this.imgSrc = this.defaultSrc;
  52 + }
  53 + }
  54 +};
  55 +</script>
  56 +
  57 +<style lang="css" scoped>
  58 +.avatar-wrap {
  59 + display: inline-block;
  60 + position: relative;
  61 +
  62 + > img {
  63 + width: 100%;
  64 + height: 100%;
  65 + }
  66 +}
  67 +
  68 +.img-avatar {
  69 + border-radius: 50%;
  70 + overflow: hidden;
  71 +}
  72 +</style>
  1 +<template>
  2 + <LayoutApp :show-back="true" title="社区详情">
  3 + <div class="detail-container">
  4 +
  5 + <div class="author-container">
  6 + <ArticleAuthor :userHeadIco="userHeadIco" :userName="userName"></ArticleAuthor>
  7 + </div>
  8 +
  9 + <div class="source-container">
  10 + <ArticleVideo v-if="showVideo" :videoUrl="videoUrl" :coverUrl="coverUrl"></ArticleVideo>
  11 + <ArticleImage v-else :imageList="imageList"></ArticleImage>
  12 + </div>
  13 +
  14 + <div class="slide-container">
  15 + <HorizontalSlide :value="imageList"></HorizontalSlide>
  16 + </div>
  17 +
  18 + <div class="note-container">
  19 + <div class="note-header">Nike 旗下大热鞋款 Air Max 95</div>
  20 + <div class="note-content">Nike 旗下大热鞋款 Air Max 95 一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型 @NIKE官方 Max 95 也轻松成为许多鞋迷的心头好紧接「Triple White」之后,备受期待的 Nike 全新鞋款 SF-AF1 Mid 又有一双鞋的「Tiger Camo」配色率先现身网络。</div>
  21 + <div class="note-date">2019-01-02</div>
  22 + <div class="praise-wrapper" ></div>
  23 + </div>
  24 +
  25 + <div class="recommend-container">
  26 + <div class="recommend-text">推荐阅读</div>
  27 + <div class="recommend-list">list</div>
  28 + </div>
  29 +
  30 + </div>
  31 + </LayoutApp>
  32 +</template>
  33 +
  34 +<script>
  35 +import ArticleAuthor from './components/article-author';
  36 +import ArticleVideo from './components/article-video';
  37 +import ArticleImage from './components/article-image';
  38 +import HorizontalSlide from './components/horizontalSlide';
  39 +
  40 +export default {
  41 + name: 'articleDetail',
  42 + components: {
  43 + ArticleAuthor,
  44 + ArticleVideo,
  45 + ArticleImage,
  46 + HorizontalSlide
  47 + },
  48 + data() {
  49 + return {
  50 + imageList: [
  51 + {image_url: 'http://img11.static.yhbimg.com/goodsimg/2019/07/02/16/0191aa70fd1f46d75e5ff974dfe0cdac1c.jpg?imageMogr2/thumbnail/{width}x{height}/background/d2hpdGU=/position/center/quality/80'},
  52 + {image_url: 'http://img11.static.yhbimg.com/goodsimg/2018/11/06/14/01bbabe3eb9597d5a36f51a0a29c23e441.jpg?imageMogr2/thumbnail/{width}x{height}/background/d2hpdGU=/position/center/quality/80'},
  53 + {image_url: 'http://img11.static.yhbimg.com/goodsimg/2018/11/06/14/016c59b642d25a92049003c318ea7b97ff.jpg?imageMogr2/thumbnail/{width}x{height}/background/d2hpdGU=/position/center/quality/80'},
  54 + {image_url: 'http://img11.static.yhbimg.com/goodsimg/2018/11/06/14/012df38fe6117ea9467a01919a4b4c2f3c.jpg?imageMogr2/thumbnail/{width}x{height}/background/d2hpdGU=/position/center/quality/80'}
  55 + ],
  56 + showVideo: false,
  57 + videoUrl: 'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4',
  58 + coverUrl: 'http://file02.16sucai.com/d/file/2015/0408/779334da99e40adb587d0ba715eca102.jpg',
  59 + userHeadIco: 'https://img12.static.yhbimg.com/article/2019/02/26/16/02456ade977d8dfdbc4ca548b196c1d62b.png?imageView/2/w/{width}/h/{height}',
  60 + userName: 'MOMO草'
  61 + };
  62 + },
  63 +};
  64 +</script>
  65 +
  66 +<style lang="scss" scoped>
  67 +.detail-container {
  68 + background: #fff;
  69 +}
  70 +
  71 +.author-container {
  72 + width: 100%;
  73 + height: 100px;
  74 +}
  75 +
  76 +.source-container {
  77 + width: 100%;
  78 + height: 750px;
  79 +}
  80 +
  81 +.slide-container {
  82 + padding: 40px 0 36px 24px;
  83 +}
  84 +
  85 +.note-container {
  86 + padding: 0 24px 100px;
  87 + background: #fff;
  88 +}
  89 +
  90 +.note-header {
  91 + font-size: 36px;
  92 + color: #222;
  93 + letter-spacing: 0.34px;
  94 + line-height: 56px;
  95 + font-weight: bold;
  96 +}
  97 +
  98 +.note-content {
  99 + font-size: 30px;
  100 + color: #000;
  101 + letter-spacing: 0.28px;
  102 + line-height: 50px;
  103 +}
  104 +
  105 +.note-date {
  106 + margin-top: 20px;
  107 + font-size: 30px;
  108 + color: #999;
  109 + letter-spacing: 0.28px;
  110 +}
  111 +
  112 +.praise-wrapper {
  113 + margin-top: 60px;
  114 + margin-left: calc((100%-160px)/2);
  115 + height: 160px;
  116 + width: 160px;
  117 + background: url("~statics/image/coupon/used@3x.png");
  118 + background-size: contain;
  119 + background-repeat: no-repeat;
  120 +}
  121 +
  122 +.recommend-container {
  123 + background: #F2F2F2;
  124 + padding: 36px 24px 0;
  125 +}
  126 +
  127 +.recommend-text {
  128 + font-size: 32px;
  129 + color: #222222;
  130 + font-weight: bold;
  131 + text-align: left;
  132 +}
  133 +
  134 +.recommend-list {
  135 + margin-top: 30px;
  136 +}
  137 +
  138 +</style>
  1 +<template>
  2 + <div class="container">
  3 + <WidgetAvatar :lazy="true" class="widget-avatar" :src="userHeadIco" :width="70" :height="70"></WidgetAvatar>
  4 + <span>{{userName}}</span>
  5 + </div>
  6 +</template>
  7 +
  8 +<script>
  9 +import WidgetAvatar from '../../../components/widget-avatar';
  10 +
  11 +export default {
  12 + name: 'articleAuthor',
  13 + components: {
  14 + WidgetAvatar
  15 + },
  16 + props: {
  17 + userHeadIco: String,
  18 + userName: String
  19 + }
  20 +};
  21 +</script>
  22 +
  23 +<style lang="scss" scoped>
  24 +.container {
  25 + height: 100px;
  26 + padding-left: 24px;
  27 + display: flex;
  28 + flex-direction: row;
  29 + align-items: center;
  30 +
  31 + .widget-avatar {
  32 + width: 70px;
  33 + height: 70px;
  34 + }
  35 +
  36 + span {
  37 + margin-left: 20px;
  38 + font-size: 24px;
  39 + color: #222;
  40 + letter-spacing: 0.05px;
  41 + }
  42 +}
  43 +</style>
  1 +<template>
  2 + <div class="cover-slide">
  3 + <cube-slide ref="slide" :loop="false" :auto-play="false" :data="imageList">
  4 + <cube-slide-item v-for="(item, index) in imageList" :key="index" @click.native="clickHandler(item, index)">
  5 + <a click="javascript:void 0" class="square-img-container">
  6 + <SquareImg :src="item.image_url" :width="600" :height="600"/>
  7 + </a>
  8 + </cube-slide-item>
  9 + <template slot="dots" slot-scope="props">
  10 + <div class="dot-wrap">
  11 + {{props.current + 1}}/{{imageList.length}}
  12 + </div>
  13 + </template>
  14 + </cube-slide>
  15 + </div>
  16 +</template>
  17 +
  18 +<script>
  19 +import { Slide } from 'cube-ui';
  20 +import SquareImg from '../../product/components/square-img';
  21 +
  22 +export default {
  23 + name: 'articleImage',
  24 + components: {
  25 + 'cube-slide': Slide,
  26 + 'cube-slide-item': Slide.Item,
  27 + SquareImg
  28 + },
  29 + props: {
  30 + imageList: Array
  31 + },
  32 + data() {
  33 + return {
  34 + };
  35 + },
  36 + methods: {
  37 + clickHandler(item, index) {
  38 + console.log(item, index);
  39 + }
  40 + }
  41 +};
  42 +</script>
  43 +
  44 +<style lang="scss" scoped>
  45 +.cover-slide {
  46 + flex: 1;
  47 +
  48 + .square-img-container {
  49 + display: block;
  50 + width: 100%;
  51 + height: 750px;
  52 + margin: 0 auto;
  53 + }
  54 +
  55 + .dot-wrap {
  56 + margin-left: 24px;
  57 + margin-bottom: 24px;
  58 + background: #000;
  59 + opacity: 0.5;
  60 + width: 80px;
  61 + height: 48px;
  62 + border-radius: 30px;
  63 + color: white;
  64 + font-size: 24px;
  65 + letter-spacing: 1.96px;
  66 + line-height: 48px;
  67 + text-align: center;
  68 + }
  69 +}
  70 +
  71 +.cube-slide-item {
  72 + img {
  73 + margin: 0 auto;
  74 + }
  75 +}
  76 +
  77 +</style>
  1 +<template>
  2 + <div class="video-container">
  3 + <VideoPlayer
  4 + ref="videoPlayer"
  5 + class="videoWrapper"
  6 + :source="videoUrl"
  7 + :cover="coverUrl"
  8 + ></VideoPlayer>
  9 + </div>
  10 +</template>
  11 +
  12 +<script>
  13 +import VideoPlayer from '@/components/video-player';
  14 +
  15 +export default {
  16 + name: 'article-video',
  17 + props: {
  18 + videoUrl: String,
  19 + coverUrl: String
  20 + },
  21 + data() {
  22 + return {
  23 + };
  24 + },
  25 + components: {
  26 + VideoPlayer
  27 + }
  28 +};
  29 +</script>
  30 +
  31 +<style scoped>
  32 +.video-container {
  33 + flex: 1;
  34 +}
  35 +
  36 +.videoWrapper {
  37 + display: block;
  38 + width: 100%;
  39 + height: 750px;
  40 +}
  41 +
  42 +</style>
@@ -27,7 +27,6 @@ export default { @@ -27,7 +27,6 @@ export default {
27 data() { 27 data() {
28 return {}; 28 return {};
29 }, 29 },
30 -  
31 methods: { 30 methods: {
32 imageformatError() { 31 imageformatError() {
33 console.log(6666); 32 console.log(6666);
@@ -35,8 +34,9 @@ export default { @@ -35,8 +34,9 @@ export default {
35 }, 34 },
36 computed: {}, 35 computed: {},
37 components: {} 36 components: {}
38 -} 37 +};
39 </script> 38 </script>
  39 +
40 <style lang="scss" scoped> 40 <style lang="scss" scoped>
41 .horizontalSlide { 41 .horizontalSlide {
42 overflow: hidden; 42 overflow: hidden;
@@ -45,17 +45,20 @@ export default { @@ -45,17 +45,20 @@ export default {
45 -webkit-overscroll-behavior-x: contain; 45 -webkit-overscroll-behavior-x: contain;
46 scroll-behavior: smooth; 46 scroll-behavior: smooth;
47 } 47 }
  48 +
48 .list-warp { 49 .list-warp {
49 overflow-x: scroll; 50 overflow-x: scroll;
50 width: max-content; 51 width: max-content;
51 font-size: 0; 52 font-size: 0;
52 } 53 }
  54 +
53 .list-item { 55 .list-item {
54 display: inline-flex; 56 display: inline-flex;
55 width: 600px; 57 width: 600px;
56 height: 180px; 58 height: 180px;
57 margin-right: 24px; 59 margin-right: 24px;
58 border-radius: 8px; 60 border-radius: 8px;
  61 +
59 .image { 62 .image {
60 min-width: 136px; 63 min-width: 136px;
61 min-height: 180px; 64 min-height: 180px;
@@ -64,6 +67,7 @@ export default { @@ -64,6 +67,7 @@ export default {
64 border-radius: 8px 0 0 8px; 67 border-radius: 8px 0 0 8px;
65 filter: contrast(0.9); 68 filter: contrast(0.9);
66 } 69 }
  70 +
67 .title { 71 .title {
68 font-size: 24px; 72 font-size: 24px;
69 color: #999; 73 color: #999;
@@ -75,11 +79,13 @@ export default { @@ -75,11 +79,13 @@ export default {
75 -webkit-line-clamp: 2; 79 -webkit-line-clamp: 2;
76 -webkit-box-orient: vertical; 80 -webkit-box-orient: vertical;
77 } 81 }
  82 +
78 .bottom { 83 .bottom {
79 display: flex; 84 display: flex;
80 align-items: center; 85 align-items: center;
81 justify-content: space-between; 86 justify-content: space-between;
82 } 87 }
  88 +
83 .info { 89 .info {
84 padding: 20px; 90 padding: 20px;
85 display: flex; 91 display: flex;
@@ -88,6 +94,7 @@ export default { @@ -88,6 +94,7 @@ export default {
88 border: 1px solid #F0F0F0; 94 border: 1px solid #F0F0F0;
89 border-radius: 0 8px 8px 0; 95 border-radius: 0 8px 8px 0;
90 } 96 }
  97 +
91 .price { 98 .price {
92 margin-left: 16px; 99 margin-left: 16px;
93 font-size: 28px; 100 font-size: 28px;
@@ -95,6 +102,7 @@ export default { @@ -95,6 +102,7 @@ export default {
95 vertical-align: middle; 102 vertical-align: middle;
96 flex: 1 1 auto; 103 flex: 1 1 auto;
97 } 104 }
  105 +
98 .icon-goumai { 106 .icon-goumai {
99 float: right; 107 float: right;
100 } 108 }
@@ -7,11 +7,6 @@ export default [ @@ -7,11 +7,6 @@ export default [
7 { 7 {
8 name: 'ArticleDetail', 8 name: 'ArticleDetail',
9 path: '/xianyu/article/detail', 9 path: '/xianyu/article/detail',
10 - // component: () => import()  
11 - },  
12 - {  
13 - name: 'waterfall',  
14 - path: '/xianyu/waterfallTest',  
15 - component: () => import('./waterfall')  
16 - }, 10 + component: () => import(/* webpackChunkName: "articleDetail" */ './articleDetail')
  11 + }
17 ]; 12 ];
1 -<!--  
2 - * @description:  
3 - * @fileName: waterfall.vue  
4 - * @author: huzhiming  
5 - * @date: 2019-12-18 15:23:55  
6 - * @后台人员:  
7 - * @version: v1.0.0  
8 - * @path: 页面访问路径及参数说明  
9 -!-->  
10 -<template>  
11 - <div class="article-waterfall-wrap">  
12 - <horizontalSlide v-model="items" :style="{'margin': '20px 0 0 12px'}"></horizontalSlide>  
13 - <!-- <div class="water">  
14 - <div class="piping" ref="piping0"></div>  
15 - <div class="piping" ref="piping1"></div>  
16 - <div class="piping" ref="piping2"></div>  
17 - <div class="piping" ref="piping3"></div>  
18 - </div> -->  
19 - </div>  
20 -</template>  
21 -  
22 -<script>  
23 -import axios from 'axios';  
24 -  
25 -import horizontalSlide from './components/horizontalSlide'  
26 -  
27 -export default {  
28 - name: 'Article',  
29 - mixins: [],  
30 - props: {},  
31 - // 服务端渲染函数  
32 - async asyncData ({ isDev, route, store, env, params, query, req, res, redirect, error }) {  
33 - return {}  
34 - },  
35 - data() {  
36 - return {  
37 - items: [...Array(20).keys()].map(item => item),  
38 -  
39 - moments: [],  
40 - available: 1,  
41 - height1: 0,  
42 - height2: 0,  
43 - height3: 0,  
44 - page: 1  
45 - }  
46 - },  
47 - created() {  
48 - // 获取第一页数据  
49 - // this.fetchMoments();  
50 - },  
51 - mounted() {  
52 - // 用来监听滚轮  
53 - window.addEventListener("scroll", this.handleScroll);  
54 - },  
55 - activated() {},  
56 - deactivated() {},  
57 - // beforeRouteEnter (to, from, next) {},  
58 - // beforeRouteUpdate(to, from, next) {},  
59 - // beforeRouteLeave(to, from, next) {},  
60 - destroyed() {},  
61 - methods: {  
62 - fetchMoments() {  
63 - // 请求接口方法  
64 - // const result = await this.$api.post('https://m.yohobuy.com/grass/api/grass/topicRelatedArticles', {  
65 - // page,  
66 - // limit: limit || 10,  
67 - // type: 6,  
68 - // topicId: 2713  
69 - // });  
70 -  
71 - axios.get('https://m.yohobuy.com/grass/api/grass/topicRelatedArticles')  
72 - .then(function (response) {  
73 - console.log(response);  
74 - })  
75 - .catch(function (error) {  
76 - console.log(error);  
77 - });  
78 -  
79 -  
80 - // https://m.yohobuy.com/grass/api/grass/topicRelatedArticles  
81 - },  
82 - // sort()函数是递归的,因为要确保每个卡片的图片加载完成后再获取管道的高度,但是图片加载完成的函数是个异步函数,  
83 - // 如果放在for循环中会打乱顺序,因此要使异步函数同步执行,for循环改为递归。  
84 - sort(j) {  
85 - if (j < this.moments.length) {  
86 - let that = this;  
87 - // 创建Image类  
88 - var newImg = new Image();  
89 - // 获取要加载的图片地址  
90 - newImg.src =  
91 - "http://lanyue.ink:8123/images/" +  
92 - (Math.floor(Math.random() * 15) + 1) +  
93 - ".png";  
94 - // 图片加载完成后(异步)  
95 - newImg.onload = () => {  
96 - // 四个管道的高度  
97 - var arr = [  
98 - that.$refs.piping0.offsetHeight,  
99 - that.$refs.piping1.offsetHeight,  
100 - that.$refs.piping2.offsetHeight,  
101 - that.$refs.piping3.offsetHeight  
102 - ];  
103 - //获取管道最小高度  
104 - var min = arr.indexOf(Math.min.apply(Math, arr));  
105 - // 添加卡片的模板  
106 - var html =  
107 - `<div class="card">  
108 - <img src=` + newImg.src + `>  
109 - <div>  
110 - <img src="http://lanyue.ink:8123/images/avatar.jpg" alt="">  
111 - <div>` + this.moments[j].id + " " + this.moments[j].content + `</div>  
112 - </div>  
113 - </div>`;  
114 - //给最小的管道添加卡片  
115 - if (min == 0) {  
116 - that.$refs.piping0.innerHTML += html;  
117 - } else if (min == 1) {  
118 - that.$refs.piping1.innerHTML += html;  
119 - } else if (min == 2) {  
120 - that.$refs.piping2.innerHTML += html;  
121 - } else if (min == 3) {  
122 - that.$refs.piping3.innerHTML += html;  
123 - }  
124 - that.sort(j + 1);  
125 - };  
126 - }  
127 - },  
128 - handleScroll() {  
129 - // 获取滚轮位置  
130 - var scrollTop =  
131 - window.pageYOffset ||  
132 - document.documentElement.scrollTop ||  
133 - document.body.scrollTop;  
134 - this.height1 = scrollTop;  
135 - // 文档高度  
136 - this.height2 = document.body.scrollHeight;  
137 - // 可视区域  
138 - this.height3 =  
139 - document.compatMode == "CSS1Compat"  
140 - ? document.documentElement.clientHeight  
141 - : document.body.clientHeight;  
142 - // 如果滚动到最低(这里设置离最底还有100距离才触发函数)  
143 - // available条件是为了防止触底时一直不断地请求。因此,请求一次后available设为0,直到滚动到离底部超过100距离(即数据加载玩后)才设为1  
144 - if (this.height3 + this.height1 >= this.height2 - 100 && this.available) {  
145 - //请求下一页  
146 - this.page++;  
147 - this.available = 0;  
148 - let that = this;  
149 - fetch("api/moments?page=" + this.page)  
150 - .then(res => res.json())  
151 - .then(res => {  
152 - that.moments = res.data;  
153 - if (that.moments[0]) {  
154 - that.sort(0);  
155 - } else {  
156 - that.page--;  
157 - }  
158 - });  
159 - } else if (this.height3 + this.height1 < this.height2 - 100) {  
160 - this.available = 1;  
161 - }  
162 - }  
163 - },  
164 - computed: {},  
165 - watch: {},  
166 - components: {  
167 - horizontalSlide  
168 - }  
169 -};  
170 -</script>  
171 -  
172 -/* 定义局部样式,添加外围容器,scss嵌套尽量不要超过三层,会影响查找器性能 */  
173 -<style rel='stylesheet/scss' lang='scss' scoped>  
174 -.waterfall-wrap {}  
175 -//@import "./style.scss";  
176 -</style>  
177 -  
178 -/* 定义全局样式,添加外围容器,避免覆盖全局样式, 若不需要,请删除 */  
179 -<style rel='stylesheet/scss' lang='scss'>  
180 -.waterfall-wrap {}  
181 -</style>