mirror of https://github.com/halo-dev/halo-admin
refactor: pending review status comment component (#425)
Signed-off-by: Ryan Wang <i@ryanc.cc>pull/426/head
parent
53e686f702
commit
aabce58dc7
|
@ -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>
|
||||
<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>
|
||||
|
|
|
@ -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 = {}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue