mirror of https://github.com/halo-dev/halo
refactor: pending review status comment component (halo-dev/console#425)
Signed-off-by: Ryan Wang <i@ryanc.cc>pull/3445/head
parent
6157859810
commit
91b5fc1035
|
@ -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>
|
|
@ -1,53 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<a-popover
|
<a-popover
|
||||||
v-model="visible"
|
|
||||||
:arrowPointAtCenter="true"
|
:arrowPointAtCenter="true"
|
||||||
:autoAdjustOverflow="true"
|
:autoAdjustOverflow="true"
|
||||||
:overlayStyle="{ width: '300px', top: '50px' }"
|
:overlayStyle="{ width: '400px', top: '50px' }"
|
||||||
|
overlayClassName="header-comment-popover"
|
||||||
placement="bottomRight"
|
placement="bottomRight"
|
||||||
title="待审核评论"
|
title="待审核评论"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
>
|
>
|
||||||
<template slot="content">
|
<template #content>
|
||||||
<div class="custom-tab-wrapper">
|
<div class="custom-tab-wrapper">
|
||||||
<a-tabs v-model="activeKey" :animated="{ inkBar: true, tabPane: false }" @change="handleTabsChanged">
|
<a-tabs v-model="activeKey" :animated="{ inkBar: true, tabPane: false }" @change="handleListAuditingComments">
|
||||||
<a-tab-pane key="post" tab="文章">
|
<a-tab-pane v-for="target in targets" :key="target.key" :tab="target.label">
|
||||||
<a-list :dataSource="converttedPostComments" :loading="postCommentsLoading">
|
<CommentListView :comments="comments[target.dataKey]" :loading="comments.loading" />
|
||||||
<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-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<span class="header-comment">
|
<span class="inline-block transition-all">
|
||||||
<a-badge v-if="postComments.length > 0 || sheetComments.length > 0" dot>
|
<a-badge v-if="comments.post.length || comments.sheet.length || comments.journal.length" dot>
|
||||||
<a-icon type="bell" />
|
<a-icon type="bell" />
|
||||||
</a-badge>
|
</a-badge>
|
||||||
<a-badge v-else>
|
<a-badge v-else>
|
||||||
|
@ -59,93 +30,64 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import apiClient from '@/utils/api-client'
|
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 {
|
export default {
|
||||||
name: 'HeaderComment',
|
name: 'HeaderComment',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
activeKey: 'post',
|
targets: targets,
|
||||||
visible: false,
|
activeKey: 'posts',
|
||||||
postComments: [],
|
comments: {
|
||||||
postCommentsLoading: false,
|
post: [],
|
||||||
sheetComments: [],
|
sheet: [],
|
||||||
sheetCommentsLoading: false
|
journal: [],
|
||||||
}
|
loading: 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
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.handleListPostAuditingComments(false)
|
this.handleListAuditingComments()
|
||||||
this.handleListSheetAuditingComments(false)
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
visible(value) {
|
|
||||||
if (value) {
|
|
||||||
if (this.activeKey === 'post') {
|
|
||||||
this.handleListPostAuditingComments(false)
|
|
||||||
} else if (this.activeKey === 'sheet') {
|
|
||||||
this.handleListSheetAuditingComments(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleListPostAuditingComments(enableLoading = true) {
|
async handleListAuditingComments() {
|
||||||
if (enableLoading) {
|
try {
|
||||||
this.postCommentsLoading = true
|
this.comments.loading = true
|
||||||
}
|
|
||||||
apiClient.comment
|
const params = { status: 'AUDITING', size: 20 }
|
||||||
.latest('posts', 5, 'AUDITING')
|
|
||||||
.then(response => {
|
const responses = await Promise.all(
|
||||||
this.postComments = response.data
|
targets.map(target => {
|
||||||
})
|
return apiClient.comment.list(target.key, params)
|
||||||
.finally(() => {
|
})
|
||||||
this.postCommentsLoading = false
|
)
|
||||||
})
|
|
||||||
},
|
this.comments.post = responses[0].data.content
|
||||||
handleListSheetAuditingComments(enableLoading = true) {
|
this.comments.sheet = responses[1].data.content
|
||||||
if (enableLoading) {
|
this.comments.journal = responses[2].data.content
|
||||||
this.sheetCommentsLoading = true
|
} catch (e) {
|
||||||
}
|
this.$log.error('Failed to get auditing comments', e)
|
||||||
apiClient.comment
|
} finally {
|
||||||
.latest('sheets', 5, 'AUDITING')
|
this.comments.loading = false
|
||||||
.then(response => {
|
|
||||||
this.sheetComments = response.data
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.sheetCommentsLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleTabsChanged(activeKey) {
|
|
||||||
if (activeKey === 'post') {
|
|
||||||
this.handleListPostAuditingComments()
|
|
||||||
} else if (activeKey === 'sheet') {
|
|
||||||
this.handleListSheetAuditingComments()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
|
||||||
.header-comment {
|
|
||||||
display: inline-block;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
span {
|
|
||||||
vertical-align: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import AttachmentDetailModal from './Attachment/AttachmentDetailModal'
|
||||||
import ReactiveButton from './Button/ReactiveButton'
|
import ReactiveButton from './Button/ReactiveButton'
|
||||||
import PostTag from './Post/PostTag'
|
import PostTag from './Post/PostTag'
|
||||||
import AttachmentInput from './Input/AttachmentInput'
|
import AttachmentInput from './Input/AttachmentInput'
|
||||||
|
import CommentListView from './Comment/CommentListView'
|
||||||
|
|
||||||
const _components = {
|
const _components = {
|
||||||
Ellipsis,
|
Ellipsis,
|
||||||
|
@ -19,7 +20,8 @@ const _components = {
|
||||||
AttachmentDetailModal,
|
AttachmentDetailModal,
|
||||||
ReactiveButton,
|
ReactiveButton,
|
||||||
PostTag,
|
PostTag,
|
||||||
AttachmentInput
|
AttachmentInput,
|
||||||
|
CommentListView
|
||||||
}
|
}
|
||||||
|
|
||||||
const components = {}
|
const components = {}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { timeAgo } from '@/utils/datetime'
|
||||||
|
|
||||||
dayjs.locale('zh-cn')
|
dayjs.locale('zh-cn')
|
||||||
|
|
||||||
|
import { marked } from 'marked'
|
||||||
|
|
||||||
Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm') {
|
Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm') {
|
||||||
return dayjs(dataStr).format(pattern)
|
return dayjs(dataStr).format(pattern)
|
||||||
})
|
})
|
||||||
|
@ -44,3 +46,7 @@ Vue.filter('dayTime', function (value) {
|
||||||
const seconds = Math.floor(((value % 86400) % 3600) % 60)
|
const seconds = Math.floor(((value % 86400) % 3600) % 60)
|
||||||
return days + 'd ' + hours + 'h ' + minutes + 'm ' + seconds + 's'
|
return days + 'd ' + hours + 'h ' + minutes + 'm ' + seconds + 's'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Vue.filter('markdownRender', function (value) {
|
||||||
|
return marked.parse(value)
|
||||||
|
})
|
||||||
|
|
|
@ -835,6 +835,15 @@ body {
|
||||||
z-index: 999 !important;
|
z-index: 999 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-comment-popover {
|
||||||
|
.ant-popover-content {
|
||||||
|
.ant-popover-inner-content {
|
||||||
|
height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#nprogress {
|
#nprogress {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,11 +75,14 @@
|
||||||
<a-tab-pane key="2" tab="最近评论">
|
<a-tab-pane key="2" tab="最近评论">
|
||||||
<div class="custom-tab-wrapper">
|
<div class="custom-tab-wrapper">
|
||||||
<a-tabs :animated="{ inkBar: true, tabPane: false }">
|
<a-tabs :animated="{ inkBar: true, tabPane: false }">
|
||||||
<a-tab-pane key="1" tab="文章">
|
<a-tab-pane key="posts" tab="文章">
|
||||||
<recent-comment-tab type="posts"></recent-comment-tab>
|
<recent-comment-tab type="posts" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="2" tab="页面">
|
<a-tab-pane key="sheets" tab="页面">
|
||||||
<recent-comment-tab type="sheets"></recent-comment-tab>
|
<recent-comment-tab type="sheets" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="journals" tab="日志">
|
||||||
|
<recent-comment-tab type="journals" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,52 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<a-list :dataSource="formmatedCommentData" :loading="loading" itemLayout="horizontal">
|
<CommentListView :comments="comments" :loading="loading" />
|
||||||
<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>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import apiClient from '@/utils/api-client'
|
import apiClient from '@/utils/api-client'
|
||||||
|
|
||||||
import { marked } from 'marked'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RecentCommentTab',
|
name: 'RecentCommentTab',
|
||||||
props: {
|
props: {
|
||||||
|
@ -65,38 +23,20 @@ export default {
|
||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
formmatedCommentData() {
|
|
||||||
return this.comments.map(comment => {
|
|
||||||
comment.content = marked.parse(comment.content)
|
|
||||||
return comment
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
created() {
|
||||||
this.handleListTargetComments()
|
this.handleListTargetComments()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleListTargetComments() {
|
async handleListTargetComments() {
|
||||||
this.loading = true
|
try {
|
||||||
apiClient.comment
|
this.loading = true
|
||||||
.latest(this.type, 5, 'PUBLISHED')
|
const { data } = await apiClient.comment.latest(this.type, 5, 'PUBLISHED')
|
||||||
.then(response => {
|
this.comments = data
|
||||||
this.comments = response.data
|
} catch (e) {
|
||||||
})
|
this.$log.error('Failed to load comments', e)
|
||||||
.finally(() => {
|
} finally {
|
||||||
this.loading = false
|
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')
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue