mirror of https://github.com/halo-dev/halo
refactor: post editing (halo-dev/console#439)
* refactor: post editing Signed-off-by: Ryan Wang <i@ryanc.cc>pull/3445/head
parent
8504a9bd8d
commit
6ed208ecec
|
@ -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",
|
||||||
|
|
|
@ -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==}
|
||||||
|
|
|
@ -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
|
||||||
},
|
},
|
||||||
|
|
|
@ -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' }
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue