博客文章

This commit is contained in:
2021-10-04 16:24:50 +08:00
parent 22932ccaf9
commit 4a64f8f492
11 changed files with 335 additions and 24 deletions
+4 -4
View File
@@ -1,16 +1,16 @@
<template>
<div>
<el-form inline :model="search" @submit.prevent>
<el-form-item label="内容">
<el-form-item label="内容">
<el-input size="small" v-model="search.content" />
</el-form-item>
<el-form-item label="类型">
<el-form-item label="类型">
<el-select size="small" v-model="search.type" multiple collapse-tags>
<el-option v-for="item in typeList" :key="item.value" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
<el-form-item label="创建时间">
<el-date-picker size="small"
v-model="search.createdAt"
type="daterange"
range-separator=""
+6 -6
View File
@@ -1,26 +1,26 @@
<template>
<div>
<el-form inline :model="search" @submit.prevent>
<el-form-item label="名称">
<el-form-item label="名称">
<el-input size="small" v-model="search.name" />
</el-form-item>
<el-form-item label="所属歌单">
<el-form-item label="所属歌单">
<el-select size="small" v-model="search.lib_id" multiple :max-tag-count="3">
<el-option v-for="musicLib in musicLibs" :key="musicLib._id" :value="musicLib._id" :label="musicLib.name" />
</el-select>
</el-form-item>
<el-form-item label="文件类型">
<el-form-item label="文件类型">
<el-select size="small" v-model="search.ext" multiple :max-tag-count="3">
<el-option v-for="ext in exts" :key="ext" :value="ext" :label="ext" />
</el-select>
</el-form-item>
<el-form-item label="标题">
<el-form-item label="标题">
<el-input size="small" v-model="search.title" />
</el-form-item>
<el-form-item label="唱片集">
<el-form-item label="唱片集">
<el-input size="small" v-model="search.album" />
</el-form-item>
<el-form-item label="艺术家">
<el-form-item label="艺术家">
<el-input size="small" v-model="search.artist" />
</el-form-item>
</el-form>
+291
View File
@@ -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(/&quot;/g, '"').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/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>
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div>
<el-form inline :model="search" @submit.prevent>
<el-form-item label="配置项">
<el-form-item label="配置项">
<el-input size="small" placeholder="名称/描述" v-model="search.name" />
</el-form-item>
</el-form>
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div>
<el-form inline :model="search" @submit.prevent>
<el-form-item label="角色名称/描述">
<el-form-item label="角色名称/描述">
<el-input size="small" v-model="search.name" />
</el-form-item>
</el-form>
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div>
<el-form inline :model="search" @submit.prevent>
<el-form-item label="用户名/昵称">
<el-form-item label="用户名/昵称">
<el-input size="small" v-model="search.username" />
</el-form-item>
</el-form>