Authored by yyq

Merge branch 'feature/userPage'

  1 +<template>
  2 + <Layout class="author-page">
  3 + <LayoutHeader slot='header' theme="white">
  4 + <div ref="headerAuthor" class="header-author">
  5 + <div class="h-name flex">{{baseData.nickName}}</div>
  6 + <div class="h-more">
  7 + <div class="flex">
  8 + <WidgetAvatar v-if="baseData.headIco" class="h-headico" :src="baseData.headIco" :width="100" :height="100"></WidgetAvatar>
  9 + </div>
  10 + <div class="h-follow flex">
  11 + <WidgetFollow class="widget-follow" :author-uid="autherInfo.authorUid" :follow="isAttention" @on-follow="follow => onFollow(follow)"></WidgetFollow>
  12 + </div>
  13 + </div>
  14 + </div>
  15 + <template v-slot:opts>
  16 + <WidgetShare class="header-share"></WidgetShare>
  17 + </template>
  18 + </LayoutHeader>
  19 + <cube-sticky :pos="scrollY">
  20 + <cube-scroll
  21 + class="main-container"
  22 + :scroll-events="scrollEvents"
  23 + @scroll="scrollHandler">
  24 + <div ref="authorProfile" class="author-profile">
  25 + <span class="avatar-box">
  26 + <WidgetAvatar v-if="baseData.headIco" :src="baseData.headIco" :width="100" :height="100"></WidgetAvatar>
  27 + </span>
  28 + <div class="author-section">
  29 + <ul class="author-fans">
  30 + <li>
  31 + <span class="num">{{baseData.attCount}}</span>
  32 + <p class="name">
  33 + <span>关注</span>
  34 + </p>
  35 + </li>
  36 + <li>
  37 + <span class="num">{{baseData.fansCount}}</span>
  38 + <p class="name">
  39 + <span>粉丝</span>
  40 + </p>
  41 + </li>
  42 + <li>
  43 + <span class="num">{{baseData.praiseAndfavorite}}</span>
  44 + <p class="name">
  45 + <span>获赞与收藏</span>
  46 + </p>
  47 + </li>
  48 + </ul>
  49 + <div class="operate-wrap">
  50 + <label v-if="isOwner" class="operate-btn btn-user-edit">编辑个人资料</label>
  51 + <WidgetFollow v-else class="operate-btn" :author-uid="autherInfo.authorUid" :follow="isAttention" @on-follow="follow => onFollow(follow)"></WidgetFollow>
  52 + </div>
  53 + </div>
  54 + </div>
  55 + <p v-if="baseData.signature" class="author-desc">{{baseData.signature}}</p>
  56 + <cube-sticky-ele ele-key="11">
  57 + <FavTabBlock :tabs-num="tabsNum" :active-index="activeIndex" @change="changeTab"></FavTabBlock>
  58 + </cube-sticky-ele>
  59 + <div class="contant-list">
  60 + <WaterFall class="pannel-wrap" :list="list" :pos="scrollY"></WaterFall>
  61 + </div>
  62 +
  63 + <div v-if="loadStatus" class="loading">
  64 + <Loading v-if="loadStatus === 1" class="load-icon" :size="20"></Loading>
  65 + <p v-else class="load-text">没有更多了</p>
  66 + </div>
  67 + </cube-scroll>
  68 + <template slot="fixed" slot-scope="props">
  69 + <FavTabBlock :tabs-num="tabsNum" :active-index="activeIndex" @change="changeTab"></FavTabBlock>
  70 + </template>
  71 + </cube-sticky>
  72 + </Layout>
  73 +</template>
  74 +
  75 +<script>
  76 + import {assign, get} from 'lodash';
  77 + import {Scroll, Sticky, Loading} from 'cube-ui';
  78 + import CubeStickyEle from 'cube-ui/src/components/sticky/sticky-ele.vue';
  79 + import FavTabBlock from './components/fav-tab-block';
  80 + import WaterFall from './components/water-fall';
  81 +
  82 + import {createNamespacedHelpers} from 'vuex';
  83 + const {mapActions} = createNamespacedHelpers('user');
  84 +
  85 + export default {
  86 + name: 'userpage',
  87 + data() {
  88 + return {
  89 + autherInfo: {},
  90 + scrollEvents: ['scroll'],
  91 + scrollY: 0,
  92 + baseData: {},
  93 + isAttention: false,
  94 + isOwner: false,
  95 + tabsNum: [10, 0],
  96 + activeIndex: 0,
  97 + fetchInfo: {},
  98 + loadStatus: ''
  99 + }
  100 + },
  101 + created() {
  102 + this.autherInfo = {
  103 + authorUid: +this.$route.params.id,
  104 + authorType: this.$route.params.type
  105 + };
  106 +
  107 + this.fetchBaseInfo();
  108 + this.fetchList();
  109 + },
  110 + mounted() {
  111 + let $dom = this.$refs.headerAuthor;
  112 +
  113 + if ($dom.offsetHeight) {
  114 + this._animeDuration = 300;
  115 + import('animejs').then(({default: anime}) => {
  116 + this._animeEl = anime({
  117 + targets: $dom,
  118 + translateY: -$dom.offsetHeight,
  119 + easing: 'easeInOutSine',
  120 + duration: this._animeDuration,
  121 + autoplay: false
  122 + });
  123 + });
  124 + }
  125 + },
  126 + watch: {
  127 + scrollY(top) {
  128 + let animePlayed = false;
  129 +
  130 + if (top > this.$refs.authorProfile.offsetHeight) {
  131 + animePlayed = true;
  132 + }
  133 +
  134 + if (!this._animePlayed === !animePlayed) {
  135 + return;
  136 + }
  137 +
  138 + let start;
  139 + let self = this;
  140 +
  141 + function step(timestamp) {
  142 + if (!start) {
  143 + start = timestamp
  144 + };
  145 +
  146 + let progress = Math.floor(timestamp - start);
  147 +
  148 + self._animeEl.seek(animePlayed ? progress : self._animeDuration - progress);
  149 +
  150 + if (progress < self._animeDuration) {
  151 + window.requestAnimationFrame(step);
  152 + }
  153 + };
  154 +
  155 + window.requestAnimationFrame(step);
  156 +
  157 + this._animePlayed = animePlayed;
  158 + }
  159 + },
  160 + computed: {
  161 + list() {
  162 + return get(this.fetchInfo, `${this.activeIndex}.list`) || [];
  163 + }
  164 + },
  165 + methods: {
  166 + ...mapActions(['autherBaseInfo', 'autherAritcleNum', 'autherPubList', 'autherFavList']),
  167 + scrollHandler({ y }) {
  168 + this.scrollY = -y;
  169 +
  170 + if (this.scrollY + 1000 > this.$el.offsetHeight) {
  171 + this._listTimer && clearTimeout(this._listTimer);
  172 + this._listTimer = setTimeout(() => {
  173 + this.fetchList();
  174 + }, 100);
  175 + }
  176 + },
  177 + changeTab(index) {
  178 + if (this.activeIndex !== index) {
  179 + this.activeIndex = index;
  180 + this.fetchList();
  181 + }
  182 + },
  183 + fetchBaseInfo() {
  184 + this.autherBaseInfo(this.autherInfo).then(res => {
  185 + if (res.code === 200) {
  186 + this.baseData = res.data;
  187 + this.isOwner = res.data.isOwner;
  188 + this.isAttention = res.data.isAttention === 'Y';
  189 + }
  190 + });
  191 +
  192 + this.autherAritcleNum(this.autherInfo).then(res => {
  193 + this.tabsNum = [get(res, 'data.articleCount'), get(res, 'data.favoriteCount')];
  194 + });
  195 + },
  196 + async fetchList() {
  197 + this.fetchInfo = this.fetchInfo || [];
  198 +
  199 + if (this.syncing) {
  200 + return;
  201 + }
  202 +
  203 + let info = this.fetchInfo[this.activeIndex] || {};
  204 + let result;
  205 +
  206 + info.page = info.page || 1;
  207 +
  208 + if (info.page >= info.totalPage) {
  209 + return;
  210 + }
  211 +
  212 + let syncServiceName;
  213 +
  214 + if (this.activeIndex === 1) {
  215 + syncServiceName = 'autherFavList';
  216 + } else {
  217 + syncServiceName = 'autherPubList';
  218 + }
  219 +
  220 + if (this[syncServiceName]) {
  221 + this.syncing = true;
  222 + result = await this[syncServiceName](assign({
  223 + page: info.page,
  224 + lastedTime: info.lastedTime || ''
  225 + }, this.autherInfo));
  226 + this.syncing = false;
  227 + }
  228 +
  229 + if (result.code === 200) {
  230 + info.list = (info.list || []).concat(result.data.list);
  231 + info.page++;
  232 + info.totalPage = 10 || result.data.totalPage;
  233 + info.lastedTime = result.data.lastedTime;
  234 + }
  235 +
  236 + if (info.page > info.totalPage) {
  237 + this.loadStatus = 2;
  238 + } else {
  239 + this.loadStatus = 1;
  240 + }
  241 +
  242 + this.fetchInfo[this.activeIndex] = info;
  243 +
  244 + this.fetchInfo = {...this.fetchInfo};
  245 + },
  246 + onFollow(follow) {
  247 + this.isAttention = follow;
  248 + }
  249 + },
  250 + components: {
  251 + CubeScroll: Scroll,
  252 + CubeSticky: Sticky,
  253 + CubeStickyEle,
  254 + Loading,
  255 + FavTabBlock,
  256 + WaterFall
  257 + }
  258 + };
  259 +</script>
  260 +
  261 +
  262 +<style lang="scss">
  263 + .author-page {
  264 + box-sizing: border-box;
  265 + color: #4a4a4a;
  266 + }
  267 +
  268 + .header-author {
  269 + width: 100%;
  270 + height: 100%;
  271 + position: relative;
  272 +
  273 + .flex {
  274 + height: 100%;
  275 + display: flex;
  276 + align-items: center;
  277 + justify-content: center;
  278 + }
  279 +
  280 + .h-name {
  281 + font-size: 36px;
  282 + font-weight: 500;
  283 + }
  284 +
  285 + .h-more {
  286 + width: 100%;
  287 + height: 100%;
  288 + position: absolute;
  289 + top: 100%;
  290 + left: 0;
  291 + }
  292 +
  293 + .h-headico {
  294 + width: 60px;
  295 + height: 60px;
  296 + }
  297 +
  298 + .h-follow {
  299 + position: absolute;
  300 + top: 0;
  301 + right: -50px;
  302 + }
  303 + }
  304 +
  305 +
  306 + .header-share {
  307 + margin-right: 26px;
  308 + color: #222;
  309 + font-weight: bold;
  310 + }
  311 +
  312 + .author-profile {
  313 + padding: 24px 30px;
  314 + display: flex;
  315 + justify-content: space-between;
  316 +
  317 + .avatar-box {
  318 + width: 150px;
  319 + height: 150px;
  320 + overflow: hidden;
  321 + border-radius: 50%;
  322 +
  323 + > img {
  324 + width: 100%;
  325 + height: 100%;
  326 + display: block;
  327 + }
  328 + }
  329 + }
  330 +
  331 + .author-section {
  332 + display: flex;
  333 + flex-direction: column;
  334 + justify-content: space-between;
  335 + }
  336 +
  337 + .author-fans {
  338 + display: flex;
  339 + justify-content: flex-end;
  340 + padding-top: 4px;
  341 + padding-right: 54px;
  342 +
  343 + li {
  344 + margin-left: 140px;
  345 + position: relative;
  346 +
  347 + &:first-child {
  348 + margin-left: 0;
  349 + }
  350 +
  351 + .num {
  352 + font-size: 28px;
  353 + font-weight: 500;
  354 + padding-bottom: 6px;
  355 + display: block;
  356 + }
  357 +
  358 + .name {
  359 + position: absolute;
  360 + word-break: keep-all;
  361 + font-size: 20px;
  362 + font-weight: 300;
  363 + color: #9b9b9b;
  364 + margin-left: 50%;
  365 +
  366 + > * {
  367 + position: relative;
  368 + left: -50%;
  369 + }
  370 + }
  371 + }
  372 + }
  373 +
  374 + .operate-wrap {
  375 + text-align: right;
  376 +
  377 + .operate-btn {
  378 + width: calc(100% - 20px);
  379 + font-size: 23px;
  380 + line-height: 48px;
  381 + display: inline-block;
  382 + }
  383 +
  384 + .btn-user-edit {
  385 + color: #222;
  386 + border: 1px solid #4a4a4a;
  387 + border-radius: 8px;
  388 + text-align: center;
  389 + }
  390 + }
  391 +
  392 + .author-desc {
  393 + margin: 20px 30px;
  394 + font-size: 24px;
  395 + font-weight: 300;
  396 + overflow: hidden;
  397 + text-overflow: ellipsis;
  398 + white-space: nowrap;
  399 + }
  400 +
  401 + .loading {
  402 + padding: 20px 0;
  403 +
  404 + .load-icon > span {
  405 + margin: auto;
  406 + }
  407 +
  408 + .load-text {
  409 + text-align: center;
  410 + }
  411 + }
  412 +</style>
  1 +<template>
  2 + <div class="tabs-wrap">
  3 + <ul class="tabs-list">
  4 + <li v-for="(item, index) in tabList" :key="index" :class="{'active': active === index}" @click="changeType(index, true)">
  5 + {{item.name}}
  6 + <span v-if="item.num" class="t-num">({{item.num}})</span>
  7 + </li>
  8 + </ul>
  9 + </div>
  10 +</template>
  11 +
  12 +
  13 +<script>
  14 +import {find} from 'lodash';
  15 +
  16 +export default {
  17 + props: {
  18 + tabsNum: Array,
  19 + activeIndex: Number
  20 + },
  21 + data() {
  22 + return {
  23 + tabList: [
  24 + {name: '内容', type: 1},
  25 + {name: '收藏', type: 2},
  26 + ],
  27 + active: ''
  28 + };
  29 + },
  30 + created() {
  31 + this.changeType(this.activeIndex);
  32 + this.computetabsNum();
  33 + },
  34 + methods: {
  35 + changeType(index, isClick) {
  36 + if (!this.tabList[index]) {
  37 + index = 0;
  38 + }
  39 +
  40 + this.active = index;
  41 +
  42 + this.$emit('change', index);
  43 + },
  44 + computetabsNum(a) {
  45 + let tabList = this.tabList;
  46 +
  47 + for (let i = this.tabList.length - 1; i >= 0; i--) {
  48 + let num = '';
  49 +
  50 + if (this.tabsNum[i] > 0) {
  51 + num = this.tabsNum[i];
  52 + }
  53 +
  54 + tabList[i].num = num;
  55 + }
  56 +
  57 + this.tabList = [...tabList];
  58 + },
  59 + computeCurrentTab() {
  60 + this.changeType(this.activeIndex || 0);
  61 + }
  62 + },
  63 + watch: {
  64 + tabsNum: 'computetabsNum',
  65 + activeIndex: 'computeCurrentTab'
  66 + }
  67 +};
  68 +</script>
  69 +
  70 +
  71 +<style>
  72 + .tabs-wrap {
  73 + padding: 20px 30px 30px;
  74 + background-color: #fff;
  75 + display: flex;
  76 + justify-content: space-between;
  77 + align-items: center;
  78 +
  79 + .tabs-list {
  80 + display: flex;
  81 +
  82 + li {
  83 + font-size: 32px;
  84 + color: #b0b0b0;
  85 + font-weight: 300;
  86 + margin-right: 40px;
  87 + }
  88 +
  89 + .active {
  90 + color: #222;
  91 + font-weight: 500;
  92 + position: relative;
  93 +
  94 + &:after {
  95 + content: '';
  96 + position: absolute;
  97 + left: 0;
  98 + bottom: -10px;
  99 + width: 100%;
  100 + height: 8px;
  101 + background-color: #d90025;
  102 + box-shadow: 0 2px 4px 0 rgba(210, 0, 13, 0.34);
  103 + }
  104 + }
  105 +
  106 + .t-num {
  107 + color: #b0b0b0;
  108 + font-size: 24px;
  109 + zoom: 0.9;
  110 + margin-left: -8px;
  111 + }
  112 + }
  113 + }
  114 +</style>
  1 +<template>
  2 + <div class="wf-list" :style="{'height': listHeight + 'px'}">
  3 + <div
  4 + v-for="i in viewList"
  5 + :key="`${i._temporary ? '_' : ''}${i.articleId}`"
  6 + class="wf-item"
  7 + :class="{'wf-item-default': i._default, 'wf-item-temp': i._temporary}"
  8 + :style="`width: ${100 / cols}%;transform: translate(${i.left}px, ${i.top}px)`">
  9 + <div class="wf-item-mid">
  10 + <router-link :to="'/article/' + i.articleId">
  11 + <div class="layer-image" :style="{'height': i.coverHeight + 'px'}">
  12 + <ImageFormat v-if="!i._temporary" :src="i[srcKey]" :width="coverImageWidth" :height="i.coverHeight"></ImageFormat>
  13 + </div>
  14 + <div class="description">{{i.content}}</div>
  15 + </router-link>
  16 +
  17 + <div class="attribution">
  18 + <router-link :to="'/article/' + i.articleId" class="auther">
  19 + <span class="avatar">
  20 + <WidgetAvatar v-if="!i._temporary" :src="i.authorHeadIco" :width="70" :height="70"></WidgetAvatar>
  21 + </span>
  22 + <span class="name">{{i.authorName}}</span>
  23 + </router-link>
  24 +
  25 + <div class="fav">
  26 + <WidgetFav :articleId="i.articleId" :num="i.praiseCount" :option="favOption"></WidgetFav>
  27 + </div>
  28 + </div>
  29 + </div>
  30 + </div>
  31 + </div>
  32 +</template>
  33 +
  34 +<script>
  35 +import {assign} from 'lodash';
  36 +
  37 +export default {
  38 + data() {
  39 + return {
  40 + viewList: [{
  41 + _default: true
  42 + }],
  43 + coverImageWidth: 0,
  44 + colWidthPer: 0,
  45 + calcIndex: 0,
  46 + loadedIndex: 0,
  47 + colsHeight: [],
  48 + favOption: {
  49 + iconFontSize: 26,
  50 + textAlign: 'normal'
  51 + }
  52 + }
  53 + },
  54 + props: {
  55 + pos: {
  56 + type: Number,
  57 + default: 0
  58 + },
  59 + list: {
  60 + type: Array,
  61 + default: []
  62 + },
  63 + srcKey: {
  64 + type: String,
  65 + default: 'coverImage'
  66 + },
  67 + cols: {
  68 + type: Number,
  69 + default: 2
  70 + },
  71 + space: {
  72 + type: Number,
  73 + default: 14
  74 + }
  75 + },
  76 + mounted() {
  77 + this.$on('calced', (nlist) => {
  78 + this.viewList = this.viewList.concat(nlist);
  79 + this.$nextTick(() => {
  80 + this.calcLayout();
  81 + })
  82 + });
  83 +
  84 + this.reset();
  85 + this.clacCoverSize();
  86 + },
  87 + watch: {
  88 + pos() {
  89 + this.timer && clearTimeout(this.timer);
  90 + this.timer = setTimeout(this.resetViewList, 0);
  91 + },
  92 + list(newList, oldList) {
  93 + if (oldList.length > newList.length) {
  94 + this.reset();
  95 + }
  96 +
  97 + this.clacCoverSize();
  98 + }
  99 + },
  100 + computed: {
  101 + listHeight() {
  102 + return Math.max.apply(null, this.colsHeight);
  103 + },
  104 + colWidth() {
  105 + return this.$el.offsetWidth / this.cols;
  106 + }
  107 + },
  108 + methods: {
  109 + clacCoverSize() {
  110 + let nlist = [];
  111 +
  112 + for (let i = this.calcIndex; i < this.list.length; i++) {
  113 + let item = this.list[i];
  114 +
  115 + item.coverHeight = Math.floor(item.imageHeight / item.imageWidth * this.coverImageWidth);
  116 +
  117 + nlist.push(assign({_temporary: true}, item));
  118 + };
  119 +
  120 + this.$emit('calced', nlist);
  121 + },
  122 + calcCoverImgHeight(w, h) {
  123 + return h / w * this.coverImageWidth;
  124 + },
  125 + calcLayout() {
  126 + let $item = this.$el.getElementsByClassName('wf-item-temp');
  127 +
  128 + if (!$item || !$item.length) {
  129 + return;
  130 + }
  131 +
  132 + if (!this.loadedIndex || !this.colsHeight) {
  133 + this.colsHeight = [];
  134 + }
  135 +
  136 + for (let i = this.loadedIndex; i < this.list.length; i++) {
  137 + let $elem = $item[i - this.loadedIndex];
  138 +
  139 + if (!$elem) {
  140 + return;
  141 + }
  142 +
  143 + let height = $elem.offsetHeight;
  144 + let top, left, leftPer;
  145 + let item = this.list[i];
  146 +
  147 + item.height = height;
  148 +
  149 + if (i < this.cols) {
  150 + this.colsHeight[i] = height;
  151 + top = 0
  152 + left = i * this.colWidth;
  153 + leftPer = i * this.colWidthPer;
  154 + } else {
  155 + let minHeight = Math.min.apply(null, this.colsHeight);
  156 + let minIndex = this.colsHeight.indexOf(minHeight);
  157 +
  158 + top = minHeight;
  159 + left = minIndex * this.colWidth;
  160 + leftPer = minIndex * this.colWidthPer;
  161 +
  162 + this.colsHeight[minIndex] = minHeight + height;
  163 + }
  164 +
  165 + item.left = left;
  166 + item.leftPer = leftPer;
  167 + item.top = top;
  168 + item.bottom = top + height;
  169 + }
  170 +
  171 + this.loadedIndex = this.list.length;
  172 +
  173 + this.resetViewList();
  174 + },
  175 + setImgWidth() {
  176 + let imgWidth = this.$el.offsetWidth / this.cols;
  177 + let $item = this.$el.getElementsByClassName('wf-item');
  178 +
  179 + if ($item && $item.length) {
  180 + let _w = $item[0].offsetWidth;
  181 + let $img = $item[0].getElementsByClassName('layer-image');
  182 +
  183 + (_w > 0) && (imgWidth = _w);
  184 +
  185 + if ($img && $img.length) {
  186 + _w = $img[0].offsetWidth;
  187 + (_w > 0) && (imgWidth = _w);
  188 + }
  189 + }
  190 +
  191 + this.coverImageWidth = imgWidth;
  192 + },
  193 + reset() {
  194 + this.offsetTop = this.$el.offsetTop;
  195 + this.clientHeight = document.body.clientHeight;
  196 + this.colWidthPer = 100 / this.cols;
  197 + this.loadedIndex = 0;
  198 + this.calcIndex = 0;
  199 +
  200 + this.viewList = [];
  201 + this.lastPos = 0;
  202 + this.viewIndex = 0;
  203 +
  204 + this.setImgWidth();
  205 + },
  206 + resetViewList() {
  207 + this.viewIndex = this.viewIndex || {};
  208 +
  209 + let i, step;
  210 + let startPos = this.pos - this.clientHeight * 2;
  211 + let endPos = this.pos + this.clientHeight * 2;
  212 + let list = [];
  213 + let indexArr = [];
  214 +
  215 + if (this.pos < this.lastPos) {
  216 + i = Math.min.apply(null, [(this.viewIndex['end'] || 0), this.list.length - 1]);
  217 + step = -1;
  218 + } else {
  219 + i = Math.max.apply(null, [(this.viewIndex['start'] || 0), 0]);
  220 + step = 1;
  221 + }
  222 +
  223 + let loop = true;
  224 +
  225 + while (loop)
  226 + {
  227 + if (i < 0 || i >= this.list.length) {
  228 + loop = false;
  229 + continue;
  230 + }
  231 +
  232 + let item = this.list[i];
  233 +
  234 + if (item) {
  235 + if (item.top > endPos - this.offsetTop) {
  236 + if (step > 0) {
  237 + loop = false;
  238 + }
  239 + } else if (item.bottom < startPos - this.offsetTop) {
  240 + if (step < 0) {
  241 + loop = false;
  242 + }
  243 + } else {
  244 + indexArr.push(i);
  245 + list.push(item);
  246 + }
  247 + }
  248 +
  249 + i += step;
  250 + }
  251 +
  252 + if (!indexArr.length) {
  253 + return;
  254 + }
  255 +
  256 + indexArr = indexArr.sort(function(a, b) {
  257 + return a - b;
  258 + });
  259 +
  260 + let viewIndex = {
  261 + start: indexArr[0],
  262 + end: indexArr[indexArr.length - 1]
  263 + };
  264 +
  265 + if (this.viewIndex.start !== viewIndex.start || this.viewIndex.end !== viewIndex.end) {
  266 + this.viewList = list;
  267 + this.lastPos = this.pos;
  268 + this.viewIndex = viewIndex;
  269 + }
  270 + }
  271 + }
  272 +};
  273 +</script>
  274 +
  275 +<style lang="css">
  276 + .wf-list {
  277 + margin: 0 20px;
  278 + font-size: 0;
  279 + position: relative;
  280 + }
  281 +
  282 + .wf-item {
  283 + padding: 10px;
  284 + font-size: 24px;
  285 + overflow: hidden;
  286 + position: absolute;
  287 +
  288 + .wf-item-mid {
  289 + border-radius: 4px;
  290 + box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.15);
  291 + overflow: hidden;
  292 + }
  293 +
  294 + .layer-image {
  295 + background-color: #f4f4f4;
  296 + min-height: 100px;
  297 +
  298 + > img {
  299 + width: 100%;
  300 + height: 100%;
  301 + display: block;
  302 + }
  303 + }
  304 +
  305 + .description {
  306 + line-height: 1.5;
  307 + padding: 10px 20px;
  308 + }
  309 +
  310 + .attribution {
  311 + display: flex;
  312 + justify-content: space-between;
  313 + padding: 20px;
  314 + }
  315 +
  316 + .avatar {
  317 + width: 60px;
  318 + height: 60px;
  319 + border-radius: 50%;
  320 + display: inline-block;
  321 + vertical-align: middle;
  322 + overflow: hidden;
  323 +
  324 + > img {
  325 + width: 100%;
  326 + height: 100%;
  327 + }
  328 + }
  329 +
  330 + .name {
  331 + display: inline-block;
  332 + vertical-align: middle;
  333 + }
  334 +
  335 + .fav {
  336 + line-height: 60px;
  337 + }
  338 + }
  339 +
  340 + .wf-item-default,
  341 + .wf-item-temp {
  342 + opacity: 0;
  343 + margin-left: -100%;
  344 + }
  345 +</style>
1 export default [{ 1 export default [{
2 - path: '/xxx',  
3 - name: 'xxx',  
4 -  
5 - // component: () => import(/* webpackChunkName: "xxx" */ './xxx') 2 + path: '/author/:type/:id',
  3 + name: 'author',
  4 + component: () => import(/* webpackChunkName: "author" */ './author')
6 }]; 5 }];
@@ -41,4 +41,40 @@ export default { @@ -41,4 +41,40 @@ export default {
41 41
42 return result; 42 return result;
43 }, 43 },
  44 + async autherBaseInfo(actions, {authorUid, authorType}) {
  45 + const result = await this.$api.get('/api/grass/getGrassUserBaseInfo', {
  46 + authorUid,
  47 + authorType
  48 + });
  49 +
  50 + return result;
  51 + },
  52 + async autherAritcleNum(actions, {authorUid, authorType}) {
  53 + const result = await this.$api.get('/api/grass/getGrassPubAndFavorNum', {
  54 + authorUid,
  55 + authorType
  56 + });
  57 +
  58 + return result;
  59 + },
  60 + async autherPubList(actions, {authorUid, authorType, page, lastedTime}) {
  61 + const result = await this.$api.get('/api/grass/userPublishedArticleList', {
  62 + authorUid,
  63 + authorType,
  64 + page,
  65 + lastedTime
  66 + });
  67 +
  68 + return result;
  69 + },
  70 + async autherFavList(actions, {authorUid, authorType, page, lastedTime}) {
  71 + const result = await this.$api.get('/api/grass/userFavouriteArticleList', {
  72 + authorUid,
  73 + authorType,
  74 + page,
  75 + lastedTime
  76 + });
  77 +
  78 + return result;
  79 + },
44 }; 80 };
@@ -2,7 +2,10 @@ const URI_PACKAGE_ARTICLE = 'guang/service/v2/article/'; @@ -2,7 +2,10 @@ const URI_PACKAGE_ARTICLE = 'guang/service/v2/article/';
2 const URI_PACKAGE_AUTHOR = 'guang/service/v1/author/'; 2 const URI_PACKAGE_AUTHOR = 'guang/service/v1/author/';
3 const URI_PACKAGE_PRAISE = 'guang/api/v1/article/'; 3 const URI_PACKAGE_PRAISE = 'guang/api/v1/article/';
4 4
  5 +const userPageApis = require('./api-map/userpage');
  6 +
5 module.exports = { 7 module.exports = {
  8 + ...userPageApis,
6 '/api/grass/labelRealtedArticleDetail': { 9 '/api/grass/labelRealtedArticleDetail': {
7 api: 'app.grass.labelRealtedArticleDetail', 10 api: 'app.grass.labelRealtedArticleDetail',
8 cache: true, 11 cache: true,
  1 +module.exports = {
  2 + '/api/grass/getGrassUserBaseInfo': {
  3 + api: 'app.grass.getGrassUserBaseInfo',
  4 + cache: true,
  5 + params: {
  6 + authorUid: {type: Number, require: true},
  7 + authorType: {type: Number, require: true}
  8 + }
  9 + },
  10 + '/api/grass/getGrassPubAndFavorNum': {
  11 + api: 'app.grass.getGrassPubAndFavorNum',
  12 + cache: true,
  13 + params: {
  14 + authorUid: {type: Number, require: true},
  15 + authorType: {type: Number, require: true}
  16 + }
  17 + },
  18 + '/api/grass/userPublishedArticleList': {
  19 + api: 'app.grass.userPublishedArticleList',
  20 + cache: true,
  21 + params: {
  22 + authorUid: {type: Number, require: true},
  23 + authorType: {type: Number, require: true},
  24 + page: {type: Number},
  25 + lastedTime: {type: Number}
  26 + }
  27 + },
  28 + '/api/grass/userFavouriteArticleList': {
  29 + api: 'app.grass.userFavouriteArticleList',
  30 + cache: true,
  31 + params: {
  32 + authorUid: {type: Number, require: true},
  33 + authorType: {type: Number, require: true},
  34 + page: {type: Number},
  35 + lastedTime: {type: Number}
  36 + }
  37 + },
  38 +}