init
This commit is contained in:
286
src/views/ReleaseArticle/components/ArticleDetail.vue
Normal file
286
src/views/ReleaseArticle/components/ArticleDetail.vue
Normal file
@@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<div class="createPost-container">
|
||||
<el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
|
||||
|
||||
<sticky :z-index="10" :class-name="'sub-navbar '+postForm.status">
|
||||
<!-- <CommentDropdown v-model="postForm.comment_disabled" />
|
||||
<PlatformDropdown v-model="postForm.platforms" />
|
||||
<SourceUrlDropdown v-model="postForm.source_uri" /> -->
|
||||
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">
|
||||
发布
|
||||
</el-button>
|
||||
<el-button v-loading="loading" type="warning" @click="draftForm">
|
||||
保存为草稿
|
||||
</el-button>
|
||||
</sticky>
|
||||
|
||||
<div class="createPost-main-container">
|
||||
<el-row>
|
||||
|
||||
<el-col :span="24">
|
||||
<el-form-item style="margin-bottom: 40px;" prop="title">
|
||||
<MDinput v-model="postForm.title" :maxlength="100" name="name" required>
|
||||
标题
|
||||
</MDinput>
|
||||
</el-form-item>
|
||||
|
||||
<div class="postInfo-container">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label-width="60px" label="作者:" class="postInfo-container-item">
|
||||
<el-select v-model="postForm.author" :remote-method="getRemoteUserList" clearable filterable default-first-option remote placeholder="搜索作者">
|
||||
<el-option v-for="(item,index) in userListOptions" :key="item+index" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="10">
|
||||
<el-form-item label-width="120px" label="发布时间:" class="postInfo-container-item">
|
||||
<el-date-picker v-model="displayTime" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="选取时间日期" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<el-form-item label-width="90px" label="重要性:" class="postInfo-container-item">
|
||||
<el-rate
|
||||
v-model="postForm.importance"
|
||||
:max="3"
|
||||
:colors="['#99A9BF', '#F7BA2A', '#FF9900']"
|
||||
:low-threshold="1"
|
||||
:high-threshold="3"
|
||||
style="display:inline-block"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item style="margin-bottom: 40px;" label-width="70px" label="概括:">
|
||||
<el-input v-model="postForm.content_short" :rows="1" type="textarea" class="article-textarea" autosize placeholder="请输入内容" />
|
||||
<span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}words</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="content" style="margin-bottom: 30px;">
|
||||
<Tinymce ref="editor" v-model="postForm.content" :height="400" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="image_uri" style="margin-bottom: 30px;">
|
||||
<!-- <Upload v-model="postForm.image_uri" /> -->
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Tinymce from '@/components/Tinymce'
|
||||
import MDinput from '@/components/MDinput'
|
||||
import Sticky from '@/components/Sticky' // 粘性header组件
|
||||
import { validURL } from '@/utils/validate'
|
||||
import { fetchArticle } from '@/api/article'
|
||||
import { searchUser } from '@/api/remote-search'
|
||||
// import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
|
||||
|
||||
const defaultForm = {
|
||||
status: 'draft',
|
||||
title: '', // 文章题目
|
||||
content: '', // 文章内容
|
||||
content_short: '', // 文章摘要
|
||||
source_uri: '', // 文章外链
|
||||
image_uri: '', // 文章图片
|
||||
display_time: undefined, // 前台展示时间
|
||||
id: undefined,
|
||||
platforms: ['a-platform'],
|
||||
comment_disabled: false,
|
||||
importance: 0
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'ArticleDetail',
|
||||
components: { Tinymce, MDinput, Sticky },
|
||||
props: {
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const validateRequire = (rule, value, callback) => {
|
||||
if (value === '') {
|
||||
this.$message({
|
||||
message: rule.field + '为必传项',
|
||||
type: 'error'
|
||||
})
|
||||
callback(new Error(rule.field + '为必传项'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateSourceUri = (rule, value, callback) => {
|
||||
if (value) {
|
||||
if (validURL(value)) {
|
||||
callback()
|
||||
} else {
|
||||
this.$message({
|
||||
message: '外链url填写不正确',
|
||||
type: 'error'
|
||||
})
|
||||
callback(new Error('外链url填写不正确'))
|
||||
}
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
postForm: Object.assign({}, defaultForm),
|
||||
loading: false,
|
||||
userListOptions: [],
|
||||
rules: {
|
||||
image_uri: [{ validator: validateRequire }],
|
||||
title: [{ validator: validateRequire }],
|
||||
content: [{ validator: validateRequire }],
|
||||
source_uri: [{ validator: validateSourceUri, trigger: 'blur' }]
|
||||
},
|
||||
tempRoute: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
contentShortLength() {
|
||||
return this.postForm.content_short.length
|
||||
},
|
||||
displayTime: {
|
||||
// set and get is useful when the data
|
||||
// returned by the back end api is different from the front end
|
||||
// back end return => "2013-06-25 06:59:25"
|
||||
// front end need timestamp => 1372114765000
|
||||
get() {
|
||||
return (+new Date(this.postForm.display_time))
|
||||
},
|
||||
set(val) {
|
||||
this.postForm.display_time = new Date(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.isEdit) {
|
||||
const id = this.$route.params && this.$route.params.id
|
||||
this.fetchData(id)
|
||||
}
|
||||
|
||||
// Why need to make a copy of this.$route here?
|
||||
// Because if you enter this page and quickly switch tag, may be in the execution of the setTagsViewTitle function, this.$route is no longer pointing to the current page
|
||||
// https://github.com/PanJiaChen/vue-element-admin/issues/1221
|
||||
this.tempRoute = Object.assign({}, this.$route)
|
||||
},
|
||||
methods: {
|
||||
fetchData(id) {
|
||||
fetchArticle(id).then(response => {
|
||||
this.postForm = response.data
|
||||
|
||||
// just for test
|
||||
this.postForm.title += ` Article Id:${this.postForm.id}`
|
||||
this.postForm.content_short += ` Article Id:${this.postForm.id}`
|
||||
|
||||
// set tagsview title
|
||||
this.setTagsViewTitle()
|
||||
|
||||
// set page title
|
||||
this.setPageTitle()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
setTagsViewTitle() {
|
||||
const title = 'Edit Article'
|
||||
const route = Object.assign({}, this.tempRoute, { title: `${title}-${this.postForm.id}` })
|
||||
this.$store.dispatch('tagsView/updateVisitedView', route)
|
||||
},
|
||||
setPageTitle() {
|
||||
const title = 'Edit Article'
|
||||
document.title = `${title} - ${this.postForm.id}`
|
||||
},
|
||||
submitForm() {
|
||||
console.log(this.postForm)
|
||||
this.$refs.postForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '发布文章成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
this.postForm.status = 'published'
|
||||
this.loading = false
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
draftForm() {
|
||||
if (this.postForm.content.length === 0 || this.postForm.title.length === 0) {
|
||||
this.$message({
|
||||
message: '请填写必要的标题和内容',
|
||||
type: 'warning'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$message({
|
||||
message: '保存成功',
|
||||
type: 'success',
|
||||
showClose: true,
|
||||
duration: 1000
|
||||
})
|
||||
this.postForm.status = 'draft'
|
||||
},
|
||||
getRemoteUserList(query) {
|
||||
searchUser(query).then(response => {
|
||||
if (!response.data.items) return
|
||||
this.userListOptions = response.data.items.map(v => v.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/styles/mixin.scss";
|
||||
|
||||
.createPost-container {
|
||||
position: relative;
|
||||
|
||||
.createPost-main-container {
|
||||
padding: 40px 45px 20px 50px;
|
||||
|
||||
.postInfo-container {
|
||||
position: relative;
|
||||
@include clearfix;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.postInfo-container-item {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.word-counter {
|
||||
width: 40px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.article-textarea ::v-deep {
|
||||
textarea {
|
||||
padding-right: 40px;
|
||||
resize: none;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
border-bottom: 1px solid #bfcbd9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
41
src/views/ReleaseArticle/components/Dropdown/Comment.vue
Normal file
41
src/views/ReleaseArticle/components/Dropdown/Comment.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<el-dropdown :show-timeout="100" trigger="click">
|
||||
<el-button plain>
|
||||
{{ !comment_disabled?'Comment: opened':'Comment: closed' }}
|
||||
<i class="el-icon-caret-bottom el-icon--right" />
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown" class="no-padding">
|
||||
<el-dropdown-item>
|
||||
<el-radio-group v-model="comment_disabled" style="padding: 10px;">
|
||||
<el-radio :label="true">
|
||||
Close comment
|
||||
</el-radio>
|
||||
<el-radio :label="false">
|
||||
Open comment
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
comment_disabled: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
46
src/views/ReleaseArticle/components/Dropdown/Platform.vue
Normal file
46
src/views/ReleaseArticle/components/Dropdown/Platform.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click">
|
||||
<el-button plain>
|
||||
Platfroms({{ platforms.length }})
|
||||
<i class="el-icon-caret-bottom el-icon--right" />
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown" class="no-border">
|
||||
<el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
|
||||
<el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key">
|
||||
{{ item.name }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
required: true,
|
||||
default: () => [],
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
platformsOptions: [
|
||||
{ key: 'a-platform', name: 'a-platform' },
|
||||
{ key: 'b-platform', name: 'b-platform' },
|
||||
{ key: 'c-platform', name: 'c-platform' }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
platforms: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
38
src/views/ReleaseArticle/components/Dropdown/SourceUrl.vue
Normal file
38
src/views/ReleaseArticle/components/Dropdown/SourceUrl.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<el-dropdown :show-timeout="100" trigger="click">
|
||||
<el-button plain>
|
||||
Link
|
||||
<i class="el-icon-caret-bottom el-icon--right" />
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px">
|
||||
<el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
|
||||
<el-input v-model="source_uri" placeholder="Please enter the content">
|
||||
<template slot="prepend">
|
||||
URL
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
source_uri: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
3
src/views/ReleaseArticle/components/Dropdown/index.js
Normal file
3
src/views/ReleaseArticle/components/Dropdown/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as CommentDropdown } from './Comment'
|
||||
export { default as PlatformDropdown } from './Platform'
|
||||
export { default as SourceUrlDropdown } from './SourceUrl'
|
||||
13
src/views/ReleaseArticle/create.vue
Normal file
13
src/views/ReleaseArticle/create.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<article-detail :is-edit="false" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ArticleDetail from './components/ArticleDetail'
|
||||
|
||||
export default {
|
||||
name: 'CreateArticle',
|
||||
components: { ArticleDetail }
|
||||
}
|
||||
</script>
|
||||
|
||||
13
src/views/ReleaseArticle/edit.vue
Normal file
13
src/views/ReleaseArticle/edit.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<article-detail :is-edit="true" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ArticleDetail from './components/ArticleDetail'
|
||||
|
||||
export default {
|
||||
name: 'EditForm',
|
||||
components: { ArticleDetail }
|
||||
}
|
||||
</script>
|
||||
|
||||
123
src/views/ReleaseArticle/list.vue
Normal file
123
src/views/ReleaseArticle/list.vue
Normal file
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<base-table :table-config="tableProps" :table-data="list" :is-loading="listLoading">
|
||||
<method-btn slot="handleBtn" :method-list="tableBtn" @clickBtn="handleClick" />
|
||||
</base-table>
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseTable from '@/components/BaseTable'
|
||||
import { fetchList } from '@/api/article'
|
||||
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
|
||||
import CheckPic from '@/components/BaseTable/subcomponents/CheckPic'
|
||||
import MethodBtn from '@/components/BaseTable/subcomponents/MethodBtn'
|
||||
function timechange(ts) {
|
||||
return ts + 'ts'
|
||||
}
|
||||
/**
|
||||
* 表格表头配置项 TypeScript接口注释
|
||||
* tableConfig<ConfigItem> = []
|
||||
*
|
||||
* Interface ConfigItem = {
|
||||
* prop: string,
|
||||
* label: string,
|
||||
* width: string,
|
||||
* align: string,
|
||||
* subcomponent: function,
|
||||
* filter: function
|
||||
* }
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
const tableBtn = [{
|
||||
type: 'edit',
|
||||
btnName: 'btn.edit'
|
||||
}, {
|
||||
type: 'delete',
|
||||
btnName: 'btn.delete'
|
||||
}]
|
||||
const tableProps = [{
|
||||
prop: 'id',
|
||||
label: '文章id',
|
||||
width: '80px'
|
||||
}, {
|
||||
prop: 'timestamp',
|
||||
label: '日期',
|
||||
filter: timechange
|
||||
}, {
|
||||
prop: 'author',
|
||||
label: '作者',
|
||||
align: 'center'
|
||||
}, {
|
||||
prop: 'importance',
|
||||
label: '重要度'
|
||||
}, {
|
||||
prop: 'status',
|
||||
label: '状态'
|
||||
}, {
|
||||
prop: 'title',
|
||||
label: '标题'
|
||||
}, {
|
||||
prop: 'image_uri',
|
||||
label: '查看图片',
|
||||
subcomponent: CheckPic
|
||||
}]
|
||||
|
||||
export default {
|
||||
name: 'ArticleList',
|
||||
components: { Pagination, BaseTable, MethodBtn },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'info',
|
||||
deleted: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableBtn,
|
||||
tableProps,
|
||||
list: [],
|
||||
total: 0,
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 20
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
handleClick(raw) {
|
||||
console.log(raw)
|
||||
},
|
||||
getList() {
|
||||
this.listLoading = true
|
||||
fetchList(this.listQuery).then(response => {
|
||||
this.list = response.data.items
|
||||
this.total = response.data.total
|
||||
this.listLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.edit-input {
|
||||
padding-right: 100px;
|
||||
}
|
||||
.cancel-btn {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user