@@ -0,0 +1,291 @@
< template >
< div >
< div class = "search-panel" >
< el-form inline :model = "search" @submit.prevent >
< el -form -item label = "标题" >
< el-input size = "small" v-model = "search.title" / >
< / el-form-item >
< el-form-item label = "创建时间" >
< el-date-picker size = "small"
v-model = "search.createDate"
type = "daterange"
range -separator = " 至 "
start -placeholder = " 开始日期 "
end -placeholder = " 结束日期 " / >
< / el-form-item >
< el-form-item label = "分类" >
< el-select size = "small" v-model = "search.category" filterable clearable >
< el-option v-for = "item in categories" :key="item" :value="item" :label="item" / >
< / el-select >
< / el-form-item >
< el-form-item label = "标签" >
< el-select size = "small" v-model = "search.tag" filterable clearable >
< el-option v-for = "item in tags" :key="item" :value="item" :label="item" / >
< / el-select >
< / el-form-item >
< el-form-item label = "已分词" >
< el-select size = "small" v-model = "search.isSplited" >
< el-option :value = "true" label = "是" / >
< el-option :value = "false" label = "否" / >
< / el-select >
< / el-form-item >
< / el-form >
< / div >
< div class = "btn-container" >
< el-button type = "primary" @click ="splitWord" size = "small" > 分词处理 < / el-button >
< el-button @click ="pullArticles" size = "small" > 拉取文章 < / el-button >
<!-- < Upload : action = "($http.defaults.baseURL || '') + '/api/system/deployBlog'"
name = "blogZip" :show-upload-list = "false"
:format = "allowUploadExt" :headers = "uploadHeaders" :max-size = "102400"
:before-upload = "beforeUpload" :on-success = "uploadSuccess" @ on -error = " uploadError "
:on-format-error = "uploadFormatError" :on-exceeded-size = "uploadFileSizeError"
style = "display: inline-block;" >
< Button type = "primary" icon = "ios-cloud-upload-outline" > 发布博客 < / Button >
< / Upload > -- >
< div class = "search-btn" >
< el-button type = "primary" @click ="loadDataBase(true)" size = "small" icon = "el-icon-search" > 搜索 < / el-button >
< el-button @click ="reset" size = "small" icon = "el-icon-refresh-right" > 重置 < / el-button >
< / div >
< / div >
< el-row >
< el-col :span = "4" style = "height: 520px;overflow: auto;" >
< el-tree :props = "treeProps" :load = "loadTreeData" lazy highlight -current @ node -click = " articlePreview " / >
< / el-col >
< el-col :span = "20" >
< el-table :data = "articleData" v-loading = "loading" stripe height="520" @selection-change="dataSelect" >
< el -table -column type = "selection" width = "55" / >
< el-table-column prop = "title" label = "标题" / >
< el-table-column prop = "path" label = "路径" >
< template # default = "scope" >
{ { scope . row . path . join ( '/' ) } }
< / template >
< / el-table-column >
< el-table-column prop = "categories" label = "分类" width = "150" >
< template # default = "scope" >
{ { typeof scope . row . categories === 'string' ? scope . row . categories : scope . row . categories . join ( ', ' ) } }
< / template >
< / el-table-column >
< el-table-column prop = "tags" label = "标签" width = "180" >
< template # default = "scope" >
{ { typeof scope . row . tags === 'string' ? scope . row . tags : scope . row . tags . join ( ', ' ) } }
< / template >
< / el-table-column >
< el-table-column prop = "content_len" label = "正文长度" width = "100" / >
< el-table-column prop = "create_date" label = "创建时间" width = "180" >
< template # default = "scope" >
{ { datetimeFormat ( scope . row . create _date ) } }
< / template >
< / el-table-column >
< el-table-column prop = "tags" label = "是否已分词" width = "120" >
< template # default = "scope" >
< i : class = "scope.row.is_splited ? 'el-icon-check' : 'el-icon-close'" > < / i >
< / template >
< / el-table-column >
< / el-table >
< / el-col >
< / el-row >
<!-- < Row :gutter = "16" >
< Col span = "4" style = "height:520px;overflow:auto;" >
< Tree :data = "articleTree" :load-data = "loadTreeData" @ on -select -change = " articlePreview " > < / Tree >
< / Col >
< Col span = "20" >
< div class = "table-container" >
< Table border :loading = "loading" :columns = "articleColumns" :data = "articleData" height = "520" @ on -selection -change = " dataSelect " > < / Table >
< / div >
< / Col >
< / Row > -- >
< div class = "page-container" >
< el-pagination background
:page-sizes = "$store.state.pageSizeOpts"
:layout = "$store.state.pageLayout"
:total = "total"
@ size -change = " pageSizeChange "
@ current -change = " pageChange " >
< / el-pagination >
< / div >
< el-drawer
v-model = "markdownPreview.show"
:title = "markdownPreview.title"
direction = "rtl"
custom -class = " custom -drawer " >
< div v-html = "markdownPreview.content" > < / div >
< / el -drawer >
< / div >
< / template >
< script lang = "ts" >
// import moment from 'moment'
import hyperdown from 'hyperdown'
import prismjs from 'prismjs'
import 'prismjs/themes/prism.css'
import { ArticleModel , TreeNodeData , TreeNodeSource } from '../../model/system/article'
import { ElButton , ElForm , ElFormItem , ElInput , ElTable , ElTableColumn , ElPagination , ElSelect , ElOption , ElDatePicker , ElRow , ElCol , ElTree , ElDrawer , ElMessage , ElMessageBox } from 'element-plus'
import Node from 'element-plus/lib/components/tree/src/model/node'
import { Options } from 'vue-class-component'
import BaseList from '../../model/baselist'
import { Page } from '../../model/common.dto'
import { AxiosResponse } from 'axios'
let selectedData : string [ ] = [ ]
let closeUploadTip : Function | void | null
@ Options ( {
name : 'Article' ,
components : { ElButton , ElForm , ElFormItem , ElInput , ElTable , ElTableColumn , ElPagination , ElSelect , ElOption , ElDatePicker , ElRow , ElCol , ElTree , ElDrawer }
} )
export default class Article extends BaseList < ArticlePage > {
search = new ArticlePage ( )
allowUploadExt = [ 'zip' ]
uploadHeaders = { token : localStorage . getItem ( 'login_token' ) }
articleData : ArticleModel [ ] = [ ]
tags : string [ ] = [ ] // 所有标签
categories : string [ ] = [ ] // 所有分类
markdownPreview : { // markdown内容预览
show ? : boolean ,
title ? : string | null ,
content ? : string | null
} = {
show : false ,
title : null ,
content : null
}
async loadData ( ) {
this . loading = true
const { data } = await this . $http . get < ArticlePage , AxiosResponse < any > > ( '/api/article/list' , { params : this . search } )
selectedData = [ ]
this . loading = false
this . total = data . total
this . articleData = data . data
}
splitWord ( ) {
if ( ! selectedData . length ) {
ElMessage . warning ( '请选择要执行分词的文章' )
return
}
ElMessageBox . confirm ( ` 是否确认对选中的 ${ selectedData . length } 篇文章执行分词处理? ` , '操作确认' , { type : 'info' } ) . then ( async ( ) => {
const { data } = await this . $http . put < { _ids : string [ ] } , AxiosResponse < any > > ( '/api/article/splitWord' , { _ids : selectedData } )
if ( data . status ) {
ElMessage . success ( data . message )
} else {
ElMessage . warning ( data . message )
}
} ) . catch ( ( ) => { } )
}
pullArticles ( ) {
ElMessageBox . confirm ( '确认拉取全部文章?' , '操作确认' , { type : 'info' } ) . then ( async ( ) => {
const { data } = await this . $http . get < never , AxiosResponse < any > > ( '/api/article/pull' )
if ( data . status ) {
ElMessage . success ( data . message )
this . loadData ( )
} else {
ElMessage . warning ( data . message )
}
} ) . catch ( ( ) => { } )
}
dataSelect ( selection : ArticleModel [ ] ) {
selectedData = selection . map ( item => item . _id )
}
// beforeUpload(file: File) {
// let filenameCut = undefined
// if(file.name.length > 15) {
// filenameCut = file.name.substr(0, 15) + '...'
// }
// closeUploadTip = ElMessage.loading({
// content: (filenameCut || file.name) + ' 正在上传,请稍候...',
// duration: 0
// })
// return true
// }
// uploadFormatError() {
// this.closeUploadTip()
// ElMessage.error(`只能上传 ${this.allowUploadExt.join('、')} 格式的文件`)
// }
// uploadFileSizeError() {
// this.closeUploadTip()
// ElMessage.error(`只能上传不超过100MB的文件`)
// }
// uploadSuccess(response: MsgResult) {
// this.closeUploadTip()
// if(response.status) {
// ElMessage.success(response.message)
// } else {
// ElMessage.warning(response.message)
// }
// }
// uploadError() {
// this.closeUploadTip()
// ElMessage.error('上传失败')
// }
// closeUploadTip() {
// if(typeof closeUploadTip === 'function') {
// closeUploadTip.call(this)
// closeUploadTip = null
// }
// }
readonly treeProps = {
label : 'name' ,
children : 'children' ,
isLeaf : 'isLeaf' ,
}
async loadTreeData ( node : Node , resolve : Function ) {
const childItems : TreeNodeSource [ ] = ( await this . $http . get ( '/api/article/tree' , { params : { deep : node . level , parent : node . data . name } } ) ) . data
resolve ( childItems . map ( ( childItem ) : TreeNodeData => {
const treeNode : TreeNodeData = {
name : childItem . _id ,
title : childItem . article _id ? childItem . _id : ` ${ childItem . _id } ( ${ childItem . cnt } ) ` ,
id : childItem . article _id || childItem . _id ,
isLeaf : ! ! childItem . article _id
}
return treeNode
} ) )
}
/**
* 树节点选中事件
* @param selectNodes 当前已选中的节点(适用于带复选框的)
* @param curNode 本次选中的节点
*/
async articlePreview ( node : TreeNodeData ) {
if ( ! node . isLeaf ) return
// 加载文章markdown预览
const mdText = ( await this . $http . get ( '/api/article/markdown' , { params : { id : node . id } } ) ) . data
this . markdownPreview . show = true
const markdownHtml = new hyperdown ( ) . makeHtml ( mdText )
this . markdownPreview . content = markdownHtml . replace ( /(?<=<pre><code[^>]*?>)[\s\S]*?(?=<\/code><\/pre>)/gi , v => {
v = v . replace ( /_&/g , ' ' ) . replace ( /"/g , '"' ) . replace ( /</g , '<' ) . replace ( />/g , '>' ) . replace ( /&/g , '&' )
return prismjs . highlight ( v , prismjs . languages . javascript , 'javascript' )
} )
this . markdownPreview . title = node . name
console . log ( this . markdownPreview )
}
created ( ) {
this . loadData ( )
this . $http . get ( '/api/article/listCategories' ) . then ( ( { data } ) => {
this . categories = data
} )
this . $http . get ( '/api/article/listTags' ) . then ( ( { data } ) => {
this . tags = data
} )
// this.loadTreeData({deep:-1, name: null}, (treeNodes: TreeNode[]) => {
// this.articleTree.push(...treeNodes)
// })
}
}
class ArticlePage extends Page {
title ? : string
createDate ? : [ Date , Date ]
category ? : string
tag ? : string
isSplited ? : boolean
reset ( ) {
super . reset ( )
this . title = undefined
this . createDate = undefined
this . category = undefined
this . tag = undefined
this . isSplited = undefined
}
}
< / script >