mirror of https://github.com/halo-dev/halo-admin
Refactor comment drawer into modal (#463)
* refactor: target comment list modal * feat: support create comment * refactor: modal title * feat: support switch target * feat: support publish and replypull/464/head
parent
f481a9f5c8
commit
8c071ae6a5
|
@ -0,0 +1,119 @@
|
||||||
|
<template>
|
||||||
|
<a-modal v-model="modalVisible" destroyOnClose title="评论回复" @close="onClose">
|
||||||
|
<template #footer>
|
||||||
|
<ReactiveButton
|
||||||
|
:errored="submitErrored"
|
||||||
|
:loading="submitting"
|
||||||
|
erroredText="回复失败"
|
||||||
|
loadedText="回复成功"
|
||||||
|
text="回复"
|
||||||
|
type="primary"
|
||||||
|
@callback="handleSubmitCallback"
|
||||||
|
@click="handleSubmit"
|
||||||
|
></ReactiveButton>
|
||||||
|
</template>
|
||||||
|
<a-form-model ref="replyCommentForm" :model="model" :rules="rules" layout="vertical">
|
||||||
|
<a-form-model-item prop="content">
|
||||||
|
<a-input ref="contentInput" v-model="model.content" :autoSize="{ minRows: 8 }" type="textarea" />
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import apiClient from '@/utils/api-client'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CommentReplyModal',
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
comment: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
targetId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
validator: value => {
|
||||||
|
return ['post', 'sheet', 'journal'].indexOf(value) !== -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
model: {},
|
||||||
|
submitting: false,
|
||||||
|
submitErrored: false,
|
||||||
|
rules: {
|
||||||
|
content: [{ required: true, message: '* 内容不能为空', trigger: ['change'] }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
modalVisible: {
|
||||||
|
get() {
|
||||||
|
return this.visible
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit('update:visible', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modalVisible(value) {
|
||||||
|
if (value) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.contentInput.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSubmit() {
|
||||||
|
const _this = this
|
||||||
|
_this.$refs.replyCommentForm.validate(async valid => {
|
||||||
|
if (valid) {
|
||||||
|
try {
|
||||||
|
_this.submitting = true
|
||||||
|
|
||||||
|
_this.model.postId = _this.targetId
|
||||||
|
|
||||||
|
if (_this.comment) {
|
||||||
|
_this.model.parentId = _this.comment.id
|
||||||
|
}
|
||||||
|
|
||||||
|
await apiClient.comment.create(`${_this.target}s`, _this.model)
|
||||||
|
} catch (e) {
|
||||||
|
_this.submitErrored = true
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => {
|
||||||
|
_this.submitting = false
|
||||||
|
}, 400)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubmitCallback() {
|
||||||
|
if (this.submitErrored) {
|
||||||
|
this.submitErrored = false
|
||||||
|
} else {
|
||||||
|
this.model = {}
|
||||||
|
this.modalVisible = false
|
||||||
|
this.$emit('succeed')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
this.model = {}
|
||||||
|
this.modalVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,157 @@
|
||||||
|
<template>
|
||||||
|
<a-modal v-model="modalVisible" :afterClose="onClose" :title="title" :width="1024" destroyOnClose>
|
||||||
|
<a-spin :spinning="list.loading">
|
||||||
|
<TargetCommentTreeNode
|
||||||
|
v-for="(comment, index) in list.data"
|
||||||
|
:key="index"
|
||||||
|
:comment="comment"
|
||||||
|
:target="target"
|
||||||
|
:target-id="targetId"
|
||||||
|
@reload="handleGetComments"
|
||||||
|
/>
|
||||||
|
</a-spin>
|
||||||
|
|
||||||
|
<a-empty v-if="!list.loading && !list.data.length" />
|
||||||
|
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<a-pagination
|
||||||
|
:current="pagination.page"
|
||||||
|
:defaultPageSize="pagination.size"
|
||||||
|
:pageSizeOptions="['10', '20', '50', '100']"
|
||||||
|
:total="pagination.total"
|
||||||
|
class="pagination"
|
||||||
|
showLessItems
|
||||||
|
showSizeChanger
|
||||||
|
@change="handlePageChange"
|
||||||
|
@showSizeChange="handlePageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<slot name="extraFooter" />
|
||||||
|
<a-button type="primary" @click="replyModalVisible = true">创建评论</a-button>
|
||||||
|
<a-button @click="modalVisible = false">关闭</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<CommentReplyModal
|
||||||
|
:target="target"
|
||||||
|
:target-id="targetId"
|
||||||
|
:visible.sync="replyModalVisible"
|
||||||
|
@succeed="handleGetComments"
|
||||||
|
/>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// components
|
||||||
|
import TargetCommentTreeNode from './TargetCommentTreeNode'
|
||||||
|
import CommentReplyModal from './CommentReplyModal'
|
||||||
|
|
||||||
|
import apiClient from '@/utils/api-client'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TargetCommentListModal',
|
||||||
|
components: {
|
||||||
|
TargetCommentTreeNode,
|
||||||
|
CommentReplyModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '评论'
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
validator: value => {
|
||||||
|
return ['post', 'sheet', 'journal'].indexOf(value) !== -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
targetId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
list: {
|
||||||
|
data: [],
|
||||||
|
loading: false,
|
||||||
|
params: {
|
||||||
|
page: 0,
|
||||||
|
size: 10
|
||||||
|
},
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
replyModalVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
modalVisible: {
|
||||||
|
get() {
|
||||||
|
return this.visible
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit('update:visible', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pagination() {
|
||||||
|
return {
|
||||||
|
page: this.list.params.page + 1,
|
||||||
|
size: this.list.params.size,
|
||||||
|
total: this.list.total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modalVisible(value) {
|
||||||
|
if (value) {
|
||||||
|
this.handleGetComments()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
targetId() {
|
||||||
|
this.handleGetComments()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async handleGetComments() {
|
||||||
|
try {
|
||||||
|
this.list.loading = true
|
||||||
|
|
||||||
|
const response = await apiClient.comment.listAsTreeView(`${this.target}s`, this.targetId, this.list.params)
|
||||||
|
this.list.data = response.data.content
|
||||||
|
this.list.total = response.data.total
|
||||||
|
} catch (e) {
|
||||||
|
this.$log.error('Failed to get target comments', e)
|
||||||
|
} finally {
|
||||||
|
this.list.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle page change
|
||||||
|
*/
|
||||||
|
handlePageChange(page = 1) {
|
||||||
|
this.list.params.page = page - 1
|
||||||
|
this.handleGetComments()
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle page size change
|
||||||
|
*/
|
||||||
|
handlePageSizeChange(current, size) {
|
||||||
|
this.list.params.page = 0
|
||||||
|
this.list.params.size = size
|
||||||
|
this.handleGetComments()
|
||||||
|
},
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
this.$emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,146 @@
|
||||||
|
<template>
|
||||||
|
<a-comment>
|
||||||
|
<template #author>
|
||||||
|
<a :href="comment.authorUrl" target="_blank">
|
||||||
|
<a-icon v-if="comment.isAdmin" style="margin-right: 3px" type="user" />
|
||||||
|
{{ comment.author }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #avatar>
|
||||||
|
<a-avatar :alt="comment.author" :src="comment.avatar" size="large" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<p v-html="$options.filters.markdownRender(comment.content)"></p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #datetime>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>
|
||||||
|
<span>{{ comment.createTime | moment }}</span>
|
||||||
|
</template>
|
||||||
|
<span>{{ comment.createTime | timeAgo }}</span>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #actions>
|
||||||
|
<a-dropdown v-if="comment.status === 'AUDITING'" :trigger="['click']">
|
||||||
|
<span>通过</span>
|
||||||
|
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu>
|
||||||
|
<a-menu-item key="1" @click="handleChangeStatus('PUBLISHED')"> 通过</a-menu-item>
|
||||||
|
<a-menu-item key="2" @click="handlePublishAndReply"> 通过并回复</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
|
||||||
|
<span v-else-if="comment.status === 'PUBLISHED'" @click="replyModalVisible = true">回复</span>
|
||||||
|
|
||||||
|
<a-popconfirm
|
||||||
|
v-else-if="comment.status === 'RECYCLE'"
|
||||||
|
:title="'你确定要还原该评论?'"
|
||||||
|
cancelText="取消"
|
||||||
|
okText="确定"
|
||||||
|
@confirm="handleChangeStatus('PUBLISHED')"
|
||||||
|
>
|
||||||
|
还原
|
||||||
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-popconfirm
|
||||||
|
v-if="comment.status === 'PUBLISHED' || comment.status === 'AUDITING'"
|
||||||
|
:title="'你确定要将该评论移到回收站?'"
|
||||||
|
cancelText="取消"
|
||||||
|
okText="确定"
|
||||||
|
@confirm="handleChangeStatus('RECYCLE')"
|
||||||
|
>
|
||||||
|
回收站
|
||||||
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-popconfirm :title="'你确定要永久删除该评论?'" cancelText="取消" okText="确定" @confirm="handleDelete">
|
||||||
|
删除
|
||||||
|
</a-popconfirm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="comment.children">
|
||||||
|
<TargetCommentTreeNode
|
||||||
|
v-for="(child, index) in comment.children"
|
||||||
|
:key="index"
|
||||||
|
:comment="child"
|
||||||
|
:target="target"
|
||||||
|
:target-id="targetId"
|
||||||
|
@reload="$emit('reload')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<CommentReplyModal
|
||||||
|
:comment="comment"
|
||||||
|
:target="target"
|
||||||
|
:target-id="targetId"
|
||||||
|
:visible.sync="replyModalVisible"
|
||||||
|
@succeed="$emit('reload')"
|
||||||
|
/>
|
||||||
|
</a-comment>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import apiClient from '@/utils/api-client'
|
||||||
|
import CommentReplyModal from './CommentReplyModal'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TargetCommentTreeNode',
|
||||||
|
components: {
|
||||||
|
CommentReplyModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
validator: value => {
|
||||||
|
return ['post', 'sheet', 'journal'].indexOf(value) !== -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
targetId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
comment: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
replyModalVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async handleChangeStatus(status) {
|
||||||
|
try {
|
||||||
|
await apiClient.comment.updateStatusById(`${this.target}s`, this.comment.id, status)
|
||||||
|
} catch (e) {
|
||||||
|
this.$log.error('Failed to change comment status', e)
|
||||||
|
} finally {
|
||||||
|
this.$emit('reload')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async handlePublishAndReply() {
|
||||||
|
await this.handleChangeStatus('PUBLISHED')
|
||||||
|
this.replyModalVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleDelete() {
|
||||||
|
try {
|
||||||
|
await apiClient.comment.delete(`${this.target}s`, this.comment.id)
|
||||||
|
} catch (e) {
|
||||||
|
this.$log.error('Failed to delete comment', e)
|
||||||
|
} finally {
|
||||||
|
this.$emit('reload')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,245 +0,0 @@
|
||||||
<template>
|
|
||||||
<a-drawer
|
|
||||||
:afterVisibleChange="handleAfterVisibleChanged"
|
|
||||||
:visible="visible"
|
|
||||||
:width="isMobile() ? '100%' : '480'"
|
|
||||||
closable
|
|
||||||
destroyOnClose
|
|
||||||
title="评论列表"
|
|
||||||
@close="onClose"
|
|
||||||
>
|
|
||||||
<a-row align="middle" type="flex">
|
|
||||||
<a-col :span="24">
|
|
||||||
<a-list itemLayout="horizontal">
|
|
||||||
<a-list-item>
|
|
||||||
<a-list-item-meta>
|
|
||||||
<template slot="description">
|
|
||||||
<p class="comment-drawer-content" v-html="description"></p>
|
|
||||||
</template>
|
|
||||||
<h3 slot="title">{{ title }}</h3>
|
|
||||||
</a-list-item-meta>
|
|
||||||
</a-list-item>
|
|
||||||
</a-list>
|
|
||||||
</a-col>
|
|
||||||
<a-divider />
|
|
||||||
<a-col :span="24">
|
|
||||||
<a-spin :spinning="list.loading">
|
|
||||||
<a-empty v-if="list.data.length === 0" />
|
|
||||||
<TargetCommentTree
|
|
||||||
v-for="(comment, index) in list.data"
|
|
||||||
v-else
|
|
||||||
:key="index"
|
|
||||||
:comment="comment"
|
|
||||||
@delete="handleCommentDelete"
|
|
||||||
@editStatus="handleEditStatusClick"
|
|
||||||
@reply="handleCommentReply"
|
|
||||||
/>
|
|
||||||
</a-spin>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
<a-divider />
|
|
||||||
<div class="page-wrapper">
|
|
||||||
<a-pagination
|
|
||||||
:current="list.pagination.page"
|
|
||||||
:defaultPageSize="list.pagination.size"
|
|
||||||
:total="list.pagination.total"
|
|
||||||
showLessItems
|
|
||||||
@change="handlePaginationChange"
|
|
||||||
></a-pagination>
|
|
||||||
</div>
|
|
||||||
<a-divider class="divider-transparent" />
|
|
||||||
<div class="bottom-control">
|
|
||||||
<a-button type="primary" @click="handleCommentReply({})">评论</a-button>
|
|
||||||
</div>
|
|
||||||
<a-modal v-model="replyModal.visible" :title="replyModalTitle" destroyOnClose @close="onReplyModalClose">
|
|
||||||
<template slot="footer">
|
|
||||||
<ReactiveButton
|
|
||||||
:errored="replyModal.saveErrored"
|
|
||||||
:loading="replyModal.saving"
|
|
||||||
erroredText="回复失败"
|
|
||||||
loadedText="回复成功"
|
|
||||||
text="回复"
|
|
||||||
type="primary"
|
|
||||||
@callback="handleReplyCallback"
|
|
||||||
@click="handleReplyClick"
|
|
||||||
></ReactiveButton>
|
|
||||||
</template>
|
|
||||||
<a-form-model ref="replyCommentForm" :model="replyModal.model" :rules="replyModal.rules" layout="vertical">
|
|
||||||
<a-form-model-item prop="content">
|
|
||||||
<a-input ref="contentInput" v-model="replyModal.model.content" :autoSize="{ minRows: 8 }" type="textarea" />
|
|
||||||
</a-form-model-item>
|
|
||||||
</a-form-model>
|
|
||||||
</a-modal>
|
|
||||||
</a-drawer>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
|
||||||
import TargetCommentTree from './TargetCommentTree'
|
|
||||||
import apiClient from '@/utils/api-client'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'TargetCommentDrawer',
|
|
||||||
mixins: [mixin, mixinDevice],
|
|
||||||
components: { TargetCommentTree },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: {
|
|
||||||
data: [],
|
|
||||||
loading: false,
|
|
||||||
selected: {},
|
|
||||||
pagination: {
|
|
||||||
page: 1,
|
|
||||||
size: 10,
|
|
||||||
sort: null,
|
|
||||||
total: 1
|
|
||||||
},
|
|
||||||
queryParam: {
|
|
||||||
page: 0,
|
|
||||||
size: 10,
|
|
||||||
sort: null,
|
|
||||||
keyword: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
replyModal: {
|
|
||||||
model: {},
|
|
||||||
visible: false,
|
|
||||||
saving: false,
|
|
||||||
saveErrored: false,
|
|
||||||
rules: {
|
|
||||||
content: [{ required: true, message: '* 内容不能为空', trigger: ['change'] }]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
visible: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
target: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
id: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
replyModalTitle() {
|
|
||||||
return this.list.selected.id ? `回复给:${this.list.selected.author}` : '评论'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleListComments() {
|
|
||||||
this.list.loading = true
|
|
||||||
this.list.queryParam.page = this.list.pagination.page - 1
|
|
||||||
this.list.queryParam.size = this.list.pagination.size
|
|
||||||
this.list.queryParam.sort = this.list.pagination.sort
|
|
||||||
apiClient.comment
|
|
||||||
.listAsTreeView(this.target, this.id, this.list.queryParam)
|
|
||||||
.then(response => {
|
|
||||||
this.list.data = response.data.content
|
|
||||||
this.list.pagination.total = response.data.total
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.list.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handlePaginationChange(page, pageSize) {
|
|
||||||
this.list.pagination.page = page
|
|
||||||
this.list.pagination.size = pageSize
|
|
||||||
this.handleListComments()
|
|
||||||
},
|
|
||||||
handleCommentReply(comment) {
|
|
||||||
this.list.selected = comment
|
|
||||||
this.replyModal.visible = true
|
|
||||||
this.replyModal.model.parentId = comment.id
|
|
||||||
this.replyModal.model.postId = this.id
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.contentInput.focus()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleReplyClick() {
|
|
||||||
const _this = this
|
|
||||||
_this.$refs.replyCommentForm.validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
_this.replyModal.saving = true
|
|
||||||
apiClient.comment
|
|
||||||
.create(_this.target, _this.replyModal.model)
|
|
||||||
.catch(() => {
|
|
||||||
_this.replyModal.saveErrored = true
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
_this.replyModal.saving = false
|
|
||||||
}, 400)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleReplyCallback() {
|
|
||||||
if (this.replyModal.saveErrored) {
|
|
||||||
this.replyModal.saveErrored = false
|
|
||||||
} else {
|
|
||||||
this.replyModal.model = {}
|
|
||||||
this.list.selected = {}
|
|
||||||
this.replyModal.visible = false
|
|
||||||
this.handleListComments()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleEditStatusClick(comment, status) {
|
|
||||||
apiClient.comment
|
|
||||||
.updateStatusById(this.target, comment.id, status)
|
|
||||||
.then(() => {
|
|
||||||
this.$message.success('操作成功!')
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.handleListComments()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleCommentDelete(comment) {
|
|
||||||
apiClient.comment
|
|
||||||
.delete(this.target, comment.id)
|
|
||||||
.then(() => {
|
|
||||||
this.$message.success('删除成功!')
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.handleListComments()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onReplyModalClose() {
|
|
||||||
this.replyModal.model = {}
|
|
||||||
this.list.selected = {}
|
|
||||||
this.replyModal.visible = false
|
|
||||||
},
|
|
||||||
onClose() {
|
|
||||||
this.list.data = []
|
|
||||||
this.list.pagination = {
|
|
||||||
page: 1,
|
|
||||||
size: 10,
|
|
||||||
sort: ''
|
|
||||||
}
|
|
||||||
this.$emit('close', false)
|
|
||||||
},
|
|
||||||
handleAfterVisibleChanged(visible) {
|
|
||||||
if (visible) {
|
|
||||||
this.handleListComments()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,93 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<a-comment>
|
|
||||||
<template slot="actions">
|
|
||||||
<a-dropdown v-if="comment.status === 'AUDITING'" :trigger="['click']">
|
|
||||||
<span>通过</span>
|
|
||||||
<a-menu slot="overlay">
|
|
||||||
<a-menu-item key="1" @click="handleEditStatusClick('PUBLISHED')"> 通过</a-menu-item>
|
|
||||||
<a-menu-item key="2"> 通过并回复</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</a-dropdown>
|
|
||||||
|
|
||||||
<span v-else-if="comment.status === 'PUBLISHED'" @click="handleReplyClick">回复</span>
|
|
||||||
|
|
||||||
<a-popconfirm
|
|
||||||
v-else-if="comment.status === 'RECYCLE'"
|
|
||||||
:title="'你确定要还原该评论?'"
|
|
||||||
cancelText="取消"
|
|
||||||
okText="确定"
|
|
||||||
@confirm="handleEditStatusClick('PUBLISHED')"
|
|
||||||
>
|
|
||||||
还原
|
|
||||||
</a-popconfirm>
|
|
||||||
|
|
||||||
<a-popconfirm
|
|
||||||
v-if="comment.status === 'PUBLISHED' || comment.status === 'AUDITING'"
|
|
||||||
:title="'你确定要将该评论移到回收站?'"
|
|
||||||
cancelText="取消"
|
|
||||||
okText="确定"
|
|
||||||
@confirm="handleEditStatusClick('RECYCLE')"
|
|
||||||
>
|
|
||||||
回收站
|
|
||||||
</a-popconfirm>
|
|
||||||
|
|
||||||
<a-popconfirm :title="'你确定要永久删除该评论?'" cancelText="取消" okText="确定" @confirm="handleDeleteClick">
|
|
||||||
删除
|
|
||||||
</a-popconfirm>
|
|
||||||
</template>
|
|
||||||
<a slot="author" :href="comment.authorUrl" target="_blank">
|
|
||||||
<a-icon v-if="comment.isAdmin" style="margin-right: 3px" type="user" />
|
|
||||||
{{ comment.author }}
|
|
||||||
</a>
|
|
||||||
<a-avatar slot="avatar" :alt="comment.author" :src="comment.avatar" size="large" />
|
|
||||||
<p slot="content" v-html="content"></p>
|
|
||||||
<a-tooltip slot="datetime">
|
|
||||||
<span slot="title">{{ comment.createTime | moment }}</span>
|
|
||||||
<span>{{ comment.createTime | timeAgo }}</span>
|
|
||||||
</a-tooltip>
|
|
||||||
<template v-if="comment.children">
|
|
||||||
<TargetCommentTree
|
|
||||||
v-for="(child, index) in comment.children"
|
|
||||||
:key="index"
|
|
||||||
:comment="child"
|
|
||||||
v-bind="$attrs"
|
|
||||||
@delete="handleDeleteClick"
|
|
||||||
@editStatus="handleEditStatusClick"
|
|
||||||
@reply="handleReplyClick"
|
|
||||||
v-on="$listeners"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</a-comment>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import { marked } from 'marked'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'TargetCommentTree',
|
|
||||||
props: {
|
|
||||||
comment: {
|
|
||||||
type: Object,
|
|
||||||
required: false,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
content() {
|
|
||||||
return marked.parse(this.comment.content)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleReplyClick() {
|
|
||||||
this.$emit('reply', this.comment)
|
|
||||||
},
|
|
||||||
handleEditStatusClick(status) {
|
|
||||||
this.$emit('editStatus', this.comment, status)
|
|
||||||
},
|
|
||||||
handleDeleteClick() {
|
|
||||||
this.$emit('delete', this.comment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -408,14 +408,18 @@
|
||||||
</template>
|
</template>
|
||||||
</PostSettingModal>
|
</PostSettingModal>
|
||||||
|
|
||||||
<TargetCommentDrawer
|
<TargetCommentListModal
|
||||||
:id="selectedPost.id"
|
:target-id="selectedPost.id"
|
||||||
:description="selectedPost.summary"
|
:title="`「${selectedPost.title}」的评论`"
|
||||||
:target="`posts`"
|
:visible.sync="postCommentVisible"
|
||||||
:title="selectedPost.title"
|
target="post"
|
||||||
:visible="postCommentVisible"
|
|
||||||
@close="onPostCommentsClose"
|
@close="onPostCommentsClose"
|
||||||
/>
|
>
|
||||||
|
<template #extraFooter>
|
||||||
|
<a-button :disabled="selectPreviousButtonDisabled" @click="handleSelectPrevious"> 上一篇</a-button>
|
||||||
|
<a-button :disabled="selectNextButtonDisabled" @click="handleSelectNext"> 下一篇</a-button>
|
||||||
|
</template>
|
||||||
|
</TargetCommentListModal>
|
||||||
</page-view>
|
</page-view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -423,7 +427,7 @@
|
||||||
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
||||||
import { PageView } from '@/layouts'
|
import { PageView } from '@/layouts'
|
||||||
import PostSettingModal from './components/PostSettingModal.vue'
|
import PostSettingModal from './components/PostSettingModal.vue'
|
||||||
import TargetCommentDrawer from '../comment/components/TargetCommentDrawer'
|
import TargetCommentListModal from '@/components/Comment/TargetCommentListModal'
|
||||||
import apiClient from '@/utils/api-client'
|
import apiClient from '@/utils/api-client'
|
||||||
import { postStatuses } from '@/core/constant'
|
import { postStatuses } from '@/core/constant'
|
||||||
|
|
||||||
|
@ -480,7 +484,7 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
PageView,
|
PageView,
|
||||||
PostSettingModal,
|
PostSettingModal,
|
||||||
TargetCommentDrawer
|
TargetCommentListModal
|
||||||
},
|
},
|
||||||
mixins: [mixin, mixinDevice],
|
mixins: [mixin, mixinDevice],
|
||||||
data() {
|
data() {
|
||||||
|
@ -733,12 +737,11 @@ export default {
|
||||||
onPostSavedCallback() {
|
onPostSavedCallback() {
|
||||||
this.handleListPosts(false)
|
this.handleListPosts(false)
|
||||||
},
|
},
|
||||||
|
|
||||||
onPostCommentsClose() {
|
onPostCommentsClose() {
|
||||||
this.postCommentVisible = false
|
this.postCommentVisible = false
|
||||||
this.selectedPost = {}
|
this.selectedPost = {}
|
||||||
setTimeout(() => {
|
this.handleListPosts(false)
|
||||||
this.handleListPosts(false)
|
|
||||||
}, 500)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -254,20 +254,24 @@
|
||||||
<a-button :disabled="selectNextButtonDisabled" @click="handleSelectNext"> 下一篇</a-button>
|
<a-button :disabled="selectNextButtonDisabled" @click="handleSelectNext"> 下一篇</a-button>
|
||||||
</template>
|
</template>
|
||||||
</SheetSettingModal>
|
</SheetSettingModal>
|
||||||
<TargetCommentDrawer
|
<TargetCommentListModal
|
||||||
:id="selectedSheet.id"
|
:target-id="selectedSheet.id"
|
||||||
:description="selectedSheet.summary"
|
:title="`「${selectedSheet.title}」的评论`"
|
||||||
:target="`sheets`"
|
:visible.sync="sheetCommentVisible"
|
||||||
:title="selectedSheet.title"
|
target="sheet"
|
||||||
:visible="sheetCommentVisible"
|
|
||||||
@close="onSheetCommentsClose"
|
@close="onSheetCommentsClose"
|
||||||
/>
|
>
|
||||||
|
<template #extraFooter>
|
||||||
|
<a-button :disabled="selectPreviousButtonDisabled" @click="handleSelectPrevious"> 上一篇</a-button>
|
||||||
|
<a-button :disabled="selectNextButtonDisabled" @click="handleSelectNext"> 下一篇</a-button>
|
||||||
|
</template>
|
||||||
|
</TargetCommentListModal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
||||||
import SheetSettingModal from './SheetSettingModal'
|
import SheetSettingModal from './SheetSettingModal'
|
||||||
import TargetCommentDrawer from '../../comment/components/TargetCommentDrawer'
|
import TargetCommentListModal from '@/components/Comment/TargetCommentListModal'
|
||||||
import apiClient from '@/utils/api-client'
|
import apiClient from '@/utils/api-client'
|
||||||
import { sheetStatuses } from '@/core/constant'
|
import { sheetStatuses } from '@/core/constant'
|
||||||
|
|
||||||
|
@ -310,7 +314,7 @@ export default {
|
||||||
mixins: [mixin, mixinDevice],
|
mixins: [mixin, mixinDevice],
|
||||||
components: {
|
components: {
|
||||||
SheetSettingModal,
|
SheetSettingModal,
|
||||||
TargetCommentDrawer
|
TargetCommentListModal
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -445,9 +449,7 @@ export default {
|
||||||
onSheetCommentsClose() {
|
onSheetCommentsClose() {
|
||||||
this.sheetCommentVisible = false
|
this.sheetCommentVisible = false
|
||||||
this.selectedSheet = {}
|
this.selectedSheet = {}
|
||||||
setTimeout(() => {
|
this.handleListSheets(false)
|
||||||
this.handleListSheets(false)
|
|
||||||
}, 500)
|
|
||||||
},
|
},
|
||||||
onSheetSavedCallback() {
|
onSheetSavedCallback() {
|
||||||
this.handleListSheets(false)
|
this.handleListSheets(false)
|
||||||
|
|
|
@ -154,22 +154,27 @@
|
||||||
</a-form-model>
|
</a-form-model>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<TargetCommentDrawer
|
|
||||||
:id="list.selected.id"
|
|
||||||
:description="list.selected.content"
|
|
||||||
:target="`journals`"
|
|
||||||
:visible="journalCommentDrawer.visible"
|
|
||||||
@close="onJournalCommentsDrawerClose"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<AttachmentSelectModal :visible.sync="attachmentSelect.visible" @confirm="handleSelectAttachment" />
|
<AttachmentSelectModal :visible.sync="attachmentSelect.visible" @confirm="handleSelectAttachment" />
|
||||||
|
|
||||||
|
<TargetCommentListModal
|
||||||
|
:target-id="list.selected.id"
|
||||||
|
:title="`「${$options.filters.moment(list.selected.createTime)}」的评论`"
|
||||||
|
:visible.sync="journalCommentDrawer.visible"
|
||||||
|
target="journal"
|
||||||
|
@close="onJournalCommentsDrawerClose"
|
||||||
|
>
|
||||||
|
<template #extraFooter>
|
||||||
|
<a-button :disabled="selectPreviousButtonDisabled" @click="handleSelectPrevious"> 上一篇</a-button>
|
||||||
|
<a-button :disabled="selectNextButtonDisabled" @click="handleSelectNext"> 下一篇</a-button>
|
||||||
|
</template>
|
||||||
|
</TargetCommentListModal>
|
||||||
</page-view>
|
</page-view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// components
|
// components
|
||||||
import { PageView } from '@/layouts'
|
import { PageView } from '@/layouts'
|
||||||
import TargetCommentDrawer from '../../comment/components/TargetCommentDrawer'
|
import TargetCommentListModal from '@/components/Comment/TargetCommentListModal'
|
||||||
|
|
||||||
// libs
|
// libs
|
||||||
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
||||||
|
@ -178,7 +183,7 @@ import apiClient from '@/utils/api-client'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [mixin, mixinDevice],
|
mixins: [mixin, mixinDevice],
|
||||||
components: { PageView, TargetCommentDrawer },
|
components: { PageView, TargetCommentListModal },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
list: {
|
list: {
|
||||||
|
@ -191,6 +196,8 @@ export default {
|
||||||
keyword: undefined,
|
keyword: undefined,
|
||||||
type: undefined
|
type: undefined
|
||||||
},
|
},
|
||||||
|
hasPrevious: false,
|
||||||
|
hasNext: false,
|
||||||
selected: {},
|
selected: {},
|
||||||
journalType: {
|
journalType: {
|
||||||
PUBLIC: {
|
PUBLIC: {
|
||||||
|
@ -239,6 +246,14 @@ export default {
|
||||||
size: this.list.params.size,
|
size: this.list.params.size,
|
||||||
total: this.list.total
|
total: this.list.total
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
selectPreviousButtonDisabled() {
|
||||||
|
const index = this.list.data.findIndex(journal => journal.id === this.list.selected.id)
|
||||||
|
return index === 0 && !this.list.hasPrevious
|
||||||
|
},
|
||||||
|
selectNextButtonDisabled() {
|
||||||
|
const index = this.list.data.findIndex(journal => journal.id === this.list.selected.id)
|
||||||
|
return index === this.list.data.length - 1 && !this.list.hasNext
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -251,6 +266,8 @@ export default {
|
||||||
|
|
||||||
this.list.data = data.content
|
this.list.data = data.content
|
||||||
this.list.total = data.total
|
this.list.total = data.total
|
||||||
|
this.list.hasPrevious = data.hasPrevious
|
||||||
|
this.list.hasNext = data.hasNext
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$log.error(e)
|
this.$log.error(e)
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -356,6 +373,7 @@ export default {
|
||||||
onJournalCommentsDrawerClose() {
|
onJournalCommentsDrawerClose() {
|
||||||
this.form.model = {}
|
this.form.model = {}
|
||||||
this.journalCommentDrawer.visible = false
|
this.journalCommentDrawer.visible = false
|
||||||
|
this.handleListJournals()
|
||||||
},
|
},
|
||||||
handleSaveOptions() {
|
handleSaveOptions() {
|
||||||
apiClient.option
|
apiClient.option
|
||||||
|
@ -371,6 +389,38 @@ export default {
|
||||||
},
|
},
|
||||||
handleSelectAttachment({ markdown }) {
|
handleSelectAttachment({ markdown }) {
|
||||||
this.$set(this.form.model, 'sourceContent', (this.form.model.sourceContent || '') + '\n' + markdown.join('\n'))
|
this.$set(this.form.model, 'sourceContent', (this.form.model.sourceContent || '') + '\n' + markdown.join('\n'))
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select previous journal
|
||||||
|
*/
|
||||||
|
async handleSelectPrevious() {
|
||||||
|
const index = this.list.data.findIndex(journal => journal.id === this.list.selected.id)
|
||||||
|
if (index > 0) {
|
||||||
|
this.list.selected = this.list.data[index - 1]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (index === 0 && this.list.hasPrevious) {
|
||||||
|
this.list.params.page--
|
||||||
|
await this.handleListJournals()
|
||||||
|
this.list.selected = this.list.data[this.list.data.length - 1]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select next journal
|
||||||
|
*/
|
||||||
|
async handleSelectNext() {
|
||||||
|
const index = this.list.data.findIndex(journal => journal.id === this.list.selected.id)
|
||||||
|
if (index < this.list.data.length - 1) {
|
||||||
|
this.list.selected = this.list.data[index + 1]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (index === this.list.data.length - 1 && this.list.hasNext) {
|
||||||
|
this.list.params.page++
|
||||||
|
await this.handleListJournals()
|
||||||
|
this.list.selected = this.list.data[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue