Skip to content

Commit

Permalink
chore(SPA): 话题详情页
Browse files Browse the repository at this point in the history
issue #467
  • Loading branch information
mutoe committed Dec 7, 2018
1 parent 9e338cb commit 5b1c65e
Show file tree
Hide file tree
Showing 11 changed files with 437 additions and 105 deletions.
56 changes: 56 additions & 0 deletions resources/spa/src/api/topic.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,59 @@ export function createTopic (data) {
const url = '/feed/topics'
return api.post(url, data, { validateStatus: s => s === 201 })
}

/**
* 获取动态详情
*
* @author mutoe <[email protected]>
* @export
* @param {number} topicId
* @returns
*/
export function getTopicDetail (topicId) {
const url = `/feed/topics/${topicId}`
return api.get(url, { validateStatus: s => s === 200 })
}

/**
* 获取话题相关动态
*
* @author mutoe <[email protected]>
* @export
* @param {number} topicId
* @param {Object} params
* @param {Object} [params.limit=15]
* @param {Object} [params.direction=desc]
* @param {Object} [params.index=0]
* @returns {FeedObject[]}
*/
export function getTopicFeeds (topicId, params) {
const url = `/feed/topics/${topicId}/feeds`
return api.get(url, { params, validateStatus: s => s === 200 })
}

/**
* 关注话题
*
* @author mutoe <[email protected]>
* @export
* @param {number} topicId
* @returns
*/
export function followTopic (topicId) {
const url = `/user/feed-topics/${topicId}`
return api.put(url, {}, { validateStatus: s => s === 204 })
}

/**
* 取消关注话题
*
* @author mutoe <[email protected]>
* @export
* @param {number} topicId
* @returns
*/
export function unfollowTopic (topicId) {
const url = `/user/feed-topics/${topicId}`
return api.delete(url, { validateStatus: s => s === 204 })
}
2 changes: 1 addition & 1 deletion resources/spa/src/components/Avatar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import _ from 'lodash'
export default {
name: 'Avatar',
props: {
size: { type: String, default: 'def' },
size: { type: String, default: 'def', validator: val => ['def', 'big', 'nano', 'small', 'tiny'].includes(val) },
user: { type: Object, required: true },
anonymity: { type: [Boolean, Number], default: false },
},
Expand Down
19 changes: 0 additions & 19 deletions resources/spa/src/components/FeedCard/FeedCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,25 +98,6 @@ import FeedVideo from './FeedVideo.vue'
import CommentItem from './CommentItem.vue'
import * as api from '@/api/feeds.js'
function time2txt (str) {
if (!str) return ''
if (typeof str === 'string') str = str.replace(/-/g, '/') // 兼容 IOS 保证传入数据格式 YYYY/MM/dd HH:mm:ss
let date = new Date(str)
// 时间差 = 当前时间 - date (单位: 毫秒)
let time = new Date() - date
if (time < 0) {
return ''
} else if (time / 3600000 < 24) {
return '今天'
} else {
const M = String(date.getMonth() + 1).padStart(2, '0')
const D = String(date.getDate()).padStart(2, '0')
return M + '' + D
}
}
export default {
name: 'FeedCard',
components: {
Expand Down
112 changes: 61 additions & 51 deletions resources/spa/src/components/PortalPanel.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<div
class="c-portal-panel"
:class="{'no-cover': !cover, loading}"
@mousedown="startDrag"
@touchstart="startDrag"
@mousemove.stop="onDrag"
Expand All @@ -9,29 +10,32 @@
@touchend="stopDrag"
@mouseleave="stopDrag"
>
<div v-if="loading" class="m-pos-f m-spinner" />
<header :class="{ 'show-title': scrollTop > 1 / 2 * bannerHeight }" class="header">
<div class="left">
<svg class="m-style-svg m-svg-def" @click="onBackClick">
<use xlink:href="#icon-back" />
</svg>
<CircleLoading v-show="updating" color="light" />
</div>
<div class="title m-text-cut">{{ title }}</div>
<div class="right">
<div :style="{opacity: loading ? 0 : 1}" class="right">
<svg class="m-style-svg m-svg-def" @click="$emit('more')">
<use xlink:href="#icon-more" />
</svg>
</div>
</header>

<div v-if="loading" class="m-pos-f m-spinner" />

<main>
<div
ref="banner"
:style="bannerStyle"
class="banner"
>
<CircleLoading
v-show="updating"
class="fetching"
:color="cover ? 'light': 'dark'"
/>
<slot name="head" />
</div>

Expand Down Expand Up @@ -74,11 +78,9 @@ export default {
name: 'PortalPanel',
props: {
title: { type: String, default: '' },
cover: { type: String, default: '' },
cover: { type: [String, Boolean], default: '' },
loading: { type: Boolean, default: false },
showFooter: { type: Boolean, default: false },
fetching: { type: Boolean, default: false },
noMore: { type: Boolean, default: false },
back: { type: Function, default: null },
},
data () {
Expand All @@ -92,26 +94,26 @@ export default {
updating: false,
tags: [],
footroom: null,
fetchFollow: false,
noMore: false,
fetching: false,
}
},
computed: {
bio () {
return this.user.bio || '这家伙很懒,什么也没留下'
},
bannerStyle () {
return [
{ 'background-image': this.cover ? `url("${this.cover}")` : undefined },
const style = [
this.paddingTop,
{ transitionDuration: this.dragging ? '0s' : '300ms' },
]
if (this.cover) {
style.push({ 'background-image': this.cover ? `url("${this.cover}")` : undefined })
}
return style
},
// banner 相关
paddingTop () {
return {
paddingTop: ((this.bannerHeight + 80 * Math.atan(this.dY / 200)) / (this.bannerHeight * 2)) * 100 + '%',
paddingTop: ((this.bannerHeight + 80 * Math.atan(this.dY / 200)) / (this.bannerHeight * (this.cover ? 2 : 3))) * 100 + '%',
}
},
},
Expand All @@ -130,6 +132,7 @@ export default {
})
this.footroom.init()
}
window.addEventListener('scroll', this.onScroll)
},
activated () {
window.addEventListener('scroll', this.onScroll)
Expand All @@ -146,19 +149,23 @@ export default {
if (this.back) this.back()
else this.goBack()
},
onUpdate () {
beforeUpdate () {
this.updating = true
this.dY = 0
},
afterUpdate () {
this.updating = false
},
beforeLoadMore () {
this.fetching = true
},
afterLoadMore (noMore = true) {
this.noMore = noMore
this.fetcing = false
},
onScroll: _.debounce(function () {
this.scrollTop = Math.max(
0,
document.body.scrollTop,
document.documentElement.scrollTop
)
const scrollTop = document.body.scrollTop || document.documentElement.scrollTop
this.scrollTop = Math.max(0, scrollTop)
}, 1000 / 60),
startDrag (e) {
e = e.changedTouches ? e.changedTouches[0] : e
Expand Down Expand Up @@ -193,15 +200,17 @@ export default {
right: 0;
bottom: initial;
display: flex;
justify-content: space-between;
height: 90px;
max-width: 768px;
border-bottom: 0;
margin-top: -1px;/*no*/
background-color: transparent;
color: #fff;
font-size: 32px;
transition: background 0.3s ease;
overflow: hidden;
z-index: 10;
z-index: 30;
.title {
display: flex;
Expand All @@ -218,20 +227,10 @@ export default {
flex: none;
width: 90px;
display: flex;
justify-content: space-around;
justify-content: center;
align-items: center;
}
.left {
width: 90*2px;
padding-left: 30px;
justify-content: flex-start;
}
.right {
padding-right: 30px;
justify-content: flex-end;
}
&.show-title {
background-image: none;
background-color: #fff;
Expand All @@ -245,28 +244,14 @@ export default {
}
.banner {
padding-top: 320/640 * 100%;
position: relative;
width: 100%;
transform: translate3d(0, 0, 0);
background-size: cover;
background-position: center;
background-image: url("../images/user_home_default_cover.png");
padding-top: (320/640 * 100%);
background: #fff center/cover no-repeat;
font-size: 28px;
color: #fff;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); /* no */
h3 {
margin: 20px 0;
font-size: 32px;
}
p {
margin: 0 0 30px 0;
span + span {
margin-left: 40px;
}
i {
margin: 0 5px;
}
}
transform: translate3d(0, 0, 0);
}
.sticky-bar {
Expand All @@ -291,6 +276,31 @@ export default {
border-top: 1px solid @border-color;
background-color: #fff;
}
&.loading {
.header {
color: #333;
}
> main {
filter: blur(30px);
}
}
&.no-cover {
.header {
color: #333;
}
.banner {
color: #333;
text-shadow: none;
h1 {
font-weight: bold;
}
p {
color: @text-color3;
}
}
}
}
</style>
24 changes: 8 additions & 16 deletions resources/spa/src/page/UserHome.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<template>
<div class="p-user-home">
<PortalPanel
ref="panel"
ref="portal"
:title="user.name"
:cover="userBackground"
:loading="loading"
:show-footer="!isMine"
:no-more="noMoreData"
:back="beforeBack"
@update="updateData"
@more="onMoreClick"
Expand Down Expand Up @@ -189,7 +188,6 @@ export default {
paid: '付费动态',
pinned: '置顶动态',
},
noMoreData: false,
fetchFeeding: false,
tags: [],
Expand Down Expand Up @@ -245,7 +243,8 @@ export default {
return this.extra.feeds_count || 0
},
userBackground () {
return this.user.bg && this.user.bg.url
const { url } = this.user.bg || {}
return url || require('@/images/user_home_default_cover.png')
},
verified () {
return this.user.verified
Expand Down Expand Up @@ -376,28 +375,21 @@ export default {
})
},
fetchUserFeed (loadmore) {
if (this.fetchFeeding) return
this.fetchFeeding = true
const params = {
limit: 15,
type: 'users',
user: this.userId,
}
const params = { limit: 15, type: 'users', user: this.userId }
if (loadmore) params.after = this.after
if (this.isMine && this.screen !== 'all') params.screen = this.screen
this.$refs.portal.beforeLoadMore()
feedApi.getFeeds(params)
.then(({ data: { feeds = [] } }) => {
this.feeds = loadmore ? [...this.feeds, ...feeds] : feeds
this.noMoreData = feeds.length < params.limit
this.$refs.portal.afterLoadMore(feeds.length < params.limit)
})
.finally(() => {
this.$refs.panel.afterUpdate()
this.fetchFeeding = false
this.$refs.portal.afterUpdate()
})
},
updateData () {
this.$refs.panel.onUpdate()
this.$refs.portal.beforeUpdate()
this.fetchUserInfo()
this.fetchUserFeed()
this.fetchUserTags()
Expand Down
Loading

0 comments on commit 5b1c65e

Please sign in to comment.