|
@@ -0,0 +1,392 @@
|
|
|
|
+<template>
|
|
|
|
+ <div class="container">
|
|
|
|
+ <el-card class="box-card">
|
|
|
|
+ <div slot="header" class="clearfix">
|
|
|
|
+ <span>文章管理</span>
|
|
|
|
+ <el-button style="float: right; padding: 3px 0" type="text" @click="addcontent">添加文章</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="main">
|
|
|
|
+ <el-tree
|
|
|
|
+ :data="menuList"
|
|
|
|
+ default-expand-all
|
|
|
|
+ :props="defaultProps"
|
|
|
|
+ @node-click="treeClick"
|
|
|
|
+ node-key="code"
|
|
|
|
+ ref="deeptree"
|
|
|
|
+ class="deeptree"
|
|
|
|
+ >
|
|
|
|
+ </el-tree>
|
|
|
|
+ <el-card class="maincard">
|
|
|
|
+ <div slot="header" class="clearfix">
|
|
|
|
+ <span>所属菜单:{{ is_title }}</span>
|
|
|
|
+ </div>
|
|
|
|
+ <naf-grid class="grid" ref="grid" @edit="edit" @delete="deletecontent" :data="contentList" :meta="meta" :total="total" @query="query"></naf-grid>
|
|
|
|
+ </el-card>
|
|
|
|
+ </div>
|
|
|
|
+ </el-card>
|
|
|
|
+ <el-card class="box-dj" v-if="visible">
|
|
|
|
+ <div slot="header" class="clearfix">
|
|
|
|
+ <span>{{ isNew ? '修改文章' : '添加文章' }}</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="main">
|
|
|
|
+ <naf-form v-if="visible" ref="ruleForm" @save="save" :meta="formmeta" :rules="rules" :data="is_data" :close="true" @close="close">
|
|
|
|
+ <template v-slot:field="{ form, item }">
|
|
|
|
+ <!-- 图片上传 -->
|
|
|
|
+ <el-upload
|
|
|
|
+ v-if="item.name == 'thumbnail'"
|
|
|
|
+ class="avatar-uploader avatar"
|
|
|
|
+ action="/api/files/upload"
|
|
|
|
+ :show-file-list="false"
|
|
|
|
+ :on-success="handleAvatarSuccess"
|
|
|
|
+ :before-upload="beforeAvatarUpload"
|
|
|
|
+ :data="{ type: 'resource' }"
|
|
|
|
+ :headers="myHeaders"
|
|
|
|
+ :on-error="imgerror"
|
|
|
|
+ >
|
|
|
|
+ <img v-if="imageUrl" :src="imageUrl" class="avatar">
|
|
|
|
+ <i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
|
|
|
+ </el-upload>
|
|
|
|
+ <!-- 选择日期 -->
|
|
|
|
+ <el-date-picker
|
|
|
|
+ value-format="yyyy-MM-dd"
|
|
|
|
+ v-model="form[item.name]"
|
|
|
|
+ v-if="item.name == 'date'"
|
|
|
|
+ type="date"
|
|
|
|
+ placeholder="选择日期">
|
|
|
|
+ </el-date-picker>
|
|
|
|
+ <!-- 菜单选择 -->
|
|
|
|
+ <el-select v-if="item.name == 'menus'" v-model="form[item.name]" placeholder="请选择绑定菜单">
|
|
|
|
+ <el-option
|
|
|
|
+ v-for="item in menuList"
|
|
|
|
+ :key="item._id"
|
|
|
|
+ :label="item.name"
|
|
|
|
+ :value="item.code">
|
|
|
|
+ </el-option>
|
|
|
|
+ </el-select>
|
|
|
|
+ <!-- 附件上传 -->
|
|
|
|
+ <el-upload
|
|
|
|
+ v-if="item.name == 'annex'"
|
|
|
|
+ class="upload-demo"
|
|
|
|
+ action="/api/files/upload"
|
|
|
|
+ :limit="1"
|
|
|
|
+ :on-exceed="handleExceed"
|
|
|
|
+ :on-success="handleFilesSuccess"
|
|
|
|
+ :before-upload="beforeFilesUpload"
|
|
|
|
+ :data="{ type: 'files' }"
|
|
|
|
+ :headers="myHeaders"
|
|
|
|
+ :before-remove="beforefileremove"
|
|
|
|
+ :on-error="fileserror"
|
|
|
|
+ :file-list="fileList">
|
|
|
|
+ <el-button size="small" type="primary">点击上传</el-button>
|
|
|
|
+ </el-upload>
|
|
|
|
+ <!-- 富文本 -->
|
|
|
|
+ <editor-bar v-if="item.name == 'content'" v-model="form[item.name]" :isClear="isClear"></editor-bar>
|
|
|
|
+ </template>
|
|
|
|
+ <template v-slot:end="{ form, item }">
|
|
|
|
+ <el-form-item :label="item.title" v-if="item.name == 'term' && form.menus == '4'" :prop="item.name">
|
|
|
|
+ <el-input v-model="form[item.name]"></el-input>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </template>
|
|
|
|
+ </naf-form>
|
|
|
|
+ </div>
|
|
|
|
+ </el-card>
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script>
|
|
|
|
+import nafGrid from '@naf/data/tables/naf-grid'
|
|
|
|
+import nafForm from '@naf/data/form'
|
|
|
|
+import editorBar from '@naf/data/editoritem'
|
|
|
|
+import { createNamespacedHelpers, mapMutations } from 'vuex'
|
|
|
|
+const token = sessionStorage.getItem('token')
|
|
|
|
+const { mapState, mapActions } = createNamespacedHelpers('content')
|
|
|
|
+const { mapState: cmenusmapState, mapActions: menusmapActions } = createNamespacedHelpers('wokesmenu')
|
|
|
|
+export default {
|
|
|
|
+ components: {
|
|
|
|
+ nafGrid,
|
|
|
|
+ nafForm,
|
|
|
|
+ editorBar
|
|
|
|
+ },
|
|
|
|
+ data () {
|
|
|
|
+ return {
|
|
|
|
+ is_title: null,
|
|
|
|
+ isClear: false,
|
|
|
|
+ detail: '',
|
|
|
|
+ myHeaders: { Authorization: `Bearer ${token}` },
|
|
|
|
+ imageUrl: '',
|
|
|
|
+ fileList: [],
|
|
|
|
+ is_data: {},
|
|
|
|
+ visible: false,
|
|
|
|
+ meta: [
|
|
|
|
+ { name: 'title', title: '标题', filter: true },
|
|
|
|
+ { name: 'date', title: '发表日期' }
|
|
|
|
+ ],
|
|
|
|
+ formmeta: [
|
|
|
|
+ { name: 'thumbnail', title: '缩略图', slots: 'field' },
|
|
|
|
+ { name: 'title', title: '标题' },
|
|
|
|
+ { name: 'date', title: '发表日期', slots: 'field' },
|
|
|
|
+ { name: 'menus', title: '绑定菜单', slots: 'field' },
|
|
|
|
+ { name: 'term', title: '文章期目', slots: 'end' },
|
|
|
|
+ { name: 'annex', title: '附件', slots: 'field' },
|
|
|
|
+ { name: 'content', title: '内容', slots: 'field' }
|
|
|
|
+ ],
|
|
|
|
+ rules: {
|
|
|
|
+ title: [
|
|
|
|
+ { required: true, message: '请输入标题', trigger: 'blur' }
|
|
|
|
+ ],
|
|
|
|
+ slug: [
|
|
|
|
+ { required: true, message: '请输入摘要', trigger: 'blur' }
|
|
|
|
+ ],
|
|
|
|
+ date: [
|
|
|
|
+ { required: true, message: '请选择日期', trigger: 'blur' }
|
|
|
|
+ ],
|
|
|
|
+ columns: [
|
|
|
|
+ { required: true, message: '请选择绑定菜单', trigger: 'blur' }
|
|
|
|
+ ],
|
|
|
|
+ thumbnail: [
|
|
|
|
+ { required: true, message: '请上传缩略图', trigger: 'blur' }
|
|
|
|
+ ],
|
|
|
|
+ content: [
|
|
|
|
+ { required: true, message: '请输入内容', trigger: 'blur' }
|
|
|
|
+ ],
|
|
|
|
+ term: [
|
|
|
|
+ { required: true, message: '请输入期目', trigger: 'blur' }
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ defaultProps: {
|
|
|
|
+ children: 'children',
|
|
|
|
+ label: 'name'
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ methods: {
|
|
|
|
+ ...mapActions(['contentquery', 'contentcreate', 'contentupdate', 'contentdelete', 'contentdetails']),
|
|
|
|
+ ...menusmapActions(['menuquery']),
|
|
|
|
+ ...mapMutations(['setcolumns']),
|
|
|
|
+ // 点击树
|
|
|
|
+ treeClick (data) {
|
|
|
|
+ this.is_title = data.name
|
|
|
|
+ this.data = data
|
|
|
|
+ this.query()
|
|
|
|
+ },
|
|
|
|
+ // 添加
|
|
|
|
+ async addcontent () {
|
|
|
|
+ this.is_data = {
|
|
|
|
+ menus: this.data.code
|
|
|
|
+ }
|
|
|
|
+ this.visible = true
|
|
|
|
+ },
|
|
|
|
+ // 删除
|
|
|
|
+ async deletecontent (e) {
|
|
|
|
+ this.$confirm('请确认删除', '提示', {
|
|
|
|
+ confirmButtonText: '确定',
|
|
|
|
+ cancelButtonText: '取消',
|
|
|
|
+ type: 'warning'
|
|
|
|
+ }).then(async () => {
|
|
|
|
+ const res = await this.contentdelete(e)
|
|
|
|
+ // eslint-disable-next-line eqeqeq
|
|
|
|
+ if (res.errcode == 0) {
|
|
|
|
+ this.$message.success('操作成功')
|
|
|
|
+ this.query()
|
|
|
|
+ this.$refs.grid.resetpage(-1)
|
|
|
|
+ }
|
|
|
|
+ }).catch(() => {
|
|
|
|
+ this.$message({
|
|
|
|
+ type: 'info',
|
|
|
|
+ message: '已取消删除'
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+ // 修改
|
|
|
|
+ async edit (e) {
|
|
|
|
+ // await this.columnquery({ filter: {}, paging: {} })
|
|
|
|
+ await this.contentdetails({ _id: e._id })
|
|
|
|
+ this.is_data = { ...this.contentItem }
|
|
|
|
+ if (this.is_data.annex) this.fileList.push({ name: this.is_data.annexname, url: this.is_data.annex })
|
|
|
|
+ if (this.contentItem.thumbnail) this.imageUrl = this.contentItem.thumbnail
|
|
|
|
+ this.visible = true
|
|
|
|
+ },
|
|
|
|
+ // 查询
|
|
|
|
+ async query ({ filter = {}, paging = {} } = {}) {
|
|
|
|
+ filter.code = this.data.code
|
|
|
|
+ await this.contentquery({ filter, paging })
|
|
|
|
+ },
|
|
|
|
+ // 保存按钮
|
|
|
|
+ async save (e) {
|
|
|
|
+ let res
|
|
|
|
+ if (this.isNew) {
|
|
|
|
+ // 修改
|
|
|
|
+ res = await this.contentupdate(e)
|
|
|
|
+ } else {
|
|
|
|
+ // 添加
|
|
|
|
+ res = await this.contentcreate(e)
|
|
|
|
+ }
|
|
|
|
+ // eslint-disable-next-line eqeqeq
|
|
|
|
+ if (res.errcode == 0) {
|
|
|
|
+ this.$message.success('操作成功')
|
|
|
|
+ this.query()
|
|
|
|
+ this.$refs.grid.resetpage(-1)
|
|
|
|
+ this.visible = false
|
|
|
|
+ this.fileList = []
|
|
|
|
+ this.imageUrl = ''
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ // 关闭弹窗
|
|
|
|
+ close () {
|
|
|
|
+ this.visible = false
|
|
|
|
+ this.fileList = []
|
|
|
|
+ this.imageUrl = ''
|
|
|
|
+ },
|
|
|
|
+ // 文件上传
|
|
|
|
+ // 文件列表移除文件时的钩子
|
|
|
|
+ beforefileremove (file, fileList) {
|
|
|
|
+ this.is_data.annex = null
|
|
|
|
+ this.$refs.ruleForm.form.annex = null
|
|
|
|
+ this.is_data.annexname = null
|
|
|
|
+ this.$refs.ruleForm.form.annexname = null
|
|
|
|
+ },
|
|
|
|
+ // 文件上传成功时的钩子
|
|
|
|
+ handleFilesSuccess (res, file) {
|
|
|
|
+ this.is_data = this.$refs.ruleForm.form
|
|
|
|
+ this.is_data.annex = res.data.path
|
|
|
|
+ this.is_data.annexname = res.data.name
|
|
|
|
+ },
|
|
|
|
+ // 文件列表移除文件时的钩子
|
|
|
|
+ beforeFilesUpload (file) {
|
|
|
|
+ const isType = file.type === 'image/jpeg' || 'image/png' || 'application/octet-stream' || 'application/zip' || 'application/octet-stream' || 'application/msword' || 'application/vnd.ms-excel' || 'application/x-zip-compressed' || 'application/pdf'
|
|
|
|
+ const isLt2M = file.size / 1024 / 1024 < 5
|
|
|
|
+ if (!isType) {
|
|
|
|
+ this.$message.error('请上传正确格式')
|
|
|
|
+ }
|
|
|
|
+ if (!isLt2M) {
|
|
|
|
+ this.$message.error('上传图片大小不能超过 5MB!')
|
|
|
|
+ }
|
|
|
|
+ return isType && isLt2M
|
|
|
|
+ },
|
|
|
|
+ // 文件超出个数限制时的钩子
|
|
|
|
+ handleExceed (files, fileList) {
|
|
|
|
+ this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)
|
|
|
|
+ },
|
|
|
|
+ fileserror () {
|
|
|
|
+ this.$message.error('上传失败')
|
|
|
|
+ },
|
|
|
|
+ // 图片上传
|
|
|
|
+ // 文件上传成功时的钩子
|
|
|
|
+ handleAvatarSuccess (res, file) {
|
|
|
|
+ this.is_data = { ...this.$refs.ruleForm.form }
|
|
|
|
+ this.is_data.thumbnail = res.data.path
|
|
|
|
+ this.$refs.ruleForm.formChage('thumbnail', res.data.path)
|
|
|
|
+ this.imageUrl = URL.createObjectURL(file.raw)
|
|
|
|
+ },
|
|
|
|
+ imgerror () {
|
|
|
|
+ this.$message.error('上传失败')
|
|
|
|
+ },
|
|
|
|
+ // 上传文件之前的钩子
|
|
|
|
+ beforeAvatarUpload (file) {
|
|
|
|
+ const isJPG = file.type === 'image/jpeg'
|
|
|
|
+ const isPNG = file.type === 'image/png'
|
|
|
|
+ const isLt2M = file.size / 1024 / 1024 < 2
|
|
|
|
+
|
|
|
|
+ if (!isJPG && !isPNG) {
|
|
|
|
+ this.$message.error('上传图片只能是 JPG 或 PNG 格式!')
|
|
|
|
+ }
|
|
|
|
+ if (!isLt2M) {
|
|
|
|
+ this.$message.error('上传图片大小不能超过 2MB!')
|
|
|
|
+ }
|
|
|
|
+ return (isJPG || isPNG) && isLt2M
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ async mounted () {
|
|
|
|
+ const res = await this.menuquery({ filter: { type: 0 } })
|
|
|
|
+ // eslint-disable-next-line eqeqeq
|
|
|
|
+ if (res.errcode == 0) {
|
|
|
|
+ this.is_title = res.data[0].name
|
|
|
|
+ this.$refs.deeptree.setCurrentKey(res.data[0].code)
|
|
|
|
+ this.data = res.data[0]
|
|
|
|
+ this.query()
|
|
|
|
+ this.setcolumns(this.menuList)
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ computed: {
|
|
|
|
+ ...mapState(['total', 'contentList', 'contentItem']),
|
|
|
|
+ ...cmenusmapState(['menuList']),
|
|
|
|
+ isNew () {
|
|
|
|
+ return Boolean(this.is_data && this.is_data._id)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style lang="less" scoped>
|
|
|
|
+.container {
|
|
|
|
+ height: 100%;
|
|
|
|
+ position: relative;
|
|
|
|
+ .box-card {
|
|
|
|
+ height: 100%;
|
|
|
|
+ /deep/ .el-card__body {
|
|
|
|
+ height: 100%;
|
|
|
|
+ padding: 0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+.main {
|
|
|
|
+ display: flex;
|
|
|
|
+ height: 100%;
|
|
|
|
+ .deeptree {
|
|
|
|
+ width: 15%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ padding-top: 1%;
|
|
|
|
+ }
|
|
|
|
+ .maincard {
|
|
|
|
+ width: 85%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ padding: 0;
|
|
|
|
+ margin: 0;
|
|
|
|
+ /deep/ .el-card__body {
|
|
|
|
+ height: 100%;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+.box-dj {
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ position: absolute;
|
|
|
|
+ left: 0;
|
|
|
|
+ top: 0;
|
|
|
|
+ z-index: 999;
|
|
|
|
+ /deep/ .el-card__body {
|
|
|
|
+ height: 90%;
|
|
|
|
+ overflow-y: auto;
|
|
|
|
+ width: 100%;
|
|
|
|
+ .main {
|
|
|
|
+ width: 60%;
|
|
|
|
+ margin: 0 auto;
|
|
|
|
+ margin-bottom: 4em;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+.avatar-uploader {
|
|
|
|
+ border: 1px dashed #d9d9d9;
|
|
|
|
+ border-radius: 6px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ position: relative;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+}
|
|
|
|
+.avatar-uploader .el-upload:hover {
|
|
|
|
+ border-color: #409EFF;
|
|
|
|
+}
|
|
|
|
+.avatar-uploader-icon {
|
|
|
|
+ font-size: 28px;
|
|
|
|
+ color: #8c939d;
|
|
|
|
+ width: 150px;
|
|
|
|
+ height: 150px;
|
|
|
|
+ line-height: 150px;
|
|
|
|
+ text-align: center;
|
|
|
|
+}
|
|
|
|
+.avatar {
|
|
|
|
+ width: 150px;
|
|
|
|
+ height: 150px;
|
|
|
|
+ display: block;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+</style>
|