diff --git a/apps/components/layouts/recycle-scroll-reveal.vue b/apps/components/layouts/recycle-scroll-reveal.vue
index 205e9f2..332dc7b 100644
--- a/apps/components/layouts/recycle-scroll-reveal.vue
+++ b/apps/components/layouts/recycle-scroll-reveal.vue
@@ -102,7 +102,9 @@ export default {
       let list = newList.slice(oldList.length, newList.length);
 
       if (list.length) {
+        this.revealCalcing = true;
         this.loadItems(list, oldList.length).then(() => {
+          this.revealCalcing = false;
           if (oldList.length < 2) {
             this._updateList();
           }
@@ -141,14 +143,16 @@ export default {
       this.load(true);
     },
     clear() {
+      this.clearTimestamp = new Date().getTime();
       this.noMore = false;
-      this.items = [];
 
       for (let i = 0; i < this.cols; i++) {
         this.visibleItems[i].length = 0;
         this.colsHeight[i] = 0;
         this.startIndexs[i] = 0;
       }
+
+      this.items = [];
     },
     load(reload) {
       if (reload) {
@@ -182,6 +186,7 @@ export default {
       }
 
       let startCol = this.getMinHeightCol();
+      const timestamp = this.clearTimestamp;
 
       for (let i = 0; i < list.length; i++) {
         await this.loadItem({
@@ -190,7 +195,7 @@ export default {
           width: this.itemWidth,
           isThumb: true,
           placeholder: false
-        }, i < lastIndex ? (startCol + i) % this.cols : -1);
+        }, i < lastIndex ? (startCol + i) % this.cols : -1, timestamp);
       }
 
       this.$nextTick(() => {
@@ -228,8 +233,12 @@ export default {
         return true;
       });
     },
-    loadItem(item, index) {
+    loadItem(item, index, timestamp) {
       return new Promise(r => {
+        if (timestamp !== this.clearTimestamp) {
+          return r();
+        }
+
         if (index < 0) {
           index = this.getMinHeightCol();
         }
@@ -298,7 +307,7 @@ export default {
       const heights = this.$refs.scroll.offsetHeight;
 
       // trigger load
-      if (scrollTop + this.$el.offsetHeight > heights - this.offset) {
+      if (scrollTop + this.$el.offsetHeight > heights - this.offset && !this.revealCalcing) {
         this.load();
       }
 
diff --git a/apps/pages/article/article-detail2.vue b/apps/pages/article/article-detail2.vue
index ac5101c..73db3db 100644
--- a/apps/pages/article/article-detail2.vue
+++ b/apps/pages/article/article-detail2.vue
@@ -1,9 +1,9 @@
 <template>
   <Layout class="article-detail">
-    <RecycleScrollReveal :size="5" ref="scroll" @scroll="onScroll" :offset="800" :on-fetch="onFetch" :manual-init="true">
+    <RecycleScrollReveal :size="10" ref="scroll" @scroll="onScroll" :offset="2000" :on-fetch="onFetch" :manual-init="true">
       <template v-slot:eternalTop>
-        <!-- <ArticleDeatilNote :data="article" :scroll-top="scrollTop" :list-title="listTitle"></ArticleDeatilNote> -->
-        <ArticleDeatilLong ref="detailLong" :data="article" :scroll-top="scrollTop" :list-title="listTitle"></ArticleDeatilLong>
+        <ArticleDeatilLong v-if="article.sort == 2" ref="detailLong" :data="article" :scroll-top="scrollTop" :list-title="listTitle"></ArticleDeatilLong>
+        <ArticleDeatilNote v-else :data="article" :scroll-top="scrollTop" :list-title="listTitle"></ArticleDeatilNote>
       </template>
       <template class="article-item" #item="{ data }">
         <ArticleItem2
@@ -52,14 +52,22 @@ export default {
       this.init();
     }
   },
+  beforeRouteUpdate(to, from, next) {
+    if (+this.$route.params.id !== +to.params.id) {
+      this.id = +to.params.id;
+      this.init();
+    }
+    next();
+  },
   mounted() {
     this.colWidthForTwo = Math.floor(this.$el.offsetWidth / 2);
   },
   computed: {
   },
   methods: {
-    ...mapActions(['fetchArticleList']),
+    ...mapActions(['fetchArticleList', 'fetchDetailRecommendAricles']),
     init() {
+      this.recommendArticles = {};
       this.syncServiceArticleDetail();
     },
     onScroll({scrollTop}) {
@@ -76,15 +84,57 @@ export default {
 
       this.fetchArticleList({
         articleId,
-        page: 1,
-        limit: 1,
         singleDetail: 'Y'
       }).then(res => {
         this.article = get(res, 'data.detailList[0]', {});
+
+        if (this.$refs.scroll) {
+          this.$refs.scroll.$el.scrollTop = 0;
+          this.$refs.scroll.clear();
+
+          this.$nextTick(() => {
+            this.$refs.scroll.init();
+          });
+        }
+
       });
     },
-    onFetch() {
-      return Promise.resolve([this.article]);
+    async onFetch() {
+      if (!this.id || this.fetching) {
+        return [];
+      }
+
+      // 推荐文章接口一次性提供,不支持分页
+      if (this.recommendArticles[this.id]) {
+        return false;
+      }
+
+      this.fetching = true;
+
+      let list = [];
+      const result = await this.fetchDetailRecommendAricles({
+        articleId: this.id
+      });
+
+      this.fetching = false;
+
+      if (result.code === 200) {
+        list = get(result, 'data', []);
+
+        if (!list || !list.length) {
+          list = false;
+        } else {
+          this.recommendArticles[this.id] = true;
+        }
+      } else {
+        this.$createToast && this.$createToast({
+          txt: result.message || '服务器开小差了',
+          type: 'warn',
+          time: 1000
+        }).show();
+      }
+
+      return list;
     }
   },
   components: {
@@ -100,4 +150,8 @@ export default {
   padding: 6px;
   background-color: #f0f0f0;
 }
+/deep/ .loading {
+  height: 100px;
+  visibility: hidden;
+}
 </style>
diff --git a/apps/pages/article/components/article/article-item2.vue b/apps/pages/article/components/article/article-item2.vue
index d096470..267abf2 100644
--- a/apps/pages/article/components/article/article-item2.vue
+++ b/apps/pages/article/components/article/article-item2.vue
@@ -2,8 +2,9 @@
   <div class="article-item">
     <slot>
       <div class="article-item-main">
+        <a v-if="actionUrl" class="action-article" :href="actionUrl" target="_blank"></a>
         <div class="layer-image" :style="`height: ${Math.floor(width * coverImage.scale)}px`" @click="toArticlePage">
-          <ImageFormat :mode="1" :src="coverImage.contentData" :width="imgWidth" :height="Math.floor(coverImage.scale * imgWidth)" :style="imageStyle"></ImageFormat>
+          <ImageFormat :mode="1" :src="coverImage.contentData" :width="coverImage.width" :height="coverImage.height"></ImageFormat>
         </div>
         <div v-if="intro" class="description" @click="toArticlePage">{{intro}}</div>
         <div class="attribution">
@@ -23,6 +24,7 @@
 <script>
 import {get, round} from 'lodash';
 import YAS from 'utils/yas-constants';
+import {getArticleImageSize} from 'utils/image-handler';
 
 import {createNamespacedHelpers} from 'vuex';
 const {mapState} = createNamespacedHelpers('article');
@@ -55,23 +57,31 @@ export default {
       return this.articleStates[this.data.articleId] || this.data;
     },
     coverImage() {
-      let img = get(this.data, 'blockList', []).filter(block => block.templateKey === 'image')[0] || {};
+      let img = get(this.data, 'blockList', []).filter(block => block.templateKey === 'image')[0] || {
+        contentData: this.data.coverImage,
+        width: this.data.imageWidth,
+        height: this.data.imageHeight
+      };
+
+      Object.assign(img, getArticleImageSize({
+        width: img.width,
+        height: img.height,
+        maxWidth: 500
+      }));
 
       img.scale = img.height / img.width;
 
       return img;
     },
-    imageStyle() {
-      let width = 370;
-      let height = Math.floor(this.coverImage.scale * width);
-
-      return {
-        width: `${round(width / 40, 2)}rem`,
-        height: `${round(height / 40, 2)}rem`,
-      };
-    },
     intro() {
-      return get(get(this.data, 'blockList', []).filter(block => block.templateKey === 'text'), '[0].contentData', '')
+      return get(get(this.data, 'blockList', []).filter(block => block.templateKey === 'text'), '[0].contentData', this.data.content)
+    },
+    actionUrl() {
+      if (this.data.sort === 3) {
+        return this.data.actionUrl;
+      }
+
+      return '';
     },
     favOption() {
       return {
@@ -110,12 +120,10 @@ export default {
       }
 
       this.$router.push({
-        name: 'article',
+        name: 'article.detail',
         params: {
-          id: this.data.articleId
-        },
-        query: {
-          columnType: 1002
+          id: this.data.articleId,
+          type: this.data.sort
         }
       });
       this.reportClickArticle();
@@ -155,9 +163,24 @@ export default {
   .article-item-main {
     background-color: #fff;
     border-radius: 2PX;
+    position: relative;
     overflow: hidden;
   }
 
+  .action-article {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    z-index: 1;
+  }
+
+  .layer-image img {
+    width: 100%;
+    height: 100%;
+  }
+
   .description {
     margin: 14px 20px;
     font-size: 24px;
diff --git a/apps/pages/article/components/detail/article-intro.vue b/apps/pages/article/components/detail/article-intro.vue
index a1c5aaa..fb289f4 100644
--- a/apps/pages/article/components/detail/article-intro.vue
+++ b/apps/pages/article/components/detail/article-intro.vue
@@ -2,7 +2,7 @@
   <div ref="intro" class="intro-wrap" :class="introClass" :style="introStyle" @click="onExpanding">
     <div v-if="trimIntro" class="intro-content">
       <p class="pre-wrap">{{intro}}</p>
-      <div ref="introPool" class="intro-pool pre-wrap">{{trimIntro}}</div>
+      <div ref="introPool" class="intro-pool pre-wrap" v-if="!introHeight">{{trimIntro}}</div>
       <span ref="expand" class="expand" v-if="showExpandTxt">…<b>继续阅读</b></span>
     </div>
     <div class="collapse" v-if="showCollapseTxt">收起</div>
@@ -47,6 +47,8 @@ export default {
       return (this.data.maxLines || MAX_LINES) - 1;
     },
     trimIntro() {
+      this.introHeight = 0;
+      this.introCollapseHeight = 0;
       return trim(this.data.intro);
     },
     showIntro() {
@@ -120,8 +122,6 @@ export default {
       this.$nextTick(() => {
         this.introHeight = get(this.$refs, 'introPool.scrollHeight', 0) + 20;
         this.introCollapseHeight = get(this.$refs, 'intro.offsetHeight', 0);
-
-        console.log(this.introHeight, this.introCollapseHeight);
       });
     },
     onExpanding() {
diff --git a/apps/pages/article/components/detail/article-long.vue b/apps/pages/article/components/detail/article-long.vue
index 0535403..69a7401 100644
--- a/apps/pages/article/components/detail/article-long.vue
+++ b/apps/pages/article/components/detail/article-long.vue
@@ -8,7 +8,7 @@
         </div>
       </div>
     </ArticleDetailHeader>
-    <div ref="coverFigure" class="cover-figure">
+    <div ref="coverFigure" class="cover-figure" :style="coverStyle">
         <ImageFormat class="cover-img" :src="coverImage.src" :width="coverImage.width" :height="coverImage.height" :style="`transform: translate3d(0, ${coverTranslateY}px, 0)`"></ImageFormat>
     </div>
     <div ref="authorBlock" class="author-block">
@@ -34,11 +34,22 @@
         <div class="article-goods">文中商品</div>
       </template>
     </ArticleDetailFooter>
+
+    <YohoActionSheet transfer v-if="showCommentAction" ref="commentAction" :full="true">
+      <Comment ref="comment"
+        :destId="data.articleId"
+        :popup="true"
+        :article-id="data.articleId"
+        :commentCount="data.commentCount"
+        :pos-id="posId"
+        @on-close="onCloseComment"
+        @on-comment="onActionComment"></Comment>
+    </YohoActionSheet>
   </div>
 </template>
 
 <script>
-import {get} from 'lodash';
+import {get, floor} from 'lodash';
 import ArticleItemHeader from '../article/article-item-header';
 import ArticleItemTopics from '../article/article-item-topics';
 import ArticleDetailFooter from './article-footer';
@@ -65,6 +76,8 @@ export default {
       showMoreOpt: false,
       authorBlock: {},
       recomendProduct: [],
+      showCommentAction: false,
+      showCommentActioning: false
     };
   },
   mounted() {
@@ -76,6 +89,11 @@ export default {
   },
   computed: {
     ...mapState(['yoho']),
+    coverStyle() {
+      return {
+        height: `${floor(this.data.imageHeight / this.data.imageHeight * 750 / 40, 2)}rem`
+      };
+    },
     coverImage() {
       this.$nextTick(() => {
         if (this.$refs.coverFigure) {
@@ -125,7 +143,7 @@ export default {
       if (this.$refs && this.$refs.header) {
         scrollTop += this.$refs.header.$el.offsetHeight;
       }
-
+console.log(scrollTop, top, height)
       if (top && height) {
        if (scrollTop >= top + height) {
           return 100;
@@ -182,8 +200,24 @@ export default {
       this.$emit('on-follow', follow);
     },
     onComment() {
-
-    }
+      this.showCommentAction = true;
+      this.$nextTick(() => {
+        if (this.showCommentActioning) {
+          return;
+        }
+        this.showCommentActioning = true;
+        this.$refs.comment.init();
+        this.$refs.commentAction.show();
+        setTimeout(() => {
+          this.showCommentActioning = false;
+        }, 300);
+      });
+    },
+    onCloseComment() {
+      this.$refs.commentAction.hide();
+    },
+    onActionComment() {
+    },
   },
   components: {
     ArticleDetailHeader,
diff --git a/apps/pages/article/index.js b/apps/pages/article/index.js
index 981916f..381e172 100644
--- a/apps/pages/article/index.js
+++ b/apps/pages/article/index.js
@@ -15,17 +15,25 @@ export default [{
     keepAlive: true
   }
 }, {
-  path: '/article/detail/:id',
-  name: 'article.detail',
-  alias: '/article/detail/:id',
+  path: '/article/detail2/:id',
+  name: 'article.detail2',
+  alias: '/article/detail2/:id',
   component: () => import(/* webpackChunkName: "article" */ './article-detail'),
   meta: {
     keepAlive: true
   }
 }, {
-  path: '/article/detail2/:id',
-  name: 'article.detail2',
-  alias: '/article/detail2/:id',
+  path: '/article/:type/:id',
+  name: 'article.detail',
+  alias: '/article/:type/:id',
+  component: () => import(/* webpackChunkName: "article-detail" */ './article-detail2'),
+  meta: {
+    keepAlive: true
+  }
+}, {
+  path: '/article/detail/:id',
+  name: 'article.detail',
+  alias: '/article/detail/:id',
   component: () => import(/* webpackChunkName: "article-detail" */ './article-detail2'),
   meta: {
     keepAlive: true
diff --git a/apps/pages/userpage/components/author-article-item.vue b/apps/pages/userpage/components/author-article-item.vue
index 3819560..7fa60f9 100644
--- a/apps/pages/userpage/components/author-article-item.vue
+++ b/apps/pages/userpage/components/author-article-item.vue
@@ -2,6 +2,7 @@
   <div class="wf-item" :class="temporary ? 'wf-temp' : ''">
     <div v-if="temporary"></div>
     <div v-else class="wf-item-mid">
+      <a v-if="actionUrl" class="action-article" :href="actionUrl" target="_blank"></a>
       <div class="layer-image" @click="onClick" :style="`height: ${data.blockWidth * data.scale}px`">
         <ImageFormat :mode="1" :src="data.coverImage" :width="imgWidth" :height="Math.floor(data.scale * imgWidth)"></ImageFormat>
       </div>
@@ -57,6 +58,13 @@ export default {
         iconFontSize: 30,
         textAlign: 'normal'
       };
+    },
+    actionUrl() {
+      if (this.data.sort === 3) {
+        return this.data.actionUrl;
+      }
+
+      return '';
     }
   },
   methods: {
@@ -91,9 +99,19 @@ export default {
     .wf-item-mid {
       border-radius: 2PX;
       overflow: hidden;
+      position: relative;
       background-color: #fff;
     }
 
+    .action-article {
+      position: absolute;
+      top: 0;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      z-index: 1;
+    }
+
     .layer-image {
       background-color: #f4f4f4;
       min-height: 100px;
diff --git a/apps/store/article/actions.js b/apps/store/article/actions.js
index b5f8a8e..f5aa039 100644
--- a/apps/store/article/actions.js
+++ b/apps/store/article/actions.js
@@ -280,5 +280,21 @@ export default {
     }
 
     return result;
+  },
+  async fetchDetailRecommendAricles({ commit }, { articleId }) {
+    commit(Types.FETCH_DETAIL_RECOMMEND_REQUEST);
+    const result = await this.$api.post('/api/grass/detailRecommendArticles', {
+      articleId
+    });
+
+    if (result && result.code === 200) {
+      commit(Types.FETCH_DETAIL_RECOMMEND_SUCCESS, {
+        data: result.data || []
+      });
+    } else {
+      commit(Types.FETCH_DETAIL_RECOMMEND_FAILD);
+    }
+
+    return result;
   }
 };
diff --git a/apps/store/article/index.js b/apps/store/article/index.js
index 38d57e7..ba17150 100644
--- a/apps/store/article/index.js
+++ b/apps/store/article/index.js
@@ -20,7 +20,9 @@ export default function() {
       articleStates: {},
       fetchTopicInfo: false,
       topicInfo: {},
-      fetchTopicArticles: false
+      fetchTopicArticles: false,
+      articleDetailList: [],
+      fetchDetailRecommendArticles: false
     },
     actions,
     mutations
diff --git a/apps/store/article/mutations.js b/apps/store/article/mutations.js
index f61a052..f75faf4 100644
--- a/apps/store/article/mutations.js
+++ b/apps/store/article/mutations.js
@@ -63,14 +63,17 @@ function setArticleList(state, data, type, thumb) {
       let {width, height} = getArticleImageSize({
         width: img.width,
         height: img.height,
-        MIN_SCALE: inx === 0 ? (3 / 4) : 0
+        minScale: inx === 0 ? (3 / 4) : 0
       });
 
       img.width = parseInt(width, 10);
       img.height = parseInt(height, 10);
     });
   });
-  state[articlefield(type, thumb)] = state[articlefield(type, thumb)].concat(data);
+
+  if (articlefield(type, thumb)) {
+    state[articlefield(type, thumb)] = state[articlefield(type, thumb)].concat(data);
+  }
 }
 
 export default {
@@ -227,4 +230,14 @@ export default {
       state.topicInfo.hasAttention = follow;
     }
   },
+  [Types.FETCH_DETAIL_RECOMMEND_REQUEST](state, topicId) {
+    state.fetchDetailRecommendArticles = true;
+  },
+  [Types.FETCH_DETAIL_RECOMMEND_SUCCESS](state, {data}) {
+    state.fetchDetailRecommendArticles = false;
+    setArticleList(state, data, 'detail');
+  },
+  [Types.FETCH_DETAIL_RECOMMEND_FAILD](state) {
+    state.fetchDetailRecommendArticles = false;
+  },
 };
diff --git a/apps/store/article/types.js b/apps/store/article/types.js
index ebeace6..022ddbc 100644
--- a/apps/store/article/types.js
+++ b/apps/store/article/types.js
@@ -33,3 +33,7 @@ export const FETCH_TOPIC_INFO_REQUEST = 'FETCH_TOPIC_INFO_REQUEST';
 export const FETCH_TOPIC_INFO_FAILD = 'FETCH_TOPIC_INFO_FAILD';
 export const FETCH_TOPIC_INFO_SUCCESS = 'FETCH_TOPIC_INFO_SUCCESS';
 export const CHANGE_TOPIC_FOLLOW = 'CHANGE_TOPIC_FOLLOW';
+
+export const FETCH_DETAIL_RECOMMEND_REQUEST = 'FETCH_DETAIL_RECOMMEND_REQUEST';
+export const FETCH_DETAIL_RECOMMEND_FAILD = 'FETCH_DETAIL_RECOMMEND_FAILD';
+export const FETCH_DETAIL_RECOMMEND_SUCCESS = 'FETCH_DETAIL_RECOMMEND_SUCCESS';
diff --git a/apps/utils/image-handler.js b/apps/utils/image-handler.js
index 3a567d9..0766b8e 100644
--- a/apps/utils/image-handler.js
+++ b/apps/utils/image-handler.js
@@ -1,21 +1,21 @@
 const MAX_WIDTH = 1000;
 
-export function getArticleImageSize({width, height, MIN_SCALE = 0.75}) {
+export function getArticleImageSize({width, height, minScale = 0.75, maxWidth = MAX_WIDTH}) {
   width = +width;
   height = +height;
-  if (width > MAX_WIDTH) {
-    height = height / (width / MAX_WIDTH);
-    width = MAX_WIDTH;
+  if (width > maxWidth) {
+    height = height / (width / maxWidth);
+    width = maxWidth;
   }
 
-  if (MIN_SCALE && width / height < MIN_SCALE) {
-    height = width / MIN_SCALE;
+  if (minScale && width / height < minScale) {
+    height = width / minScale;
   }
   if (width === 1) {
-    width = MAX_WIDTH / 2;
+    width = maxWidth / 2;
   }
   if (height === 1) {
-    height = MAX_WIDTH / 2;
+    height = maxWidth / 2;
   }
-  return {width, height};
+  return {width, height: Math.round(height)};
 }
diff --git a/config/api-map.js b/config/api-map.js
index f2f9a9c..c96b1a1 100644
--- a/config/api-map.js
+++ b/config/api-map.js
@@ -37,6 +37,14 @@ module.exports = {
       singleDetail: {type: String}
     }
   },
+  '/api/grass/detailRecommendArticles': {
+    api: 'app.grass.otherArticle',
+    cache: true,
+    auth: true,
+    params: {
+      articleId: {type: Number, require: false}
+    }
+  },
   '/api/grass/updateAttention': {
     api: 'app.grass.updateAttention',
     auth: true,