refactor: post editing (halo-dev/console#439)

* refactor: post editing

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/3445/head
Ryan Wang 2022-02-20 22:38:25 +08:00 committed by GitHub
parent 8504a9bd8d
commit 6ed208ecec
4 changed files with 83 additions and 110 deletions

View File

@ -32,6 +32,7 @@
"filepond-plugin-file-validate-type": "^1.2.6", "filepond-plugin-file-validate-type": "^1.2.6",
"filepond-plugin-image-preview": "^4.6.10", "filepond-plugin-image-preview": "^4.6.10",
"halo-editor": "^2.8.4", "halo-editor": "^2.8.4",
"lodash.debounce": "^4.0.8",
"marked": "^4.0.10", "marked": "^4.0.10",
"md5.js": "^1.3.5", "md5.js": "^1.3.5",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",

View File

@ -27,6 +27,7 @@ specifiers:
less: ^3.13.1 less: ^3.13.1
less-loader: ^5.0.0 less-loader: ^5.0.0
lint-staged: ^11.2.6 lint-staged: ^11.2.6
lodash.debounce: ^4.0.8
marked: ^4.0.10 marked: ^4.0.10
md5.js: ^1.3.5 md5.js: ^1.3.5
nprogress: ^0.2.0 nprogress: ^0.2.0
@ -58,6 +59,7 @@ dependencies:
filepond-plugin-file-validate-type: 1.2.6_filepond@4.30.3 filepond-plugin-file-validate-type: 1.2.6_filepond@4.30.3
filepond-plugin-image-preview: 4.6.10_filepond@4.30.3 filepond-plugin-image-preview: 4.6.10_filepond@4.30.3
halo-editor: 2.8.4 halo-editor: 2.8.4
lodash.debounce: 4.0.8
marked: 4.0.10 marked: 4.0.10
md5.js: 1.3.5 md5.js: 1.3.5
nprogress: 0.2.0 nprogress: 0.2.0
@ -5112,7 +5114,6 @@ packages:
/lodash.debounce/4.0.8: /lodash.debounce/4.0.8:
resolution: {integrity: sha1-gteb/zCmfEAF/9XiUVMArZyk168=} resolution: {integrity: sha1-gteb/zCmfEAF/9XiUVMArZyk168=}
dev: true
/lodash.defaultsdeep/4.6.1: /lodash.defaultsdeep/4.6.1:
resolution: {integrity: sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==} resolution: {integrity: sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==}

View File

@ -1,18 +1,12 @@
<template> <template>
<page-view :title="postToStage.title ? postToStage.title : '新文章'" affix> <page-view
:title="postToStage.title ? postToStage.title : '新文章'"
:sub-title="postToStage.inProgress ? '当前内容已保存,但还未发布。' : ''"
affix
>
<template slot="extra"> <template slot="extra">
<a-space> <a-space>
<ReactiveButton <a-button :loading="previewSaving" @click="handlePreviewClick"></a-button>
:errored="draftSaveErrored"
:loading="draftSaving"
erroredText="保存失败"
loadedText="保存成功"
text="保存草稿"
type="danger"
@callback="draftSaveErrored = false"
@click="handleSaveDraft(false)"
></ReactiveButton>
<a-button :loading="previewSaving" @click="handlePreview"></a-button>
<a-button type="primary" @click="postSettingVisible = true">发布</a-button> <a-button type="primary" @click="postSettingVisible = true">发布</a-button>
</a-space> </a-space>
</template> </template>
@ -25,7 +19,7 @@
<MarkdownEditor <MarkdownEditor
:originalContent="postToStage.originalContent" :originalContent="postToStage.originalContent"
@onContentChange="onContentChange" @onContentChange="onContentChange"
@onSaveDraft="handleSaveDraft(true)" @onSaveDraft="handleSaveDraft()"
/> />
</div> </div>
</a-col> </a-col>
@ -50,6 +44,7 @@ import { PageView } from '@/layouts'
import { mixin, mixinDevice, mixinPostEdit } from '@/mixins/mixin.js' import { mixin, mixinDevice, mixinPostEdit } from '@/mixins/mixin.js'
import { datetimeFormat } from '@/utils/datetime' import { datetimeFormat } from '@/utils/datetime'
import apiClient from '@/utils/api-client' import apiClient from '@/utils/api-client'
import debounce from 'lodash.debounce'
export default { export default {
mixins: [mixin, mixinDevice, mixinPostEdit], mixins: [mixin, mixinDevice, mixinPostEdit],
@ -63,19 +58,16 @@ export default {
postSettingVisible: false, postSettingVisible: false,
postToStage: {}, postToStage: {},
contentChanges: 0, contentChanges: 0,
draftSaving: false, previewSaving: false
previewSaving: false,
draftSaveErrored: false
} }
}, },
beforeRouteEnter(to, from, next) { beforeRouteEnter(to, from, next) {
// Get post id from query // Get post id from query
const postId = to.query.postId const postId = to.query.postId
next(vm => { next(async vm => {
if (postId) { if (postId) {
apiClient.post.get(postId).then(response => { const { data } = await apiClient.post.get(Number(postId))
vm.postToStage = response.data vm.postToStage = data
})
} }
}) })
}, },
@ -110,104 +102,74 @@ export default {
} }
}, },
methods: { methods: {
handleSaveDraft(draftOnly = false) { handleSaveDraft: debounce(async function () {
this.$log.debug('Draft only: ' + draftOnly)
this.postToStage.status = 'DRAFT'
if (!this.postToStage.title) {
this.postToStage.title = datetimeFormat(new Date(), 'YYYY-MM-DD-HH-mm-ss')
}
this.draftSaving = true
if (this.postToStage.id) { if (this.postToStage.id) {
// Update the post // Update the post content
if (draftOnly) { try {
apiClient.post const { data } = await apiClient.post.updateDraftById(this.postToStage.id, this.postToStage.originalContent)
.updateDraftById(this.postToStage.id, this.postToStage.originalContent) this.postToStage.inProgress = data.inProgress
.then(() => { this.handleRestoreSavedStatus()
this.handleRestoreSavedStatus() this.$message.success({
}) content: '内容已保存',
.catch(() => { duration: 0.5
this.draftSaveErrored = true })
}) } catch (e) {
.finally(() => { this.$log.error('Failed to update post content', e)
setTimeout(() => {
this.draftSaving = false
}, 400)
})
} else {
apiClient.post
.update(this.postToStage.id, this.postToStage)
.then(response => {
this.postToStage = response.data
this.handleRestoreSavedStatus()
})
.catch(() => {
this.draftSaveErrored = true
})
.finally(() => {
setTimeout(() => {
this.draftSaving = false
}, 400)
})
} }
} else { } else {
// Create the post await this.handleCreatePost()
apiClient.post
.create(this.postToStage)
.then(response => {
this.postToStage = response.data
this.handleRestoreSavedStatus()
})
.catch(() => {
this.draftSaveErrored = true
})
.finally(() => {
setTimeout(() => {
this.draftSaving = false
}, 400)
})
} }
}, }, 300),
handlePreview() {
this.postToStage.status = 'DRAFT' async handleCreatePost() {
if (!this.postToStage.title) { if (!this.postToStage.title) {
this.postToStage.title = datetimeFormat(new Date(), 'YYYY-MM-DD-HH-mm-ss') this.postToStage.title = datetimeFormat(new Date(), 'YYYY-MM-DD-HH-mm-ss')
} }
this.previewSaving = true // Create the post
if (this.postToStage.id) { try {
// Update the post const { data } = await apiClient.post.create(this.postToStage)
apiClient.post.update(this.postToStage.id, this.postToStage).then(response => { this.postToStage = data
this.$log.debug('Updated post', response.data) this.handleRestoreSavedStatus()
apiClient.post
.getPreviewLinkById(this.postToStage.id) // add params to url
.then(response => { const path = this.$router.history.current.path
window.open(response, '_blank') this.$router.push({ path, query: { postId: this.postToStage.id } }).catch(err => err)
this.handleRestoreSavedStatus()
}) this.$message.success({
.finally(() => { content: '文章已创建',
setTimeout(() => { duration: 0.5
this.previewSaving = false
}, 400)
})
})
} else {
// Create the post
apiClient.post.create(this.postToStage).then(response => {
this.$log.debug('Created post', response.data)
this.postToStage = response.data
apiClient.post
.getPreviewLinkById(this.postToStage.id)
.then(response => {
window.open(response, '_blank')
this.handleRestoreSavedStatus()
})
.finally(() => {
setTimeout(() => {
this.previewSaving = false
}, 400)
})
}) })
} catch (e) {
this.$log.error('Failed to create post', e)
} }
}, },
async handlePreviewClick() {
this.previewSaving = true
if (this.postToStage.id) {
// Update the post content
const { data } = await apiClient.post.updateDraftById(this.postToStage.id, this.postToStage.originalContent)
this.postToStage.inProgress = data.inProgress
} else {
await this.handleCreatePost()
}
await this.handleOpenPreview()
},
async handleOpenPreview() {
try {
const response = await apiClient.post.getPreviewLinkById(this.postToStage.id)
window.open(response, '_blank')
this.handleRestoreSavedStatus()
} catch (e) {
this.$log.error('Failed to get preview link', e)
} finally {
setTimeout(() => {
this.previewSaving = false
}, 400)
}
},
handleRestoreSavedStatus() { handleRestoreSavedStatus() {
this.contentChanges = 0 this.contentChanges = 0
}, },

View File

@ -243,6 +243,15 @@
twoToneColor="red" twoToneColor="red"
type="pushpin" type="pushpin"
/> />
<a-tooltip v-if="record.inProgress" title="当前有内容已保存,但还未发布。" placement="top">
<a-icon
class="cursor-pointer"
style="margin-right: 3px"
theme="twoTone"
twoToneColor="#52c41a"
type="info-circle"
/>
</a-tooltip>
<a-tooltip <a-tooltip
v-if="['PUBLISHED', 'INTIMATE'].includes(record.status)" v-if="['PUBLISHED', 'INTIMATE'].includes(record.status)"
:title="'点击访问【' + text + '】'" :title="'点击访问【' + text + '】'"
@ -411,7 +420,7 @@ const columns = [
{ {
title: '标题', title: '标题',
dataIndex: 'title', dataIndex: 'title',
width: '150px', width: '200px',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'postTitle' } scopedSlots: { customRender: 'postTitle' }
}, },