refactor: pending review status comment component (#425)

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/426/head
Ryan Wang 2022-01-28 20:28:57 +08:00 committed by GitHub
parent 53e686f702
commit aabce58dc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 169 additions and 188 deletions

View File

@ -0,0 +1,79 @@
<template>
<a-list :dataSource="comments" :loading="loading" item-layout="vertical">
<template #renderItem="item, index">
<a-list-item :key="index" class="!p-0">
<a-comment :avatar="item.avatar">
<template #author>
<a v-if="item.authorUrl" :href="item.authorUrl" class="!text-gray-800 hover:!text-blue-500" target="_blank">
{{ item.author }}
</a>
<span v-else class="!text-gray-500">{{ item.author }}</span>
发表在
<span class="hover:!text-blue-500 cursor-pointer" @click="handleOpenTarget(item)">
{{ targetTitle(item) }}
</span>
</template>
<template #content>
<p v-html="$options.filters.markdownRender(item.content)" />
</template>
<template #datetime>
<a-tooltip :title="item.createTime | moment">
<span>{{ item.createTime | timeAgo }}</span>
</a-tooltip>
</template>
</a-comment>
</a-list-item>
</template>
</a-list>
</template>
<script>
import { datetimeFormat } from '@/utils/datetime'
import apiClient from '@/utils/api-client'
export default {
name: 'CommentListView',
props: {
comments: {
type: Array,
default: () => []
},
loading: {
type: Boolean,
default: false
}
},
computed: {
targetTitle() {
return function (comment) {
if (comment.post) {
return comment.post.title
}
if (comment.sheet) {
return comment.sheet.title
}
if (comment.journal) {
return datetimeFormat(comment.journal.createTime)
}
return ''
}
}
},
methods: {
async handleOpenTarget(comment) {
const { post, sheet } = comment
if (post || sheet) {
const { status, fullPath, id } = post || sheet
if (['PUBLISHED', 'INTIMATE'].includes(status)) {
window.open(fullPath, '_blank')
return
}
if (status === 'DRAFT') {
const target = post ? 'post' : 'sheet'
const link = await apiClient[target].getPreviewLinkById(id)
window.open(link, '_blank')
}
}
}
}
}
</script>

View File

@ -1,53 +1,24 @@
<template>
<a-popover
v-model="visible"
:arrowPointAtCenter="true"
:autoAdjustOverflow="true"
:overlayStyle="{ width: '300px', top: '50px' }"
:overlayStyle="{ width: '400px', top: '50px' }"
overlayClassName="header-comment-popover"
placement="bottomRight"
title="待审核评论"
trigger="click"
>
<template slot="content">
<template #content>
<div class="custom-tab-wrapper">
<a-tabs v-model="activeKey" :animated="{ inkBar: true, tabPane: false }" @change="handleTabsChanged">
<a-tab-pane key="post" tab="文章">
<a-list :dataSource="converttedPostComments" :loading="postCommentsLoading">
<a-list-item slot="renderItem" slot-scope="item">
<a-list-item-meta>
<a-avatar slot="avatar" :src="item.avatar" class="bg-white" size="large" />
<template slot="title">
<a :href="item.authorUrl" target="_blank">{{ item.author }}</a
><span v-html="item.content"></span>
</template>
<template slot="description">
{{ item.createTime | timeAgo }}
</template>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane key="sheet" tab="页面">
<a-list :dataSource="converttedSheetComments" :loading="sheetCommentsLoading">
<a-list-item slot="renderItem" slot-scope="item">
<a-list-item-meta>
<a-avatar slot="avatar" :src="item.avatar" class="bg-white" size="large" />
<template slot="title">
<a :href="item.authorUrl" target="_blank">{{ item.author }}</a
><span v-html="item.content"></span>
</template>
<template slot="description">
{{ item.createTime | timeAgo }}
</template>
</a-list-item-meta>
</a-list-item>
</a-list>
<a-tabs v-model="activeKey" :animated="{ inkBar: true, tabPane: false }" @change="handleListAuditingComments">
<a-tab-pane v-for="target in targets" :key="target.key" :tab="target.label">
<CommentListView :comments="comments[target.dataKey]" :loading="comments.loading" />
</a-tab-pane>
</a-tabs>
</div>
</template>
<span class="header-comment">
<a-badge v-if="postComments.length > 0 || sheetComments.length > 0" dot>
<span class="inline-block transition-all">
<a-badge v-if="comments.post.length || comments.sheet.length || comments.journal.length" dot>
<a-icon type="bell" />
</a-badge>
<a-badge v-else>
@ -59,93 +30,64 @@
<script>
import apiClient from '@/utils/api-client'
import { marked } from 'marked'
const targets = [
{
key: 'posts',
dataKey: 'post',
label: '文章'
},
{
key: 'sheets',
dataKey: 'sheet',
label: '页面'
},
{
key: 'journals',
dataKey: 'journal',
label: '日志'
}
]
export default {
name: 'HeaderComment',
data() {
return {
activeKey: 'post',
visible: false,
postComments: [],
postCommentsLoading: false,
sheetComments: [],
sheetCommentsLoading: false
}
},
computed: {
converttedPostComments() {
return this.postComments.map(comment => {
comment.content = marked.parse(comment.content)
return comment
})
},
converttedSheetComments() {
return this.sheetComments.map(comment => {
comment.content = marked.parse(comment.content)
return comment
})
targets: targets,
activeKey: 'posts',
comments: {
post: [],
sheet: [],
journal: [],
loading: false
}
}
},
created() {
this.handleListPostAuditingComments(false)
this.handleListSheetAuditingComments(false)
},
watch: {
visible(value) {
if (value) {
if (this.activeKey === 'post') {
this.handleListPostAuditingComments(false)
} else if (this.activeKey === 'sheet') {
this.handleListSheetAuditingComments(false)
}
}
}
this.handleListAuditingComments()
},
methods: {
handleListPostAuditingComments(enableLoading = true) {
if (enableLoading) {
this.postCommentsLoading = true
}
apiClient.comment
.latest('posts', 5, 'AUDITING')
.then(response => {
this.postComments = response.data
})
.finally(() => {
this.postCommentsLoading = false
})
},
handleListSheetAuditingComments(enableLoading = true) {
if (enableLoading) {
this.sheetCommentsLoading = true
}
apiClient.comment
.latest('sheets', 5, 'AUDITING')
.then(response => {
this.sheetComments = response.data
})
.finally(() => {
this.sheetCommentsLoading = false
})
},
handleTabsChanged(activeKey) {
if (activeKey === 'post') {
this.handleListPostAuditingComments()
} else if (activeKey === 'sheet') {
this.handleListSheetAuditingComments()
async handleListAuditingComments() {
try {
this.comments.loading = true
const params = { status: 'AUDITING', size: 20 }
const responses = await Promise.all(
targets.map(target => {
return apiClient.comment.list(target.key, params)
})
)
this.comments.post = responses[0].data.content
this.comments.sheet = responses[1].data.content
this.comments.journal = responses[2].data.content
} catch (e) {
this.$log.error('Failed to get auditing comments', e)
} finally {
this.comments.loading = false
}
}
}
}
</script>
<style lang="less" scoped>
.header-comment {
display: inline-block;
transition: all 0.3s;
span {
vertical-align: initial;
}
}
</style>

View File

@ -9,6 +9,7 @@ import AttachmentDetailModal from './Attachment/AttachmentDetailModal'
import ReactiveButton from './Button/ReactiveButton'
import PostTag from './Post/PostTag'
import AttachmentInput from './Input/AttachmentInput'
import CommentListView from './Comment/CommentListView'
const _components = {
Ellipsis,
@ -19,7 +20,8 @@ const _components = {
AttachmentDetailModal,
ReactiveButton,
PostTag,
AttachmentInput
AttachmentInput,
CommentListView
}
const components = {}

View File

@ -7,6 +7,8 @@ import { timeAgo } from '@/utils/datetime'
dayjs.locale('zh-cn')
import { marked } from 'marked'
Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm') {
return dayjs(dataStr).format(pattern)
})
@ -44,3 +46,7 @@ Vue.filter('dayTime', function (value) {
const seconds = Math.floor(((value % 86400) % 3600) % 60)
return days + 'd ' + hours + 'h ' + minutes + 'm ' + seconds + 's'
})
Vue.filter('markdownRender', function (value) {
return marked.parse(value)
})

View File

@ -835,6 +835,15 @@ body {
z-index: 999 !important;
}
.header-comment-popover {
.ant-popover-content {
.ant-popover-inner-content {
height: 500px;
overflow-y: auto;
}
}
}
#nprogress {
pointer-events: none;
}

View File

@ -75,11 +75,14 @@
<a-tab-pane key="2" tab="最近评论">
<div class="custom-tab-wrapper">
<a-tabs :animated="{ inkBar: true, tabPane: false }">
<a-tab-pane key="1" tab="文章">
<recent-comment-tab type="posts"></recent-comment-tab>
<a-tab-pane key="posts" tab="文章">
<recent-comment-tab type="posts" />
</a-tab-pane>
<a-tab-pane key="2" tab="页面">
<recent-comment-tab type="sheets"></recent-comment-tab>
<a-tab-pane key="sheets" tab="页面">
<recent-comment-tab type="sheets" />
</a-tab-pane>
<a-tab-pane key="journals" tab="日志">
<recent-comment-tab type="journals" />
</a-tab-pane>
</a-tabs>
</div>

View File

@ -1,52 +1,10 @@
<template>
<a-list :dataSource="formmatedCommentData" :loading="loading" itemLayout="horizontal">
<a-list-item :key="index" slot="renderItem" slot-scope="item, index">
<a-comment :avatar="item.avatar">
<template v-if="type === 'posts'" slot="author">
<a :href="item.authorUrl" target="_blank">{{ item.author }}</a> 发表在 <a
v-if="['PUBLISHED', 'INTIMATE'].includes(item.post.status)"
:href="item.post.fullPath"
target="_blank"
>{{ item.post.title }}</a
><a
v-else-if="item.post.status === 'DRAFT'"
href="javascript:void(0)"
@click="handlePostPreview(item.post.id)"
>{{ item.post.title }}</a
><a v-else href="javascript:void(0)">{{ item.post.title }}</a>
</template>
<template v-else-if="type === 'sheets'" slot="author">
<a :href="item.authorUrl" target="_blank">{{ item.author }}</a> 发表在 <a
v-if="item.sheet.status === 'PUBLISHED'"
:href="item.sheet.fullPath"
target="_blank"
>{{ item.sheet.title }}</a
><a
v-else-if="item.sheet.status === 'DRAFT'"
href="javascript:void(0)"
@click="handleSheetPreview(item.sheet.id)"
>{{ item.sheet.title }}</a
><a v-else href="javascript:void(0)">{{ item.sheet.title }}</a
>
</template>
<!-- <template slot="actions">
<span>回复</span>
</template> -->
<p slot="content" class="comment-content-wrapper" v-html="item.content"></p>
<a-tooltip slot="datetime" :title="item.createTime | moment">
<span>{{ item.createTime | timeAgo }}</span>
</a-tooltip>
</a-comment>
</a-list-item>
</a-list>
<CommentListView :comments="comments" :loading="loading" />
</template>
<script>
import apiClient from '@/utils/api-client'
import { marked } from 'marked'
export default {
name: 'RecentCommentTab',
props: {
@ -65,38 +23,20 @@ export default {
loading: false
}
},
computed: {
formmatedCommentData() {
return this.comments.map(comment => {
comment.content = marked.parse(comment.content)
return comment
})
}
},
created() {
this.handleListTargetComments()
},
methods: {
handleListTargetComments() {
this.loading = true
apiClient.comment
.latest(this.type, 5, 'PUBLISHED')
.then(response => {
this.comments = response.data
})
.finally(() => {
this.loading = false
})
},
handlePostPreview(postId) {
apiClient.post.getPreviewLinkById(postId).then(response => {
window.open(response.data, '_blank')
})
},
handleSheetPreview(sheetId) {
apiClient.post.getPreviewLinkById(sheetId).then(response => {
window.open(response.data, '_blank')
})
async handleListTargetComments() {
try {
this.loading = true
const { data } = await apiClient.comment.latest(this.type, 5, 'PUBLISHED')
this.comments = data
} catch (e) {
this.$log.error('Failed to load comments', e)
} finally {
this.loading = false
}
}
}
}