refactor: form verification. (halo-dev/console#250)

* refactor: form verification.

* refactor: form verification.

* refactor: form verification.
pull/3445/head
Ryan Wang 2020-09-23 00:20:47 +08:00 committed by GitHub
parent 37ca43ad78
commit ab0617251f
5 changed files with 544 additions and 395 deletions

View File

@ -845,7 +845,7 @@ body {
.journal-list-content, .journal-list-content,
.comment-drawer-content { .comment-drawer-content {
img { img {
width: 100%; width: 50%;
} }
} }

View File

@ -15,7 +15,7 @@
> >
<a-form-item label="关键词:"> <a-form-item label="关键词:">
<a-input <a-input
v-model="queryParam.keyword" v-model="list.queryParam.keyword"
@keyup.enter="handleQuery()" @keyup.enter="handleQuery()"
/> />
</a-form-item> </a-form-item>
@ -27,14 +27,14 @@
<a-form-item label="状态:"> <a-form-item label="状态:">
<a-select <a-select
placeholder="请选择状态" placeholder="请选择状态"
v-model="queryParam.type" v-model="list.queryParam.type"
@change="handleQuery()" @change="handleQuery()"
> >
<a-select-option <a-select-option
v-for="type in Object.keys(journalType)" v-for="type in Object.keys(list.journalType)"
:key="type" :key="type"
:value="type" :value="type"
>{{ journalType[type].text }}</a-select-option> >{{ list.journalType[type].text }}</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
@ -48,7 +48,7 @@
type="primary" type="primary"
@click="handleQuery()" @click="handleQuery()"
>查询</a-button> >查询</a-button>
<a-button @click="resetParam()"></a-button> <a-button @click="handleResetParam()"></a-button>
</a-space> </a-space>
</span> </span>
</a-col> </a-col>
@ -59,18 +59,18 @@
<a-button <a-button
type="primary" type="primary"
icon="plus" icon="plus"
@click="handleNew" @click="handleOpenPublishModal"
>写日志</a-button> >写日志</a-button>
</div> </div>
<a-divider /> <a-divider />
<div class="mt-4"> <div class="mt-4">
<a-empty v-if="!listLoading && journals.length==0" /> <a-empty v-if="!list.loading && list.data.length==0" />
<a-list <a-list
v-else v-else
itemLayout="vertical" itemLayout="vertical"
:pagination="false" :pagination="false"
:dataSource="journals" :dataSource="list.data"
:loading="listLoading" :loading="list.loading"
> >
<a-list-item <a-list-item
slot="renderItem" slot="renderItem"
@ -87,7 +87,7 @@
<span> <span>
<a <a
href="javascript:void(0);" href="javascript:void(0);"
@click="handleShowJournalComments(item)" @click="handleOpenJournalCommentsDrawer(item)"
> >
<a-icon type="message" /> <a-icon type="message" />
{{ item.commentCount }} {{ item.commentCount }}
@ -110,7 +110,7 @@
<template slot="extra"> <template slot="extra">
<a <a
href="javascript:void(0);" href="javascript:void(0);"
@click="handleEdit(item)" @click="handleOpenEditModal(item)"
>编辑</a> >编辑</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popconfirm <a-popconfirm
@ -141,9 +141,9 @@
<div class="page-wrapper"> <div class="page-wrapper">
<a-pagination <a-pagination
class="pagination" class="pagination"
:current="pagination.page" :current="list.pagination.page"
:total="pagination.total" :total="list.pagination.total"
:defaultPageSize="pagination.size" :defaultPageSize="list.pagination.size"
:pageSizeOptions="['1', '2', '5', '10', '20', '50', '100']" :pageSizeOptions="['1', '2', '5', '10', '20', '50', '100']"
showSizeChanger showSizeChanger
@showSizeChange="handlePaginationChange" @showSizeChange="handlePaginationChange"
@ -163,13 +163,13 @@
shape="circle" shape="circle"
icon="setting" icon="setting"
size="large" size="large"
@click="optionFormVisible=true" @click="optionModal.visible=true"
></a-button> ></a-button>
</div> </div>
<a-modal <a-modal
v-model="optionFormVisible" v-model="optionModal.visible"
title="页面设置" title="页面设置"
:afterClose="() => optionFormVisible = false" :afterClose="() => optionModal.visible = false"
> >
<template slot="footer"> <template slot="footer">
<a-button <a-button
@ -183,11 +183,11 @@
label="页面标题:" label="页面标题:"
help="* 需要主题进行适配" help="* 需要主题进行适配"
> >
<a-input v-model="options.journals_title" /> <a-input v-model="optionModal.options.journals_title" />
</a-form-item> </a-form-item>
<a-form-item label="每页显示条数:"> <a-form-item label="每页显示条数:">
<a-input-number <a-input-number
v-model="options.journals_page_size" v-model="optionModal.options.journals_page_size"
style="width:100%" style="width:100%"
/> />
</a-form-item> </a-form-item>
@ -195,9 +195,9 @@
</a-modal> </a-modal>
<!-- 编辑日志弹窗 --> <!-- 编辑日志弹窗 -->
<a-modal v-model="visible"> <a-modal v-model="form.visible">
<template slot="title"> <template slot="title">
{{ title }} {{ formTitle }}
<a-tooltip <a-tooltip
slot="action" slot="action"
title="只能输入250字" title="只能输入250字"
@ -208,47 +208,52 @@
<template slot="footer"> <template slot="footer">
<a-button <a-button
type="dashed" type="dashed"
@click="attachmentDrawerVisible = true" @click="attachmentDrawer.visible = true"
>附件库</a-button> >附件库</a-button>
<ReactiveButton <ReactiveButton
type="primary" type="primary"
@click="createOrUpdateJournal" @click="handleSaveOrUpdate"
@callback="handleSavedCallback" @callback="handleSaveOrUpdateCallback"
:loading="saving" :loading="form.saving"
:errored="errored" :errored="form.saveErrored"
text="发布" text="发布"
loadedText="发布成功" loadedText="发布成功"
erroredText="发布失败" erroredText="发布失败"
></ReactiveButton> ></ReactiveButton>
</template> </template>
<a-form layout="vertical"> <a-form-model
<a-form-item> ref="journalForm"
:model="form.model"
:rules="form.rules"
layout="vertical"
>
<a-form-model-item prop="sourceContent">
<a-input <a-input
type="textarea" type="textarea"
:autoSize="{ minRows: 8 }" :autoSize="{ minRows: 8 }"
v-model="journal.sourceContent" v-model="form.model.sourceContent"
/> />
</a-form-item> </a-form-model-item>
<a-form-item> <a-form-model-item>
<a-switch <a-switch
checkedChildren="公开" checkedChildren="公开"
unCheckedChildren="私密" unCheckedChildren="私密"
v-model="isPublic" v-model="form.isPublic"
defaultChecked defaultChecked
/> />
</a-form-item> </a-form-model-item>
</a-form> </a-form-model>
</a-modal> </a-modal>
<TargetCommentDrawer <TargetCommentDrawer
:visible="journalCommentVisible" :visible="journalCommentDrawer.visible"
:description="journal.content" :description="list.selected.content"
:target="`journals`" :target="`journals`"
:id="journal.id" :id="list.selected.id"
@close="onJournalCommentsClose" @close="onJournalCommentsDrawerClose"
/> />
<AttachmentDrawer v-model="attachmentDrawerVisible" /> <AttachmentDrawer v-model="attachmentDrawer.visible" />
</div> </div>
</template> </template>
@ -264,166 +269,172 @@ export default {
components: { TargetCommentDrawer, AttachmentDrawer }, components: { TargetCommentDrawer, AttachmentDrawer },
data() { data() {
return { return {
journalType: journalApi.journalType, list: {
title: '发表', data: [],
listLoading: false, loading: false,
visible: false,
journalCommentVisible: false,
attachmentDrawerVisible: false,
optionFormVisible: false,
pagination: { pagination: {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1 total: 1,
}, },
queryParam: { queryParam: {
page: 0, page: 0,
size: 10, size: 10,
sort: null, sort: null,
keyword: null, keyword: null,
type: null type: null,
}, },
journals: [], selected: {},
comments: [],
journal: {}, journalType: journalApi.journalType,
isPublic: true, },
replyComment: {},
options: [], form: {
model: {},
rules: {
sourceContent: [{ required: true, message: '* 内容不能为空', trigger: ['change'] }],
},
visible: false,
saving: false, saving: false,
errored: false saveErrored: false,
isPublic: true,
},
journalCommentDrawer: {
visible: false,
},
attachmentDrawer: {
visible: false,
},
optionModal: {
visible: false,
options: [],
},
} }
}, },
created() { beforeMount() {
this.hanldeListJournals() this.hanldeListJournals()
this.hanldeListOptions() this.hanldeListOptions()
}, },
computed: { computed: {
...mapGetters(['user']) ...mapGetters(['user']),
formTitle() {
return this.form.model.id ? '编辑' : '发表'
},
}, },
methods: { methods: {
...mapActions(['refreshOptionsCache']), ...mapActions(['refreshOptionsCache']),
hanldeListJournals() { hanldeListJournals() {
this.listLoading = true this.list.loading = true
this.queryParam.page = this.pagination.page - 1 this.list.queryParam.page = this.list.pagination.page - 1
this.queryParam.size = this.pagination.size this.list.queryParam.size = this.list.pagination.size
this.queryParam.sort = this.pagination.sort this.list.queryParam.sort = this.list.pagination.sort
journalApi journalApi
.query(this.queryParam) .query(this.list.queryParam)
.then(response => { .then((response) => {
this.journals = response.data.data.content this.list.data = response.data.data.content
this.pagination.total = response.data.data.total this.list.pagination.total = response.data.data.total
}) })
.finally(() => { .finally(() => {
setTimeout(() => { setTimeout(() => {
this.listLoading = false this.list.loading = false
}, 200) }, 200)
}) })
}, },
hanldeListOptions() { hanldeListOptions() {
optionApi.listAll().then(response => { optionApi.listAll().then((response) => {
this.options = response.data.data this.optionModal.options = response.data.data
}) })
}, },
handleQuery() { handleQuery() {
this.handlePaginationChange(1, this.pagination.size) this.handlePaginationChange(1, this.list.pagination.size)
}, },
handleNew() { handleResetParam() {
this.title = '新建' this.list.queryParam.keyword = null
this.visible = true this.list.queryParam.type = null
this.journal = {} this.handlePaginationChange(1, this.list.pagination.size)
}, },
handleEdit(item) { handleOpenPublishModal() {
this.title = '编辑' this.form.visible = true
this.journal = item this.form.model = {}
this.isPublic = item.type !== 'INTIMATE' },
this.visible = true handleOpenEditModal(item) {
this.form.model = item
this.form.isPublic = item.type !== 'INTIMATE'
this.form.visible = true
}, },
handleDelete(id) { handleDelete(id) {
journalApi journalApi.delete(id).finally(() => {
.delete(id)
.then(response => {
this.$message.success('删除成功!')
})
.finally(() => {
this.hanldeListJournals() this.hanldeListJournals()
}) })
}, },
handleShowJournalComments(journal) { handleOpenJournalCommentsDrawer(journal) {
this.journal = journal this.list.selected = journal
this.journalCommentVisible = true this.journalCommentDrawer.visible = true
}, },
createOrUpdateJournal() { handleSaveOrUpdate() {
this.journal.type = this.isPublic ? 'PUBLIC' : 'INTIMATE' const _this = this
_this.$refs.journalForm.validate((valid) => {
if (!this.journal.sourceContent) { if (valid) {
this.$notification['error']({ _this.form.model.type = _this.form.isPublic ? 'PUBLIC' : 'INTIMATE'
message: '提示', _this.form.saving = true
description: '发布内容不能为空!' if (_this.form.model.id) {
})
return
}
this.saving = true
if (this.journal.id) {
journalApi journalApi
.update(this.journal.id, this.journal) .update(_this.form.model.id, _this.form.model)
.catch(() => { .catch(() => {
this.errored = true _this.form.saveErrored = true
}) })
.finally(() => { .finally(() => {
setTimeout(() => { setTimeout(() => {
this.saving = false _this.form.saving = false
}, 400) }, 400)
}) })
} else { } else {
journalApi journalApi
.create(this.journal) .create(_this.form.model)
.catch(() => { .catch(() => {
this.errored = true _this.form.saveErrored = true
}) })
.finally(() => { .finally(() => {
setTimeout(() => { setTimeout(() => {
this.saving = false _this.form.saving = false
}, 400) }, 400)
}) })
} }
}
})
}, },
handleSavedCallback() { handleSaveOrUpdateCallback() {
if (this.errored) { if (this.form.saveErrored) {
this.errored = false this.form.saveErrored = false
} else { } else {
this.isPublic = true this.form.isPublic = true
this.visible = false this.form.visible = false
this.hanldeListJournals() this.hanldeListJournals()
} }
}, },
handlePaginationChange(page, pageSize) { handlePaginationChange(page, pageSize) {
this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`) this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`)
this.pagination.page = page this.list.pagination.page = page
this.pagination.size = pageSize this.list.pagination.size = pageSize
this.hanldeListJournals() this.hanldeListJournals()
}, },
onJournalCommentsClose() { onJournalCommentsDrawerClose() {
this.journal = {} this.form.model = {}
this.journalCommentVisible = false this.journalCommentDrawer.visible = false
},
resetParam() {
this.queryParam.keyword = null
this.queryParam.type = null
this.handlePaginationChange(1, this.pagination.size)
}, },
handleSaveOptions() { handleSaveOptions() {
optionApi optionApi
.save(this.options) .save(this.optionModal.options)
.then(response => { .then((response) => {
this.$message.success('保存成功!') this.$message.success('保存成功!')
this.optionFormVisible = false this.optionModal.visible = false
}) })
.finally(() => { .finally(() => {
this.hanldeListOptions() this.hanldeListOptions()
this.refreshOptionsCache() this.refreshOptionsCache()
}) })
} },
} },
} }
</script> </script>

View File

@ -59,7 +59,7 @@
<a-button <a-button
type="primary" type="primary"
icon="plus" icon="plus"
@click="formVisible=true" @click="form.visible=true"
>新增</a-button> >新增</a-button>
</div> </div>
<div class="mt-4"> <div class="mt-4">
@ -134,35 +134,50 @@
</div> </div>
</a-card> </a-card>
<a-modal <a-modal
v-model="formVisible" v-model="form.visible"
:title="formTitle" :title="formTitle"
:afterClose="onFormClose" :afterClose="onFormClose"
> >
<template slot="footer"> <template slot="footer">
<a-button <ReactiveButton
key="submit" @click="handleSaveOrUpdate"
type="primary" @callback="handleSaveOrUpdateCallback"
@click="createOrUpdateOption()" :loading="form.saving"
>保存</a-button> :errored="form.saveErrored"
text="保存"
loadedText="保存成功"
erroredText="保存失败"
></ReactiveButton>
</template> </template>
<a-alert <a-alert
v-if="optionToStage.type === optionType.INTERNAL.value" v-if="form.model.type === optionType.INTERNAL.value"
message="注意:在不知道系统变量的具体用途时,请不要随意修改!" message="注意:在不知道系统变量的具体用途时,请不要随意修改!"
banner banner
closable closable
/> />
<a-form layout="vertical"> <a-form-model
<a-form-item label="Key"> ref="optionForm"
<a-input v-model="optionToStage.key" /> :model="form.model"
</a-form-item> :rules="form.rules"
<a-form-item label="Value"> layout="vertical"
>
<a-form-model-item
prop="key"
label="Key"
>
<a-input v-model="form.model.key" />
</a-form-model-item>
<a-form-model-item
prop="value"
label="Value"
>
<a-input <a-input
type="textarea" type="textarea"
:autoSize="{ minRows: 5 }" :autoSize="{ minRows: 5 }"
v-model="optionToStage.value" v-model="form.model.value"
/> />
</a-form-item> </a-form-model-item>
</a-form> </a-form-model>
</a-modal> </a-modal>
</div> </div>
</template> </template>
@ -174,38 +189,38 @@ const columns = [
title: 'Key', title: 'Key',
dataIndex: 'key', dataIndex: 'key',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'key' } scopedSlots: { customRender: 'key' },
}, },
{ {
title: 'Value', title: 'Value',
dataIndex: 'value', dataIndex: 'value',
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: 'value' } scopedSlots: { customRender: 'value' },
}, },
{ {
title: '类型', title: '类型',
dataIndex: 'typeProperty', dataIndex: 'typeProperty',
width: '100px', width: '100px',
scopedSlots: { customRender: 'type' } scopedSlots: { customRender: 'type' },
}, },
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'createTime', dataIndex: 'createTime',
width: '200px', width: '200px',
scopedSlots: { customRender: 'createTime' } scopedSlots: { customRender: 'createTime' },
}, },
{ {
title: '更新时间', title: '更新时间',
dataIndex: 'updateTime', dataIndex: 'updateTime',
width: '200px', width: '200px',
scopedSlots: { customRender: 'updateTime' } scopedSlots: { customRender: 'updateTime' },
}, },
{ {
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
width: '120px', width: '120px',
scopedSlots: { customRender: 'action' } scopedSlots: { customRender: 'action' },
} },
] ]
export default { export default {
name: 'OptionsList', name: 'OptionsList',
@ -213,37 +228,46 @@ export default {
return { return {
optionType: optionApi.type, optionType: optionApi.type,
columns: columns, columns: columns,
formVisible: false,
pagination: { pagination: {
page: 1, page: 1,
size: 10, size: 10,
sort: null, sort: null,
total: 1 total: 1,
}, },
queryParam: { queryParam: {
page: 0, page: 0,
size: 10, size: 10,
sort: null, sort: null,
keyword: null, keyword: null,
type: null type: null,
}, },
optionToStage: {},
loading: false, loading: false,
options: [] options: [],
form: {
visible: false,
model: {},
rules: {
key: [{ required: true, message: '* Key 不能为空', trigger: ['change'] }],
value: [{ required: true, message: '* Value 不能为空', trigger: ['change'] }],
},
saving: false,
saveErrored: false,
},
} }
}, },
computed: { computed: {
formattedDatas() { formattedDatas() {
return this.options.map(option => { return this.options.map((option) => {
option.typeProperty = this.optionType[option.type] option.typeProperty = this.optionType[option.type]
return option return option
}) })
}, },
formTitle() { formTitle() {
return this.optionToStage.id ? '编辑' : '新增' return this.form.model.id ? '编辑' : '新增'
}
}, },
created() { },
beforeMount() {
this.hanldeListOptions() this.hanldeListOptions()
}, },
methods: { methods: {
@ -255,7 +279,7 @@ export default {
this.queryParam.sort = this.pagination.sort this.queryParam.sort = this.pagination.sort
optionApi optionApi
.query(this.queryParam) .query(this.queryParam)
.then(response => { .then((response) => {
this.options = response.data.data.content this.options = response.data.data.content
this.pagination.total = response.data.data.total this.pagination.total = response.data.data.total
}) })
@ -271,7 +295,7 @@ export default {
handleDeleteOption(id) { handleDeleteOption(id) {
optionApi optionApi
.delete(id) .delete(id)
.then(response => { .then((response) => {
this.$message.success('删除成功!') this.$message.success('删除成功!')
}) })
.finally(() => { .finally(() => {
@ -280,8 +304,8 @@ export default {
}) })
}, },
handleEditOption(option) { handleEditOption(option) {
this.optionToStage = option this.form.model = option
this.formVisible = true this.form.visible = true
}, },
handlePaginationChange(page, pageSize) { handlePaginationChange(page, pageSize) {
this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`) this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`)
@ -295,51 +319,51 @@ export default {
this.handlePaginationChange(1, this.pagination.size) this.handlePaginationChange(1, this.pagination.size)
}, },
onFormClose() { onFormClose() {
this.formVisible = false this.form.visible = false
this.optionToStage = {} this.form.model = {}
}, },
createOrUpdateOption() { handleSaveOrUpdate() {
if (!this.optionToStage.key) { const _this = this
this.$notification['error']({ _this.$refs.optionForm.validate((valid) => {
message: '提示', if (valid) {
description: 'Key 不能为空!' _this.form.saving = true
}) if (_this.form.model.id) {
return
}
if (!this.optionToStage.value) {
this.$notification['error']({
message: '提示',
description: 'Value 不能为空!'
})
return
}
if (this.optionToStage.id) {
optionApi optionApi
.update(this.optionToStage.id, this.optionToStage) .update(_this.form.model.id, _this.form.model)
.then(response => { .catch(() => {
this.$message.success('更新成功!') _this.form.saveErrored = true
this.optionToStage = {}
this.formVisible = false
}) })
.finally(() => { .finally(() => {
this.hanldeListOptions() setTimeout(() => {
this.refreshOptionsCache() _this.form.saving = false
}, 400)
}) })
} else { } else {
this.optionToStage.type = this.optionType.CUSTOM.value _this.form.model.type = _this.optionType.CUSTOM.value
optionApi optionApi
.create(this.optionToStage) .create(_this.form.model)
.then(response => { .catch(() => {
this.$message.success('保存成功!') _this.form.saveErrored = true
this.optionToStage = {}
this.formVisible = false
}) })
.finally(() => { .finally(() => {
this.hanldeListOptions() setTimeout(() => {
this.refreshOptionsCache() _this.form.saving = false
}, 400)
}) })
} }
} }
})
},
handleSaveOrUpdateCallback() {
if (this.form.saveErrored) {
this.form.saveErrored = false
} else {
this.form.model = {}
this.form.visible = false
this.hanldeListOptions()
this.refreshOptionsCache()
} }
},
},
} }
</script> </script>

View File

@ -24,10 +24,12 @@
<a-button <a-button
type="primary" type="primary"
@click="handleLoadLogsLines()" @click="handleLoadLogsLines()"
:loading="loading"
>刷新</a-button> >刷新</a-button>
<a-button <a-button
type="dashed" type="dashed"
@click="handleDownloadLogFile()" @click="handleDownloadLogFile()"
:loading="downloading"
>下载</a-button> >下载</a-button>
</a-space> </a-space>
</a-form-item> </a-form-item>
@ -41,7 +43,7 @@ import moment from 'moment'
export default { export default {
name: 'RuntimeLogs', name: 'RuntimeLogs',
components: { components: {
codemirror codemirror,
}, },
data() { data() {
return { return {
@ -49,14 +51,15 @@ export default {
tabSize: 4, tabSize: 4,
mode: 'shell', mode: 'shell',
lineNumbers: true, lineNumbers: true,
line: true line: true,
}, },
logContent: '', logContent: '',
loading: false, loading: false,
logLines: 200 logLines: 200,
downloading: false,
} }
}, },
created() { beforeMount() {
this.handleLoadLogsLines() this.handleLoadLogsLines()
}, },
updated() { updated() {
@ -68,20 +71,21 @@ export default {
this.loading = true this.loading = true
adminApi adminApi
.getLogFiles(this.logLines) .getLogFiles(this.logLines)
.then(response => { .then((response) => {
this.logContent = response.data.data this.logContent = response.data.data
}) })
.finally(() => { .finally(() => {
setTimeout(() => { setTimeout(() => {
this.loading = false this.loading = false
}, 200) }, 400)
}) })
}, },
handleDownloadLogFile() { handleDownloadLogFile() {
const hide = this.$message.loading('下载中...', 0) const hide = this.$message.loading('下载中...', 0)
this.downloading = true
adminApi adminApi
.getLogFiles(this.logLines) .getLogFiles(this.logLines)
.then(response => { .then((response) => {
var blob = new Blob([response.data.data]) var blob = new Blob([response.data.data])
var downloadElement = document.createElement('a') var downloadElement = document.createElement('a')
var href = window.URL.createObjectURL(blob) var href = window.URL.createObjectURL(blob)
@ -91,15 +95,17 @@ export default {
downloadElement.click() downloadElement.click()
document.body.removeChild(downloadElement) document.body.removeChild(downloadElement)
window.URL.revokeObjectURL(href) window.URL.revokeObjectURL(href)
this.$message.success('下载成功!')
}) })
.catch(() => { .catch(() => {
this.$message.error('下载失败!') this.$message.error('下载失败!')
}) })
.finally(() => { .finally(() => {
setTimeout(() => {
this.downloading = false
hide() hide()
}, 400)
}) })
} },
} },
} }
</script> </script>

View File

@ -8,18 +8,18 @@
<a-button <a-button
type="primary" type="primary"
icon="cloud-upload" icon="cloud-upload"
@click="() => (uploadVisible = true)" @click="uploadModal.visible = true"
>上传</a-button> >上传</a-button>
<a-button <a-button
icon="plus" icon="plus"
@click="handleShowCreateFolderModal({})" @click="handleOpenCreateDirectoryModal({})"
> >
新建文件夹 新建文件夹
</a-button> </a-button>
<a-button <a-button
icon="sync" icon="sync"
@click="handleListStatics" @click="handleListStatics"
:loading="loading" :loading="list.loading"
> >
刷新 刷新
</a-button> </a-button>
@ -27,11 +27,11 @@
<div class="mt-4"> <div class="mt-4">
<a-table <a-table
:rowKey="record => record.id" :rowKey="record => record.id"
:columns="columns" :columns="list.columns"
:dataSource="sortedStatics" :dataSource="sortedStatics"
:pagination="false" :pagination="false"
size="middle" size="middle"
:loading="loading" :loading="list.loading"
> >
<span <span
slot="name" slot="name"
@ -55,7 +55,7 @@
slot-scope="text, record" slot-scope="text, record"
> >
<a <a
href="javascript:;" href="javascript:void(0);"
v-if="!record.isFile" v-if="!record.isFile"
@click="handleUpload(record)" @click="handleUpload(record)"
>上传</a> >上传</a>
@ -76,8 +76,8 @@
v-if="!record.isFile" v-if="!record.isFile"
> >
<a <a
href="javascript:;" href="javascript:void(0);"
@click="handleShowCreateFolderModal(record)" @click="handleOpenCreateDirectoryModal(record)"
>创建文件夹</a> >创建文件夹</a>
</a-menu-item> </a-menu-item>
<a-menu-item key="2"> <a-menu-item key="2">
@ -87,13 +87,13 @@
cancelText="取消" cancelText="取消"
@confirm="handleDelete(record.relativePath)" @confirm="handleDelete(record.relativePath)"
> >
<a href="javascript:;">删除</a> <a href="javascript:void(0);">删除</a>
</a-popconfirm> </a-popconfirm>
</a-menu-item> </a-menu-item>
<a-menu-item key="3"> <a-menu-item key="3">
<a <a
href="javascript:;" href="javascript:void(0);"
@click="handleShowRenameModal(record)" @click="handleOpenRenameModal(record)"
>重命名</a> >重命名</a>
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item
@ -101,8 +101,8 @@
v-if="record.isFile" v-if="record.isFile"
> >
<a <a
href="javascript:;" href="javascript:void(0);"
@click="handleShowEditModal(record)" @click="handleOpenEditContentModal(record)"
>编辑</a> >编辑</a>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
@ -113,64 +113,88 @@
</a-card> </a-card>
<a-modal <a-modal
title="上传文件" title="上传文件"
v-model="uploadVisible" v-model="uploadModal.visible"
:footer="null" :footer="null"
:afterClose="onUploadClose" :afterClose="onUploadModalClose"
destroyOnClose destroyOnClose
> >
<FilePondUpload <FilePondUpload
ref="upload" ref="upload"
name="file" name="file"
:uploadHandler="uploadHandler" :uploadHandler="uploadModal.uploadHandler"
:filed="selectedFile.relativePath" :filed="list.selected.relativePath"
></FilePondUpload> ></FilePondUpload>
</a-modal> </a-modal>
<a-modal <a-modal
v-model="createFolderModal" v-model="directoryForm.visible"
:afterClose="onCreateFolderClose" :afterClose="onDirectoryFormModalClose"
title="创建文件夹" title="创建文件夹"
> >
<template slot="footer"> <template slot="footer">
<a-button <ReactiveButton
key="submit" @click="handleCreateDirectory"
type="primary" @callback="handleCreateDirectoryCallback"
@click="handleCreateFolder()" :loading="directoryForm.saving"
>创建</a-button> :errored="directoryForm.saveErrored"
text="创建"
loadedText="创建成功"
erroredText="创建失败"
></ReactiveButton>
</template> </template>
<a-form layout="vertical"> <a-form-model
<a-form-item label="文件夹名:"> ref="directoryForm"
:model="directoryForm.model"
:rules="directoryForm.rules"
layout="vertical"
>
<a-form-model-item
prop="name"
label="文件夹名:"
>
<a-input <a-input
ref="createFoldeInput" ref="createDirectoryInput"
v-model="createFolderName" v-model="directoryForm.model.name"
@keyup.enter="handleCreateFolder" @keyup.enter="handleCreateDirectory"
/> />
</a-form-item> </a-form-model-item>
</a-form> </a-form-model>
</a-modal> </a-modal>
<a-modal <a-modal
v-model="renameModal" v-model="renameForm.visible"
:afterClose="onRenameClose" :afterClose="onRenameModalClose"
title="重命名" title="重命名"
> >
<template slot="footer"> <template slot="footer">
<a-button <ReactiveButton
key="submit" @click="handleRenameDirectoryOrFile"
type="primary" @callback="handleRenameDirectoryOrFileCallback"
@click="handleRename()" :loading="renameForm.saving"
>重命名</a-button> :errored="renameForm.saveErrored"
text="重命名"
loadedText="重命名成功"
erroredText="重命名失败"
></ReactiveButton>
</template> </template>
<a-form layout="vertical"> <a-form-model
<a-form-item :label="renameFile?'文件名:':'文件夹名:'"> ref="renameForm"
:model="renameForm.model"
:rules="renameForm.rules"
layout="vertical"
>
<a-form-model-item
prop="name"
:label="list.selected.isFile?'文件名:':'文件夹名:'"
>
<a-input <a-input
ref="renameModalInput" ref="renameModalInput"
v-model="renameName" v-model="renameForm.model.name"
@keyup.enter="handleRename" @keyup.enter="handleRenameDirectoryOrFile"
/> />
</a-form-item> </a-form-model-item>
</a-form> </a-form-model>
</a-modal> </a-modal>
<a-modal <a-modal
v-model="editModal" v-model="editContentForm.visible"
title="编辑文件" title="编辑文件"
width="80%" width="80%"
style="max-width: 1000px" style="max-width: 1000px"
@ -183,22 +207,26 @@
title="未保存的内容将会丢失,确定要退出吗?" title="未保存的内容将会丢失,确定要退出吗?"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
@confirm="handleEditClose" @confirm="handleEditContentModalClose"
> >
<a-button>取消</a-button> <a-button>取消</a-button>
</a-popconfirm> </a-popconfirm>
<a-button <ReactiveButton
key="submit" @click="handleContentEdit"
type="primary" @callback="handleContentEditCallback"
@click="handleEditSave()" :loading="editContentForm.saving"
>保存</a-button> :errored="editContentForm.saveErrored"
text="保存"
loadedText="保存成功"
erroredText="保存失败"
></ReactiveButton>
</template> </template>
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item> <a-form-item>
<codemirror <codemirror
ref="editor" ref="editor"
:value="editContent" :value="editContentForm.model.content"
:options="codemirrorOptions" :options="editContentForm.codeMirror.options"
></codemirror> ></codemirror>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -206,7 +234,6 @@
</div> </div>
</template> </template>
<script> <script>
import Vue from 'vue'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import staticApi from '@/api/static' import staticApi from '@/api/static'
import { codemirror } from 'vue-codemirror-lite' import { codemirror } from 'vue-codemirror-lite'
@ -216,86 +243,119 @@ const columns = [
{ {
title: '文件名', title: '文件名',
dataIndex: 'name', dataIndex: 'name',
scopedSlots: { customRender: 'name' } scopedSlots: { customRender: 'name' },
}, },
{ {
title: '文件类型', title: '文件类型',
dataIndex: 'mimeType', dataIndex: 'mimeType',
scopedSlots: { customRender: 'mimeType' } scopedSlots: { customRender: 'mimeType' },
}, },
{ {
title: '上传时间', title: '上传时间',
dataIndex: 'createTime', dataIndex: 'createTime',
width: '200px', width: '200px',
scopedSlots: { customRender: 'createTime' } scopedSlots: { customRender: 'createTime' },
}, },
{ {
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
width: '120px', width: '120px',
scopedSlots: { customRender: 'action' } scopedSlots: { customRender: 'action' },
} },
] ]
export default { export default {
components: { components: {
codemirror codemirror,
}, },
name: 'StaticStorage', name: 'StaticStorage',
data() { data() {
return { return {
list: {
columns: columns, columns: columns,
statics: [], data: [],
loading: false, loading: false,
selected: {},
},
uploadModal: {
visible: false,
uploadHandler: staticApi.upload, uploadHandler: staticApi.upload,
uploadVisible: false, },
selectedFile: {},
createFolderModal: false, directoryForm: {
createFolderName: '', model: {
renameModal: false, name: null,
renameName: '', },
renameFile: false, visible: false,
codemirrorOptions: { saving: false,
saveErrored: false,
rules: {
name: [{ required: true, message: '* 文件夹名不能为空', trigger: ['change'] }],
},
},
renameForm: {
model: {
name: null,
},
visible: false,
saving: false,
saveErrored: false,
rules: {
name: [{ required: true, message: '* 文件夹名不能为空', trigger: ['change'] }],
},
},
editContentForm: {
model: {
content: null,
},
visible: false,
saving: false,
saveErrored: false,
codeMirror: {
instance: null,
options: {
tabSize: 4, tabSize: 4,
lineNumbers: true, lineNumbers: true,
line: true line: true,
},
},
}, },
editModal: false,
editContent: '',
CodeMirror: null
} }
}, },
created() { beforeMount() {
this.handleListStatics() this.handleListStatics()
this.CodeMirror = require('codemirror') this.editContentForm.codeMirror.instance = require('codemirror')
this.CodeMirror.modeURL = 'codemirror/mode/%N/%N.js' this.editContentForm.codeMirror.instance.modeURL = 'codemirror/mode/%N/%N.js'
}, },
computed: { computed: {
...mapGetters(['options']), ...mapGetters(['options']),
sortedStatics() { sortedStatics() {
const data = this.statics.slice(0) const data = this.list.data.slice(0)
return data.sort(function(a, b) { return data.sort(function(a, b) {
return a.isFile - b.isFile return a.isFile - b.isFile
}) })
} },
}, },
methods: { methods: {
handleListStatics() { handleListStatics() {
this.loading = true this.list.loading = true
staticApi staticApi
.list() .list()
.then(response => { .then((response) => {
this.statics = response.data.data this.list.data = response.data.data
}) })
.finally(() => { .finally(() => {
setTimeout(() => { setTimeout(() => {
this.loading = false this.list.loading = false
}, 200) }, 200)
}) })
}, },
handleDelete(path) { handleDelete(path) {
staticApi staticApi
.delete(path) .delete(path)
.then(response => { .then((response) => {
this.$message.success(`删除成功!`) this.$message.success(`删除成功!`)
}) })
.finally(() => { .finally(() => {
@ -303,25 +363,51 @@ export default {
}) })
}, },
handleUpload(file) { handleUpload(file) {
this.selectedFile = file this.list.selected = file
this.uploadVisible = true this.uploadModal.visible = true
}, },
handleShowCreateFolderModal(file) { handleOpenCreateDirectoryModal(file) {
this.selectedFile = file const _this = this
this.createFolderModal = true _this.list.selected = file
const that = this _this.directoryForm.visible = true
Vue.nextTick().then(() => { _this.$nextTick(() => {
that.$refs.createFoldeInput.focus() _this.$refs.createDirectoryInput.focus()
}) })
}, },
handleShowRenameModal(file) { handleCreateDirectory() {
this.selectedFile = file const _this = this
this.renameName = file.name _this.$refs.directoryForm.validate((valid) => {
this.renameFile = file.isFile if (valid) {
this.renameModal = true this.directoryForm.saving = true
const that = this staticApi
Vue.nextTick().then(() => { .createFolder(_this.list.selected.relativePath, _this.directoryForm.model.name)
const inputRef = that.$refs.renameModalInput .catch(() => {
_this.directoryForm.saveErrored = true
})
.finally(() => {
setTimeout(() => {
this.directoryForm.saving = false
}, 400)
})
}
})
},
handleCreateDirectoryCallback() {
if (this.directoryForm.saveErrored) {
this.directoryForm.saveErrored = false
} else {
this.directoryForm.model = {}
this.directoryForm.visible = false
this.handleListStatics()
}
},
handleOpenRenameModal(file) {
const _this = this
_this.list.selected = file
_this.$set(_this.renameForm.model, 'name', file.name)
_this.renameForm.visible = true
_this.$nextTick(() => {
const inputRef = _this.$refs.renameModalInput
const tmp = inputRef.value.split('.') const tmp = inputRef.value.split('.')
inputRef.focus() inputRef.focus()
if (tmp.length <= 1) { if (tmp.length <= 1) {
@ -331,71 +417,93 @@ export default {
} }
}) })
}, },
handleShowEditModal(file) { handleRenameDirectoryOrFile() {
this.selectedFile = file const _this = this
_this.$refs.renameForm.validate((valid) => {
if (valid) {
this.renameForm.saving = true
staticApi
.rename(_this.list.selected.relativePath, _this.renameForm.model.name)
.catch(() => {
_this.renameForm.saveErrored = true
})
.finally(() => {
setTimeout(() => {
this.renameForm.saving = false
}, 400)
})
}
})
},
handleRenameDirectoryOrFileCallback() {
if (this.renameForm.saveErrored) {
this.renameForm.saveErrored = false
} else {
this.renameForm.model = {}
this.renameForm.visible = false
this.handleListStatics()
}
},
handleOpenEditContentModal(file) {
const _this = this
_this.list.selected = file
const arr = file.name.split('.') const arr = file.name.split('.')
const postfix = arr[arr.length - 1] const postfix = arr[arr.length - 1]
staticApi.getContent(this.options.blog_url + file.relativePath).then(response => { staticApi.getContent(_this.options.blog_url + file.relativePath).then((response) => {
this.editContent = response.data _this.editContentForm.model.content = response.data
const info = this.CodeMirror.findModeByExtension(postfix) const info = _this.editContentForm.codeMirror.instance.findModeByExtension(postfix)
if (info === undefined) { if (info === undefined) {
this.$message.error(`不支持编辑 "${postfix}" 类型的文件`) _this.$message.error(`不支持编辑 "${postfix}" 类型的文件`)
} else { } else {
this.editModal = true _this.editContentForm.visible = true
Vue.nextTick().then(() => { _this.$nextTick(() => {
const editor = this.$refs.editor.editor const editor = _this.$refs.editor.editor
editor.setOption('mode', info.mime) editor.setOption('mode', info.mime)
this.CodeMirror.autoLoadMode(editor, info.mode) _this.editContentForm.codeMirror.instance.autoLoadMode(editor, info.mode)
}) })
} }
}) })
}, },
handleCreateFolder() { handleContentEdit() {
this.editContentForm.saving = true
staticApi staticApi
.createFolder(this.selectedFile.relativePath, this.createFolderName) .save(this.list.selected.relativePath, this.$refs.editor.editor.getValue())
.then(response => { .catch(() => {
this.$message.success(`创建文件夹成功!`) this.editContentForm.saveErrored = true
this.createFolderModal = false
}) })
.finally(() => { .finally(() => {
setTimeout(() => {
this.editContentForm.saving = false
}, 400)
})
},
handleContentEditCallback() {
if (this.editContentForm.saveErrored) {
this.editContentForm.saveErrored = false
} else {
this.editContentForm.model = {}
this.editContentForm.visible = false
this.handleListStatics() this.handleListStatics()
}) }
}, },
handleRename() { onDirectoryFormModalClose() {
staticApi this.list.selected = {}
.rename(this.selectedFile.relativePath, this.renameName) this.$set(this.directoryForm.model, 'name', null)
.then(response => {
this.$message.success(`重命名成功!`)
this.renameModal = false
})
.finally(() => {
this.handleListStatics()
})
}, },
handleEditSave() { onRenameModalClose() {
staticApi.save(this.selectedFile.relativePath, this.$refs.editor.editor.getValue()).then(response => { this.list.selected = {}
this.$message.success(`文件保存成功!`) this.$set(this.renameForm.model, 'name', null)
this.editModal = false
})
}, },
onCreateFolderClose() { onUploadModalClose() {
this.selectedFile = {}
this.createFolderName = ''
},
onRenameClose() {
this.selectedFile = {}
this.renameName = ''
},
onUploadClose() {
this.$refs.upload.handleClearFileList() this.$refs.upload.handleClearFileList()
this.selectedFile = {} this.list.selected = {}
this.handleListStatics() this.handleListStatics()
}, },
handleEditClose() { handleEditContentModalClose() {
this.editModal = false this.editContentForm.visible = false
this.selectedFile = {} this.list.selected = {}
this.editContent = '' this.editContentForm.model.content = ''
} },
} },
} }
</script> </script>