parent
eaa9caa47c
commit
41e5d73c9b
|
|
@ -222,7 +222,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'space-before-blocks': [2, 'always'],
|
'space-before-blocks': [2, 'always'],
|
||||||
'space-before-function-paren': [2, 'never'],
|
'space-before-function-paren': [2, 'always'],
|
||||||
'space-in-parens': [2, 'never'],
|
'space-in-parens': [2, 'never'],
|
||||||
'space-infix-ops': 2,
|
'space-infix-ops': 2,
|
||||||
'space-unary-ops': [
|
'space-unary-ops': [
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,18 @@ export const deleteArticle = (data) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary 批量删除文章
|
||||||
|
// @Param data body request.IdsReq true "批量删除文章"
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||||
|
// @Router /product/deleteProduct [delete]
|
||||||
|
export const deleteArticleByIds = (params) => {
|
||||||
|
return service({
|
||||||
|
url: '/cms/article/deleteByIds',
|
||||||
|
method: 'delete',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary 修改文章
|
// @Summary 修改文章
|
||||||
// @Produce application/json
|
// @Produce application/json
|
||||||
// @Param menu Object
|
// @Param menu Object
|
||||||
|
|
@ -47,7 +59,7 @@ export const updateArticle = (data) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags 文章文章
|
// @Tags 文章
|
||||||
// @Summary 根据id获取文章
|
// @Summary 根据id获取文章
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @accept application/json
|
// @accept application/json
|
||||||
|
|
@ -63,14 +75,50 @@ export const getArticleById = (params) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary 发布文章
|
// @Summary 文章提审
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param menu Object
|
||||||
|
// @Router /cms/article/submit [put]
|
||||||
|
export const submitArticle = (data) => {
|
||||||
|
return service({
|
||||||
|
url: '/cms/article/submit',
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary 文章审核
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param menu Object
|
||||||
|
// @Router /cms/article/review [put]
|
||||||
|
export const reviewArticle = (data) => {
|
||||||
|
return service({
|
||||||
|
url: '/cms/article/review',
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary 文章发布
|
||||||
// @Produce application/json
|
// @Produce application/json
|
||||||
// @Param menu Object
|
// @Param menu Object
|
||||||
// @Router /cms/article/release [put]
|
// @Router /cms/article/release [put]
|
||||||
export const releaseArticle = (data) => {
|
export const releaseArticle = (data) => {
|
||||||
return service({
|
return service({
|
||||||
url: '/cms/article/release',
|
url: '/cms/article/release',
|
||||||
method: 'post',
|
method: 'put',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary 文章撤销
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param menu Object
|
||||||
|
// @Router /cms/article/cancel [put]
|
||||||
|
export const cancelArticle = (data) => {
|
||||||
|
return service({
|
||||||
|
url: '/cms/article/cancel',
|
||||||
|
method: 'put',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import service from '@/utils/request'
|
||||||
|
|
||||||
|
// @Summary 获取列表数据
|
||||||
|
// @Produce application/json
|
||||||
|
// @Router /cms/tag/getList [post]
|
||||||
|
export const getTagList = (data) => {
|
||||||
|
return service({
|
||||||
|
url: '/cms/tag/getList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -72,8 +72,6 @@ const emit = defineEmits(['on-select', 'on-before-upload', 'on-upload-success',
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
category: { type: String, required: true },
|
category: { type: String, required: true },
|
||||||
size: { type: String, default: '800px' },
|
size: { type: String, default: '800px' },
|
||||||
target: { type: Object, default: null },
|
|
||||||
targetKey: { type: String, default: '' },
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const drawer = ref(false)
|
const drawer = ref(false)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ const emit = defineEmits([
|
||||||
'on-success',
|
'on-success',
|
||||||
])
|
])
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
category: {
|
||||||
|
type: String, required: true
|
||||||
|
},
|
||||||
imageUrl: {
|
imageUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
|
|
||||||
|
|
@ -1,417 +0,0 @@
|
||||||
<template>
|
|
||||||
|
|
||||||
<div v-loading="fullscreenLoading">
|
|
||||||
<div class="gva-form-box">
|
|
||||||
<div v-if="showErrMessage != ''">
|
|
||||||
{{ showErrMessage }}
|
|
||||||
</div>
|
|
||||||
<el-form v-else ref="elEditFormRef" label-position="top" :model="editForm" :rules="formRules">
|
|
||||||
<el-row :gutter="10">
|
|
||||||
<el-col :span="17">
|
|
||||||
<el-row :gutter="10">
|
|
||||||
<el-col :span="16">
|
|
||||||
<el-form-item label="标题" prop="title">
|
|
||||||
<el-input v-model="editForm.title" autocomplete="off" :show-word-limit="true" maxlength="50" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="副标题" prop="subtitle">
|
|
||||||
<el-input v-model="editForm.subtitle" autocomplete="off" :show-word-limit="true" maxlength="50" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="文章类型" prop="articleType">
|
|
||||||
<el-select v-model="editForm.articleType" placeholder="请选择">
|
|
||||||
<el-option v-for="item in articleTypeOptions" :key="item.key" :label="item.label"
|
|
||||||
:value="item.key" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="作者" prop="author">
|
|
||||||
<el-input v-model="editForm.author" autocomplete="off" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="10">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="所属栏目" prop="channels">
|
|
||||||
<el-cascader v-model="channelIdList" :options="channelOptions" style="width:100%"
|
|
||||||
:props="{ label: 'title', value: 'ID', emitPath: false, expandTrigger: 'hover', multiple: true }"
|
|
||||||
:collapse-tags="true" :collapse-tags-tooltip="true" :max-collapse-tags="3" filterable />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="文章分类" prop="categories">
|
|
||||||
<el-cascader v-model="categoryIdList" :options="categoryOptions" style="width:100%"
|
|
||||||
:props="{ label: 'title', value: 'ID', emitPath: false, expandTrigger: 'hover', multiple: true }"
|
|
||||||
:collapse-tags="true" :collapse-tags-tooltip="true" :max-collapse-tags="2" filterable />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-form-item label="文章内容" prop="content">
|
|
||||||
<RichEdit v-model="editForm.content" style="height: 50rem;" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1" align="center">
|
|
||||||
<el-divider direction="vertical" style="height: 100%" />
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="6">
|
|
||||||
<el-form-item label="摘要" prop="desc">
|
|
||||||
<el-input v-model="editForm.desc" type="textarea" rows="7" :show-word-limit="true" maxlength="100"
|
|
||||||
autocomplete="off" resize="none" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="来源" prop="source">
|
|
||||||
<el-select v-model="editForm.source" placeholder="请选择" allow-create clearable filterable>
|
|
||||||
<el-option v-for="item in sourceOptions" :key="item" :label="item" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="跳转链接" prop="source">
|
|
||||||
<el-input v-model="editForm.url" autocomplete="off" placeholder="https://" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="发布时间">
|
|
||||||
<el-date-picker v-model="editForm.publishDate" type="datetime" placeholder="请选择" style="width:100%" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="文章标签" prop="tags">
|
|
||||||
<div class="flex flex-wrap gap-2">
|
|
||||||
<el-tag v-for="(item, index) in tagList" :key="index" type="info" closable size="large"
|
|
||||||
@close="tagList.splice(index, 1)">
|
|
||||||
{{ item }}
|
|
||||||
</el-tag>
|
|
||||||
<el-input v-if="tagInputFlag" ref="InputRef" v-model="inputTag" class="w-20" size="default"
|
|
||||||
style="width: 80px;" @keyup.enter="handleTagOK" @keyup.esc="handleTagCancel" @blur="handleTagOK" />
|
|
||||||
<el-button v-else type="primary" plain icon="plus" class="button-new-tag" size="default"
|
|
||||||
style="width: 80px;" @click="handleTagAdd">
|
|
||||||
添加
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="图片(首张图可用来做栏目列表用)" prop="imgs">
|
|
||||||
<ChooseImg ref="chooseImg" category="article_imgs" size="800px" @on-select="handleSelectImg" />
|
|
||||||
<template #label>
|
|
||||||
<div style="margin-bottom: 10px;">图片(首张图可用来做栏目列表用)</div>
|
|
||||||
<div class="flex gap-3">
|
|
||||||
<el-button type="success" plain icon="folder" @click="handleChooseImg">媒体库</el-button>
|
|
||||||
<upload-common class="upload-btn-media-library" category="article_imgs"
|
|
||||||
@on-success="handleImgUpload" />
|
|
||||||
<el-tooltip effect="dark" content="图片超过 512K 或者 长宽 > 1080 会自动压缩后再上传" placement="top-start">
|
|
||||||
<upload-image :file-size="512" :max-w-h="1080" category="article_imgs"
|
|
||||||
@on-success="handleImgUpload" />
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="media">
|
|
||||||
<div v-for="(item, key) in imgFileList" :key="key" class="media-box">
|
|
||||||
<div class="img-box-list">
|
|
||||||
<el-image :key="key" :src="getUrl(item.url)" fit="cover" :preview-src-list="[getUrl(item.url)]"
|
|
||||||
hide-on-click-modal style="width: 138px; height: 138px;">
|
|
||||||
<template #error>
|
|
||||||
<div class="img-box-list">
|
|
||||||
<el-icon>
|
|
||||||
<picture />
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-image>
|
|
||||||
</div>
|
|
||||||
<div class="img-title">
|
|
||||||
<el-button size="small" type="default" icon="delete" @click="handleImgRemove(item)">删除</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" icon="check" style="width: 150px;" @click="handleFormSubmit">保存</el-button>
|
|
||||||
<el-button type="default" icon="close" @click="handleFormClose">关闭</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { getUrl } from '@/utils/image'
|
|
||||||
import { reactive, ref } from 'vue'
|
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import RichEdit from '@/components/richtext/rich-edit.vue'
|
|
||||||
import ChooseImg from '@/components/chooseImg/index.vue'
|
|
||||||
import { getSourceList } from '@/api/source'
|
|
||||||
import { getCategoryTree } from '@/api/category'
|
|
||||||
import { getChannelTree } from '@/api/channel'
|
|
||||||
import UploadCommon from '@/components/upload/common.vue'
|
|
||||||
import UploadImage from '@/components/upload/image.vue'
|
|
||||||
import {
|
|
||||||
addArticle,
|
|
||||||
updateArticle,
|
|
||||||
getArticleById,
|
|
||||||
} from '@/api/article'
|
|
||||||
import { getFetcherArticleById } from '@/api/fetcher'
|
|
||||||
|
|
||||||
const showErrMessage = ref('')
|
|
||||||
|
|
||||||
const fullscreenLoading = ref(true)
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const isEdit = ref(false) // 确定是修改还是增加 isEdit = true 为修改,否则为增加
|
|
||||||
const elEditFormRef = ref()
|
|
||||||
const formRules = reactive({
|
|
||||||
articleType: [
|
|
||||||
{ required: true, message: '请输选择文章类型', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
author: [
|
|
||||||
{ required: true, message: '请输入作者', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
title: [
|
|
||||||
{ required: true, message: '请输入文章名称', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
subtitle: [
|
|
||||||
{ required: true, message: '请输入文章副标题', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
content: [
|
|
||||||
{ required: true, message: '请输入文章内容', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
})
|
|
||||||
const articleTypeOptions = ref([
|
|
||||||
{ key: 1, label: '图文' },
|
|
||||||
{ key: 2, label: '视频' },
|
|
||||||
])
|
|
||||||
const editForm = ref({
|
|
||||||
ID: 0,
|
|
||||||
articleType: '',
|
|
||||||
title: '',
|
|
||||||
subtitle: '',
|
|
||||||
author: '',
|
|
||||||
channels: [],
|
|
||||||
categories: [],
|
|
||||||
desc: '',
|
|
||||||
source: '',
|
|
||||||
url: '',
|
|
||||||
publishDate: '',
|
|
||||||
articleTime: '',
|
|
||||||
tags: '',
|
|
||||||
imgs: '',
|
|
||||||
})
|
|
||||||
// options 相关
|
|
||||||
const initOptions = (data, optionsData) => {
|
|
||||||
data &&
|
|
||||||
data.forEach(item => {
|
|
||||||
if (item.children && item.children.length) {
|
|
||||||
const option = {
|
|
||||||
title: item.title,
|
|
||||||
ID: String(item.ID),
|
|
||||||
children: []
|
|
||||||
}
|
|
||||||
initOptions(
|
|
||||||
item.children,
|
|
||||||
option.children,
|
|
||||||
)
|
|
||||||
optionsData.push(option)
|
|
||||||
} else {
|
|
||||||
const option = {
|
|
||||||
title: item.title,
|
|
||||||
ID: String(item.ID),
|
|
||||||
}
|
|
||||||
optionsData.push(option)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 以下为 options 使用
|
|
||||||
const channelOptions = ref([])
|
|
||||||
const categoryOptions = ref([])
|
|
||||||
const sourceOptions = ref([])
|
|
||||||
|
|
||||||
const getCategoryData = async () => {
|
|
||||||
categoryOptions.value = []
|
|
||||||
const res = await getCategoryTree()
|
|
||||||
if (res.code === 0) {
|
|
||||||
initOptions(res.data.categoryTree, categoryOptions.value, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const getChannelData = async () => {
|
|
||||||
channelOptions.value = []
|
|
||||||
const res = await getChannelTree()
|
|
||||||
if (res.code === 0) {
|
|
||||||
initOptions(res.data.channelTree, channelOptions.value, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const getSourceData = async () => {
|
|
||||||
sourceOptions.value = []
|
|
||||||
const res = await getSourceList({ page: 1, pageSize: 999 })
|
|
||||||
if (res.code === 0) {
|
|
||||||
sourceOptions.value = res.data.list && res.data.list.map(item => item.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化方法
|
|
||||||
const initPage = async () => {
|
|
||||||
getCategoryData()
|
|
||||||
getChannelData()
|
|
||||||
getSourceData()
|
|
||||||
|
|
||||||
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
|
|
||||||
if (route.query && route.query.id && route.query.id > 0) {
|
|
||||||
const res = await getArticleById({ ID: route.query.id })
|
|
||||||
if (res.code === 0) {
|
|
||||||
// 初始化表单数据
|
|
||||||
editForm.value = res.data.article
|
|
||||||
isEdit.value = true
|
|
||||||
// 初始化关联数据
|
|
||||||
const { categories, channels, imgs, tags } = res.data.article
|
|
||||||
categoryIdList.value = categories && categories.map(item => String(item.ID))
|
|
||||||
channelIdList.value = channels && channels.map(item => String(item.ID))
|
|
||||||
if (imgs && (imgs !== '')) {
|
|
||||||
imgFileList.value = JSON.parse(imgs)
|
|
||||||
}
|
|
||||||
tagList.value = tags && tags.split(',')
|
|
||||||
fullscreenLoading.value = false
|
|
||||||
} else {
|
|
||||||
ElMessage({
|
|
||||||
type: 'error',
|
|
||||||
message: '获取数据失败'
|
|
||||||
})
|
|
||||||
showErrMessage.value = '获取数据失败'
|
|
||||||
}
|
|
||||||
} else if (route.query && route.query.fetcherId && route.query.fetcherId > 0) {
|
|
||||||
// 从爬虫数据中获取内容并填充至文章内容中
|
|
||||||
const res = await getFetcherArticleById({ ID: route.query.fetcherId })
|
|
||||||
if (res.code === 0 && res.data && res.data.article) {
|
|
||||||
console.log(res.data)
|
|
||||||
const { title, author, source, content, publicTime } = res.data.article
|
|
||||||
editForm.value.title = title
|
|
||||||
editForm.value.subtitle = title
|
|
||||||
editForm.value.author = author
|
|
||||||
editForm.value.source = source
|
|
||||||
editForm.value.content = content
|
|
||||||
editForm.value.publishDate = publicTime
|
|
||||||
editForm.value.articleTime = publicTime
|
|
||||||
} else {
|
|
||||||
ElMessage({
|
|
||||||
type: 'error',
|
|
||||||
message: '获取数据失败'
|
|
||||||
})
|
|
||||||
showErrMessage.value = '获取数据失败'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fullscreenLoading.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
initPage()
|
|
||||||
|
|
||||||
// ---- 以下为图片相关操作 ----
|
|
||||||
const chooseImg = ref(null)
|
|
||||||
const imgFileList = ref([])
|
|
||||||
const handleChooseImg = (event) => {
|
|
||||||
chooseImg.value.open()
|
|
||||||
event.preventDefault()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
const handleSelectImg = (file) => {
|
|
||||||
imgFileList.value.push({
|
|
||||||
key: file.key,
|
|
||||||
name: file.name,
|
|
||||||
url: file.url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const handleImgRemove = (file) => {
|
|
||||||
imgFileList.value = imgFileList.value.filter(item => item.key !== file.key)
|
|
||||||
}
|
|
||||||
const handleImgUpload = (file) => {
|
|
||||||
imgFileList.value.push({
|
|
||||||
key: file.key,
|
|
||||||
name: file.name,
|
|
||||||
url: file.url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 以下为tag相关操作 ----
|
|
||||||
const tagInputFlag = ref(false)
|
|
||||||
const inputTag = ref('')
|
|
||||||
const tagList = ref([])
|
|
||||||
const handleTagOK = (key) => {
|
|
||||||
const val = inputTag.value.trim()
|
|
||||||
if (val !== '' && !tagList.value.includes(val)) {
|
|
||||||
tagList.value.push(val)
|
|
||||||
tagInputFlag.value = false
|
|
||||||
inputTag.value = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const handleTagCancel = (key) => {
|
|
||||||
if (key.keyCode === 27) {
|
|
||||||
// 按下esc
|
|
||||||
tagInputFlag.value = false
|
|
||||||
inputTag.value = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const handleTagAdd = () => {
|
|
||||||
tagInputFlag.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const categoryIdList = ref([])
|
|
||||||
const channelIdList = ref([])
|
|
||||||
|
|
||||||
// 添加/保存修改
|
|
||||||
const handleFormSubmit = async () => {
|
|
||||||
const rel = {}
|
|
||||||
rel.categoryIds = categoryIdList.value.map(item => parseInt(item, 10))
|
|
||||||
rel.channelIds = channelIdList.value.map(item => parseInt(item, 10))
|
|
||||||
rel.imgList = imgFileList.value
|
|
||||||
rel.tagList = tagList.value
|
|
||||||
if (editForm.value.publishDate === '') {
|
|
||||||
editForm.value.publishDate = null
|
|
||||||
}
|
|
||||||
if (editForm.value.articleTime === '') {
|
|
||||||
editForm.value.articleTime = null
|
|
||||||
}
|
|
||||||
elEditFormRef.value.validate(async valid => {
|
|
||||||
if (valid) {
|
|
||||||
let res
|
|
||||||
if (isEdit.value) {
|
|
||||||
res = await updateArticle({ article: editForm.value, rel: rel })
|
|
||||||
} else {
|
|
||||||
res = await addArticle({ article: editForm.value, rel: rel })
|
|
||||||
}
|
|
||||||
if (res.code === 0) {
|
|
||||||
ElMessage({
|
|
||||||
type: 'success',
|
|
||||||
message: isEdit.value ? '修改成功' : '添加成功!'
|
|
||||||
})
|
|
||||||
if (!isEdit.value && res.data && res.data.article) {
|
|
||||||
const query = { id: res.data.article.ID }
|
|
||||||
router.replace({ name: 'articleEdit', query })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返回按钮
|
|
||||||
const handleFormClose = () => {
|
|
||||||
router.go(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style type="scss">
|
|
||||||
.admin-box .el-table td .cell {
|
|
||||||
line-height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-truncate .cell {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 4;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-item {
|
|
||||||
width: 90px;
|
|
||||||
height: 90px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,36 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="gva-search-box">
|
<div class="gva-search-box">
|
||||||
<el-form ref="elSearchFormRef" :inline="true" :model="searchInfo" label-width="90px" class="demo-form-inline"
|
<el-form ref="elSearchFormRef" :inline="true" :model="searchInfo" label-width="0" class="demo-form-inline"
|
||||||
@keyup.enter="handleSubmitSearch">
|
@keyup.enter="handleSubmitSearch">
|
||||||
<el-form-item label="创建日期" prop="createdAt" style="width:300px">
|
<el-form-item prop="createdAt" style="width:260px">
|
||||||
<template #label>
|
|
||||||
<span>
|
|
||||||
<el-tooltip content="搜索范围是创建开始日期(包含)至创建结束日期(包含)" placement="top-start">
|
<el-tooltip content="搜索范围是创建开始日期(包含)至创建结束日期(包含)" placement="top-start">
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<QuestionFilled />
|
<QuestionFilled />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
创建日期
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<el-date-picker v-model="searchInfo.dateRange" type="daterange" value-format="YYYY-MM-DD" :clearable="false"
|
<el-date-picker v-model="searchInfo.dateRange" type="daterange" value-format="YYYY-MM-DD" :clearable="false"
|
||||||
:editable="false" />
|
:editable="false" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="关键词" style="width:300px">
|
<el-form-item style="width:200px">
|
||||||
<template #label>
|
|
||||||
<span>
|
|
||||||
<el-tooltip content="从标题、副标题、摘要中搜索" placement="top-start">
|
<el-tooltip content="从标题、副标题、摘要中搜索" placement="top-start">
|
||||||
<el-icon>
|
<el-input v-model="searchInfo.keyword" class="keyword" placeholder="请输入关键词" clearable style="width:100%" />
|
||||||
<QuestionFilled />
|
|
||||||
</el-icon>
|
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
关键词
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<el-input v-model="searchInfo.keyword" class="keyword" placeholder="请输入" clearable style="width:100%" />
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所属栏目" prop="channelId" style="width:300px">
|
<!-- <el-form-item label="所属栏目" prop="channelId" style="width:300px">
|
||||||
<el-cascader v-model="searchChannelId" :options="channelOptions" style="width:100%"
|
<el-cascader v-model="searchChannelId" :options="channelOptions" style="width:100%"
|
||||||
:props="{ label: 'title', value: 'ID', disabled: 'disabled', emitPath: false }" :show-all-levels="true"
|
:props="{ label: 'title', value: 'ID', disabled: 'disabled', emitPath: false }" :show-all-levels="true"
|
||||||
clearable placeholder="请选择" filterable />
|
clearable placeholder="请选择" filterable />
|
||||||
|
|
@ -39,9 +26,9 @@
|
||||||
<el-cascader v-model="searchCategoryId" :options="categoryOptions" style="width:100%"
|
<el-cascader v-model="searchCategoryId" :options="categoryOptions" style="width:100%"
|
||||||
:props="{ label: 'title', value: 'ID', disabled: 'disabled', emitPath: false }" :show-all-levels="true"
|
:props="{ label: 'title', value: 'ID', disabled: 'disabled', emitPath: false }" :show-all-levels="true"
|
||||||
clearable placeholder="请选择" filterable />
|
clearable placeholder="请选择" filterable />
|
||||||
</el-form-item>
|
</el-form-item> -->
|
||||||
<el-form-item label="发布状态" prop="status" style="width:300px">
|
<el-form-item label="" prop="status" style="width:200px">
|
||||||
<el-select v-model="searchInfo.status" placeholder="请选择" clearable style="width:300px">
|
<el-select v-model="searchInfo.status" placeholder="请选择状态" clearable style="width:300px">
|
||||||
<el-option v-for="item in statusOptions" :key="item.key" :label="item.label" :value="item.key" />
|
<el-option v-for="item in statusOptions" :key="item.key" :label="item.label" :value="item.key" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
@ -56,8 +43,6 @@
|
||||||
<el-button type="primary" icon="plus" @click="handleAdd('0')">新增文章</el-button>
|
<el-button type="primary" icon="plus" @click="handleAdd('0')">新增文章</el-button>
|
||||||
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length"
|
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length"
|
||||||
@click="handleMultiDelete">删除</el-button>
|
@click="handleMultiDelete">删除</el-button>
|
||||||
<el-button icon="plus" style="margin-left: 10px;" :disabled="!multipleSelection.length"
|
|
||||||
@click="handleMultiPublish">发布</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 由于此处菜单跟左侧列表一一对应所以不需要分页 pageSize默认999 -->
|
<!-- 由于此处菜单跟左侧列表一一对应所以不需要分页 pageSize默认999 -->
|
||||||
|
|
@ -114,21 +99,24 @@
|
||||||
<el-table-column align="left" label="状态" width="80">
|
<el-table-column align="left" label="状态" width="80">
|
||||||
<template #default="scope">{{ formatStatus(scope.row.status) }}</template>
|
<template #default="scope">{{ formatStatus(scope.row.status) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="left" fixed="right" label="操作" width="80">
|
<el-table-column align="left" fixed="right" label="操作" width="140">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div>
|
<div class="flex md-2">
|
||||||
<el-button type="primary" link icon="memo" :disabled="scope.row.status === 2"
|
<el-button type="primary" link icon="memo" @click="handleRowPreview(scope.row)">预览</el-button>
|
||||||
@click="handleRowPreview(scope.row)">预览</el-button>
|
<el-button v-if="scope.row.status === 1" type="success" link icon="check"
|
||||||
|
@click="handleRowChange(scope.row, 2)">提审</el-button>
|
||||||
|
<el-button v-if="scope.row.status === 2" type="primary" link icon="WindPower"
|
||||||
|
@click="handleRowReview(scope.row)">审核</el-button>
|
||||||
|
<el-button v-if="scope.row.status === 3" type="success" link icon="top"
|
||||||
|
@click="handleRowChange(scope.row, 4)">发布</el-button>
|
||||||
|
<el-button v-if="scope.row.status === 4" type="warning" link icon="bottom"
|
||||||
|
@click="handleRowChange(scope.row, 5)">撤销</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="flex">
|
||||||
<el-button type="primary" link icon="plus" :disabled="scope.row.status === 2"
|
<el-button :type="scope.row.status === 1 || scope.row.status === 5 ? 'primary' : 'info'" link icon="edit"
|
||||||
@click="handleRowChange(scope.row)">发布</el-button>
|
:disabled="scope.row.status === 4 || scope.row.status === 2"
|
||||||
</div>
|
@click="handleRowEdit(scope.row.ID)">编辑</el-button>
|
||||||
<div>
|
<el-button type="danger" link icon="delete" @click="handleRowDelete(scope.row.ID)">删除</el-button>
|
||||||
<el-button type="primary" link icon="edit" @click="handleRowEdit(scope.row.ID)">编辑</el-button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-button type="primary" link icon="delete" @click="handleRowDelete(scope.row.ID)">删除</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
@ -139,12 +127,13 @@
|
||||||
@size-change="handleSizeChange" />
|
@size-change="handleSizeChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ArticleEdit ref="articleEditRef" :title="articleEditTitle" @on-save="handlerSaveArticle" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, nextTick } from 'vue'
|
import { ref, watch, nextTick } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { formatDate } from '@/utils/format'
|
import { formatDate } from '@/utils/format'
|
||||||
import { getSourceList } from '@/api/source'
|
import { getSourceList } from '@/api/source'
|
||||||
|
|
@ -154,14 +143,19 @@ import { formatTimeToStr } from '@/utils/date'
|
||||||
import { equalArr } from '@/utils/arr'
|
import { equalArr } from '@/utils/arr'
|
||||||
import {
|
import {
|
||||||
getArticleList,
|
getArticleList,
|
||||||
releaseArticle,
|
|
||||||
deleteArticle,
|
deleteArticle,
|
||||||
|
deleteArticleByIds,
|
||||||
setArticleChannels,
|
setArticleChannels,
|
||||||
setArticleCategories
|
setArticleCategories,
|
||||||
|
submitArticle,
|
||||||
|
reviewArticle,
|
||||||
|
releaseArticle,
|
||||||
|
cancelArticle
|
||||||
} from '@/api/article'
|
} from '@/api/article'
|
||||||
|
import ArticleEdit from '@/view/content/components/articleEdit.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const articleEditTitle = ref('')
|
||||||
|
const articleEditRef = ref(false)
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const pageSize = ref(10)
|
const pageSize = ref(10)
|
||||||
|
|
@ -176,8 +170,11 @@ const articleTypeOptions = ref([
|
||||||
{ key: 2, label: '视频' },
|
{ key: 2, label: '视频' },
|
||||||
])
|
])
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ key: 1, label: '未发布' },
|
{ key: 1, label: '草稿' },
|
||||||
{ key: 2, label: '已发布' },
|
{ key: 2, label: '待审核' },
|
||||||
|
{ key: 3, label: '待发布' },
|
||||||
|
{ key: 4, label: '已发布' },
|
||||||
|
{ key: 5, label: '已撤销' },
|
||||||
])
|
])
|
||||||
|
|
||||||
const formatArticleType = (value) => {
|
const formatArticleType = (value) => {
|
||||||
|
|
@ -217,10 +214,35 @@ const handleSelectionChange = (val) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMultiDelete = () => {
|
const handleMultiDelete = () => {
|
||||||
// @todo 批量删除
|
ElMessageBox.confirm('确定要删除所选文章吗?', '请确认', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async valid => {
|
||||||
|
const IDs = []
|
||||||
|
if (multipleSelection.value.length === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请选择要删除的数据'
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
const handleMultiPublish = () => {
|
multipleSelection.value &&
|
||||||
// @todo 批量发布
|
multipleSelection.value.map(item => {
|
||||||
|
IDs.push(item.ID)
|
||||||
|
})
|
||||||
|
const res = await deleteArticleByIds({ IDs })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '删除成功'
|
||||||
|
})
|
||||||
|
if (tableData.value.length === IDs.length && page.value > 1) {
|
||||||
|
page.value--
|
||||||
|
}
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
|
|
@ -231,7 +253,7 @@ const handleResetSearch = () => {
|
||||||
|
|
||||||
// 搜索
|
// 搜索
|
||||||
const handleSubmitSearch = () => {
|
const handleSubmitSearch = () => {
|
||||||
elSearchFormRef.value?.validate(async (valid) => {
|
elSearchFormRef.value?.validate(async valid => {
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
if (searchInfo.value.status === '') {
|
if (searchInfo.value.status === '') {
|
||||||
searchInfo.value.status = null
|
searchInfo.value.status = null
|
||||||
|
|
@ -260,22 +282,28 @@ const handleCurrentChange = (val) => {
|
||||||
|
|
||||||
// 添加
|
// 添加
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
router.push({ name: 'articleEdit' })
|
articleEditTitle.value = '添加文章'
|
||||||
|
articleEditRef.value.openPage({ articleId: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改
|
// 修改
|
||||||
const handleRowEdit = (ID) => {
|
const handleRowEdit = (ID) => {
|
||||||
const query = { id: ID }
|
articleEditTitle.value = '编辑文章 ID:' + ID
|
||||||
router.push({ name: 'articleEdit', query })
|
articleEditRef.value.openPage({ articleId: ID })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存文章后
|
||||||
|
const handlerSaveArticle = () => {
|
||||||
|
getTableData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
const handleRowDelete = (ID) => {
|
const handleRowDelete = (ID) => {
|
||||||
ElMessageBox.confirm('此操作将永久删除文章, 是否继续?', '提示', {
|
ElMessageBox.confirm('此操作将删除文章, 是否继续?', '请确认', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(async () => {
|
}).then(async valid => {
|
||||||
const res = await deleteArticle({ ID })
|
const res = await deleteArticle({ ID })
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
|
|
@ -290,23 +318,67 @@ const handleRowDelete = (ID) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发布
|
// 提审、发布、撤销
|
||||||
const handleRowChange = (row) => {
|
const handleRowChange = (row, status) => {
|
||||||
ElMessageBox.confirm('此操作将发布文章, 是否继续?', '提示', {
|
let msg
|
||||||
|
if (status === 2) {
|
||||||
|
msg = '文章提交审核'
|
||||||
|
} else if (status === 4) {
|
||||||
|
msg = '发布文章到网站'
|
||||||
|
} else if (status === 5) {
|
||||||
|
msg = '从网站撤销文章'
|
||||||
|
}
|
||||||
|
ElMessageBox.confirm('此操作将' + msg + ', 是否继续?', '请确认', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(async () => {
|
}).then(async valid => {
|
||||||
const res = await releaseArticle({ ID: row.ID, status: 2 })
|
let res
|
||||||
|
if (status === 2) {
|
||||||
|
res = await submitArticle({ ID: row.ID })
|
||||||
|
} else if (status === 4) {
|
||||||
|
res = await releaseArticle({ ID: row.ID })
|
||||||
|
} else if (status === 5) {
|
||||||
|
res = await cancelArticle({ ID: row.ID, status: 2 })
|
||||||
|
}
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: '发布成功!'
|
message: msg + '成功!'
|
||||||
})
|
})
|
||||||
if (tableData.value.length === 1 && page.value > 1) {
|
row.status = status
|
||||||
page.value--
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 审核文章,弹出选择框
|
||||||
|
const handleRowReview = row => {
|
||||||
|
ElMessageBox.confirm('请确认是否审核通过,确认请按“是”,否则请按“否”,取消请按“ESC”关闭弹窗', '请确认', {
|
||||||
|
distinguishCancelAndClose: true,
|
||||||
|
confirmButtonText: '是',
|
||||||
|
cancelButtonText: '否',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async valid => {
|
||||||
|
// 确认通过,状态为待发布
|
||||||
|
const res = await reviewArticle({ ID: row.ID, status: 3 })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: '审核确认通过!'
|
||||||
|
})
|
||||||
|
row.status = 3
|
||||||
|
}
|
||||||
|
}).catch(async (action) => {
|
||||||
|
// 确认不通过,状态为待提审
|
||||||
|
if (action === 'cancel') {
|
||||||
|
const res = await reviewArticle({ ID: row.ID, status: 1 })
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'warning',
|
||||||
|
message: '审核确认不通过!'
|
||||||
|
})
|
||||||
|
row.status = 1
|
||||||
}
|
}
|
||||||
getTableData()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -314,13 +386,13 @@ const handleRowChange = (row) => {
|
||||||
// 预览
|
// 预览
|
||||||
const handleRowPreview = (row) => {
|
const handleRowPreview = (row) => {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: 'success',
|
type: 'warning',
|
||||||
message: '开发中。。。'
|
message: '开发中。。。'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- 列表更新栏目相关 -----
|
// ----- 列表更新栏目相关 -----
|
||||||
const handleChangeChannels = async (row) => {
|
const handleChangeChannels = async row => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
const ids = row.channelIds.map(i => { return parseInt(i) })
|
const ids = row.channelIds.map(i => { return parseInt(i) })
|
||||||
if (ids.length === 0) {
|
if (ids.length === 0) {
|
||||||
|
|
@ -349,7 +421,7 @@ const restoreChannels = (row, message) => {
|
||||||
ElMessage({ type: 'error', message: message })
|
ElMessage({ type: 'error', message: message })
|
||||||
}
|
}
|
||||||
// ----- 列表分类相关 -----
|
// ----- 列表分类相关 -----
|
||||||
const handleChangeCategories = async (row) => {
|
const handleChangeCategories = async row => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
const ids = row.categoryIds.map(i => { return parseInt(i) })
|
const ids = row.categoryIds.map(i => { return parseInt(i) })
|
||||||
if (ids.length === 0) {
|
if (ids.length === 0) {
|
||||||
|
|
@ -385,7 +457,7 @@ watch(() => tableData.value, () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// ----- 查询 -----
|
// ----- 查询 -----
|
||||||
const getTableData = async () => {
|
const getTableData = async valid => {
|
||||||
const res = await getArticleList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
const res = await getArticleList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
tableData.value = res.data.list
|
tableData.value = res.data.list
|
||||||
|
|
@ -433,21 +505,21 @@ const initOptions = (data, optionsData, disabled) => {
|
||||||
const channelOptions = ref([])
|
const channelOptions = ref([])
|
||||||
const categoryOptions = ref([])
|
const categoryOptions = ref([])
|
||||||
|
|
||||||
const getCategoryData = async () => {
|
const getCategoryData = async valid => {
|
||||||
categoryOptions.value = []
|
categoryOptions.value = []
|
||||||
const res = await getCategoryTree()
|
const res = await getCategoryTree()
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
initOptions(res.data.categoryTree, categoryOptions.value, false)
|
initOptions(res.data.categoryTree, categoryOptions.value, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const getChannelData = async () => {
|
const getChannelData = async valid => {
|
||||||
channelOptions.value = []
|
channelOptions.value = []
|
||||||
const res = await getChannelTree()
|
const res = await getChannelTree()
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
initOptions(res.data.channelTree, channelOptions.value, false)
|
initOptions(res.data.channelTree, channelOptions.value, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const getSourceData = async () => {
|
const getSourceData = async valid => {
|
||||||
channelOptions.value = []
|
channelOptions.value = []
|
||||||
const res = await getSourceList({ page: 1, pageSize: 999 })
|
const res = await getSourceList({ page: 1, pageSize: 999 })
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,490 @@
|
||||||
|
<template>
|
||||||
|
<el-drawer v-model="showDrawer" size="90%" :show-close="false" :close-on-press-escape="false"
|
||||||
|
:close-on-click-modal="false">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-lg">{{ props.title }}</span>
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" style="width: 120px;" @click="handleFormSubmit">确 定</el-button>
|
||||||
|
<el-button @click="handleFormClose">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-loading="fullscreenLoading">
|
||||||
|
<div class="gva-form-box">
|
||||||
|
<div v-if="showErrMessage != ''">
|
||||||
|
{{ showErrMessage }}
|
||||||
|
</div>
|
||||||
|
<el-form v-else ref="elEditFormRef" label-position="top" :model="editForm" :rules="formRules">
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="17">
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="标题" prop="title">
|
||||||
|
<el-input v-model="editForm.title" autocomplete="off" :show-word-limit="true" maxlength="50" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="16">
|
||||||
|
<el-form-item label="副标题" prop="subtitle">
|
||||||
|
<el-input v-model="editForm.subtitle" autocomplete="off" :show-word-limit="true" maxlength="50" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="文章类型" prop="articleType">
|
||||||
|
<el-select v-model="editForm.articleType" placeholder="请选择">
|
||||||
|
<el-option v-for="item in articleTypeOptions" :key="item.key" :label="item.label"
|
||||||
|
:value="item.key" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="所属栏目" prop="channels">
|
||||||
|
<el-cascader v-model="channelIdList" :options="channelOptions" style="width:100%"
|
||||||
|
:props="{ label: 'title', value: 'ID', emitPath: false, expandTrigger: 'hover', multiple: true }"
|
||||||
|
:collapse-tags="true" :collapse-tags-tooltip="true" :max-collapse-tags="3" filterable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="文章分类" prop="categories">
|
||||||
|
<el-cascader v-model="categoryIdList" :options="categoryOptions" style="width:100%"
|
||||||
|
:props="{ label: 'title', value: 'ID', emitPath: false, expandTrigger: 'hover', multiple: true }"
|
||||||
|
:collapse-tags="true" :collapse-tags-tooltip="true" :max-collapse-tags="2" filterable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-form-item label="文章内容" prop="content">
|
||||||
|
<RichEdit v-model="editForm.content" style="height: 50rem;" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1" align="center">
|
||||||
|
<el-divider direction="vertical" style="height: 100%" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="摘要" prop="desc">
|
||||||
|
<el-input v-model="editForm.desc" type="textarea" rows="7" :show-word-limit="true" maxlength="100"
|
||||||
|
autocomplete="off" resize="none" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="作者" prop="author">
|
||||||
|
<el-input v-model="editForm.author" autocomplete="off" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="来源" prop="source">
|
||||||
|
<el-select v-model="editForm.source" placeholder="请选择" allow-create clearable filterable>
|
||||||
|
<el-option v-for="item in sourceOptions" :key="item" :label="item" :value="item" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="跳转链接" prop="source">
|
||||||
|
<el-input v-model="editForm.url" autocomplete="off" placeholder="https://" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="发布时间">
|
||||||
|
<el-date-picker v-model="editForm.publishDate" type="datetime" placeholder="请选择" style="width:100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="文章标签" prop="tags">
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<el-tag v-for="(item, index) in tagList" :key="index" type="info" closable size="large"
|
||||||
|
@close="tagList.splice(index, 1)">
|
||||||
|
{{ item }}
|
||||||
|
</el-tag>
|
||||||
|
<el-select v-model="selectTag" filterable remote allow-create reserve-keyword placeholder="请输入"
|
||||||
|
:remote-method="loadRemoteTag" :loading="loadingTag" style="width: 100px" @change="handleSelectTag">
|
||||||
|
<el-option v-for="item in tagOptions" :key="item" :label="item" :value="item" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="图片(首张图可用来做栏目列表用)" prop="imgs">
|
||||||
|
<ChooseImg ref="chooseImg" category="article_imgs" size="800px" @on-select="handleSelectImg" />
|
||||||
|
<template #label>
|
||||||
|
<div style="margin-bottom: 10px;">图片(首张图可用来做栏目列表用)</div>
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<el-button type="success" plain icon="folder" @click="handleChooseImg">媒体库</el-button>
|
||||||
|
<upload-common class="upload-btn-media-library" category="article_imgs"
|
||||||
|
@on-success="handleImgUpload" />
|
||||||
|
<el-tooltip effect="dark" content="图片超过 512K 或者 长宽 > 1080 会自动压缩后再上传" placement="top-start">
|
||||||
|
<upload-image :file-size="512" :max-w-h="1080" category="article_imgs"
|
||||||
|
@on-success="handleImgUpload" />
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="media">
|
||||||
|
<div v-for="(item, key) in imgFileList" :key="key" class="media-box">
|
||||||
|
<div class="img-box-list">
|
||||||
|
<el-image :key="key" :src="getUrl(item.url)" fit="cover" :preview-src-list="[getUrl(item.url)]"
|
||||||
|
hide-on-click-modal style="width: 138px; height: 138px;">
|
||||||
|
<template #error>
|
||||||
|
<div class="img-box-list">
|
||||||
|
<el-icon>
|
||||||
|
<picture />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</div>
|
||||||
|
<div class="img-title">
|
||||||
|
<el-button size="small" type="default" icon="delete" @click="handleImgRemove(item)">删除</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { getUrl } from '@/utils/image'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import RichEdit from '@/components/richtext/rich-edit.vue'
|
||||||
|
import ChooseImg from '@/components/chooseImg/index.vue'
|
||||||
|
import { getSourceList } from '@/api/source'
|
||||||
|
import { getCategoryTree } from '@/api/category'
|
||||||
|
import { getChannelTree } from '@/api/channel'
|
||||||
|
import { getTagList } from '@/api/tag'
|
||||||
|
import UploadCommon from '@/components/upload/common.vue'
|
||||||
|
import UploadImage from '@/components/upload/image.vue'
|
||||||
|
import {
|
||||||
|
addArticle,
|
||||||
|
updateArticle,
|
||||||
|
getArticleById,
|
||||||
|
} from '@/api/article'
|
||||||
|
import { getFetcherArticleById } from '@/api/fetcher'
|
||||||
|
|
||||||
|
// 组件定义
|
||||||
|
defineOptions({
|
||||||
|
name: 'ArticleEdit',
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['on-save', 'on-close'])
|
||||||
|
const props = defineProps({
|
||||||
|
title: { type: String, default: '' }
|
||||||
|
})
|
||||||
|
|
||||||
|
const showDrawer = ref(false)
|
||||||
|
const showErrMessage = ref('')
|
||||||
|
const fullscreenLoading = ref(true)
|
||||||
|
const isEdit = ref(false) // 确定是修改还是增加 isEdit = true 为修改,否则为增加
|
||||||
|
const fetcherArticleId = ref(0) // 记录从抓取库导入的文章id
|
||||||
|
const elEditFormRef = ref()
|
||||||
|
const checkCategories = (rule, value, callback) => {
|
||||||
|
if (categoryIdList.value.length === 0) {
|
||||||
|
callback(new Error(rule.message))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const checkChannels = (rule, value, callback) => {
|
||||||
|
if (channelIdList.value.length === 0) {
|
||||||
|
callback(new Error(rule.message))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const formRules = reactive({
|
||||||
|
articleType: [
|
||||||
|
{ required: true, message: '请输选择文章类型', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
title: [
|
||||||
|
{ required: true, message: '请输入文章名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
subtitle: [
|
||||||
|
{ required: true, message: '请输入文章副标题', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
categories: [
|
||||||
|
{ validator: checkCategories, message: '请选择文章分类', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
channels: [
|
||||||
|
{ validator: checkChannels, message: '请选择所属栏目', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
content: [
|
||||||
|
{ required: true, message: '请输入文章内容', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
})
|
||||||
|
const articleTypeOptions = ref([
|
||||||
|
{ key: 1, label: '图文' },
|
||||||
|
{ key: 2, label: '视频' },
|
||||||
|
])
|
||||||
|
const editForm = ref({
|
||||||
|
ID: 0,
|
||||||
|
articleType: '',
|
||||||
|
title: '',
|
||||||
|
subtitle: '',
|
||||||
|
author: '',
|
||||||
|
channels: [],
|
||||||
|
categories: [],
|
||||||
|
desc: '',
|
||||||
|
source: '',
|
||||||
|
url: '',
|
||||||
|
publishDate: '',
|
||||||
|
articleTime: '',
|
||||||
|
tags: '',
|
||||||
|
imgs: '',
|
||||||
|
})
|
||||||
|
// options 相关
|
||||||
|
const initOptions = (data, optionsData) => {
|
||||||
|
data &&
|
||||||
|
data.forEach(item => {
|
||||||
|
if (item.children && item.children.length) {
|
||||||
|
const option = {
|
||||||
|
title: item.title,
|
||||||
|
ID: String(item.ID),
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
initOptions(
|
||||||
|
item.children,
|
||||||
|
option.children,
|
||||||
|
)
|
||||||
|
optionsData.push(option)
|
||||||
|
} else {
|
||||||
|
const option = {
|
||||||
|
title: item.title,
|
||||||
|
ID: String(item.ID),
|
||||||
|
}
|
||||||
|
optionsData.push(option)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以下为 options 使用
|
||||||
|
const channelOptions = ref([])
|
||||||
|
const categoryOptions = ref([])
|
||||||
|
const sourceOptions = ref([])
|
||||||
|
|
||||||
|
const getCategoryData = async () => {
|
||||||
|
categoryOptions.value = []
|
||||||
|
const res = await getCategoryTree()
|
||||||
|
if (res.code === 0) {
|
||||||
|
initOptions(res.data.categoryTree, categoryOptions.value, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const getChannelData = async () => {
|
||||||
|
channelOptions.value = []
|
||||||
|
const res = await getChannelTree()
|
||||||
|
if (res.code === 0) {
|
||||||
|
initOptions(res.data.channelTree, channelOptions.value, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const getSourceData = async () => {
|
||||||
|
sourceOptions.value = []
|
||||||
|
const res = await getSourceList({ page: 1, pageSize: 999 })
|
||||||
|
if (res.code === 0) {
|
||||||
|
sourceOptions.value = res.data.list && res.data.list.map(item => item.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyForm = () => {
|
||||||
|
channelIdList.value = []
|
||||||
|
categoryIdList.value = []
|
||||||
|
imgFileList.value = []
|
||||||
|
tagList.value = []
|
||||||
|
editForm.value = {
|
||||||
|
ID: 0,
|
||||||
|
articleType: '',
|
||||||
|
title: '',
|
||||||
|
subtitle: '',
|
||||||
|
author: '',
|
||||||
|
channels: [],
|
||||||
|
categories: [],
|
||||||
|
desc: '',
|
||||||
|
source: '',
|
||||||
|
url: '',
|
||||||
|
publishDate: '',
|
||||||
|
articleTime: '',
|
||||||
|
tags: '',
|
||||||
|
imgs: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化方法
|
||||||
|
const openPage = async (params) => {
|
||||||
|
showDrawer.value = true
|
||||||
|
isEdit.value = false
|
||||||
|
|
||||||
|
emptyForm()
|
||||||
|
getCategoryData()
|
||||||
|
getChannelData()
|
||||||
|
getSourceData()
|
||||||
|
|
||||||
|
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
|
||||||
|
if (params.articleId && params.articleId > 0) {
|
||||||
|
// 从文章库编辑
|
||||||
|
isEdit.value = true
|
||||||
|
initFormByArticle(params.articleId)
|
||||||
|
} else if (params.fetcherId && params.fetcherId > 0) {
|
||||||
|
// 从爬虫库导入
|
||||||
|
initFormByFetcher(params.fetcherId)
|
||||||
|
} else {
|
||||||
|
// 新建
|
||||||
|
}
|
||||||
|
fullscreenLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const initFormByArticle = async (id) => {
|
||||||
|
const res = await getArticleById({ ID: id })
|
||||||
|
if (res.code === 0) {
|
||||||
|
// 初始化表单数据
|
||||||
|
editForm.value = res.data.article
|
||||||
|
// 初始化关联数据
|
||||||
|
const { categories, channels, imgs, tags } = res.data.article
|
||||||
|
categoryIdList.value = categories && categories.map(item => String(item.ID))
|
||||||
|
channelIdList.value = channels && channels.map(item => String(item.ID))
|
||||||
|
if (imgs && (imgs !== '')) {
|
||||||
|
imgFileList.value = JSON.parse(imgs)
|
||||||
|
}
|
||||||
|
if (tags !== '') {
|
||||||
|
tagList.value = tags.split(',')
|
||||||
|
}
|
||||||
|
fullscreenLoading.value = false
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '获取数据失败'
|
||||||
|
})
|
||||||
|
showErrMessage.value = '获取数据失败'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从爬虫数据中获取内容并填充至文章内容中
|
||||||
|
const initFormByFetcher = async (id) => {
|
||||||
|
const res = await getFetcherArticleById({ ID: id })
|
||||||
|
if (res.code === 0 && res.data && res.data.article) {
|
||||||
|
fetcherArticleId.value = id
|
||||||
|
const { title, author, source, content, publicTime } = res.data.article
|
||||||
|
editForm.value.title = title
|
||||||
|
editForm.value.subtitle = title
|
||||||
|
editForm.value.author = author
|
||||||
|
editForm.value.source = source
|
||||||
|
editForm.value.content = content
|
||||||
|
editForm.value.publishDate = publicTime
|
||||||
|
editForm.value.articleTime = publicTime
|
||||||
|
editForm.value.articleType = 1
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: '获取数据失败'
|
||||||
|
})
|
||||||
|
showErrMessage.value = '获取数据失败'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 以下为图片相关操作 ----
|
||||||
|
const chooseImg = ref(null)
|
||||||
|
const imgFileList = ref([])
|
||||||
|
const handleChooseImg = () => {
|
||||||
|
chooseImg.value.open()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const handleSelectImg = (file) => {
|
||||||
|
imgFileList.value.push({
|
||||||
|
key: file.key,
|
||||||
|
name: file.name,
|
||||||
|
url: file.url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const handleImgRemove = (file) => {
|
||||||
|
imgFileList.value = imgFileList.value.filter(item => item.key !== file.key)
|
||||||
|
}
|
||||||
|
const handleImgUpload = (file) => {
|
||||||
|
imgFileList.value.push({
|
||||||
|
key: file.key,
|
||||||
|
name: file.name,
|
||||||
|
url: file.url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 以下为tag相关操作 ----
|
||||||
|
const tagInputFlag = ref(false)
|
||||||
|
const selectTag = ref('')
|
||||||
|
const tagList = ref([])
|
||||||
|
const tagOptions = ref([])
|
||||||
|
const loadingTag = ref(false)
|
||||||
|
const loadRemoteTag = (query) => {
|
||||||
|
if (query) {
|
||||||
|
loadingTag.value = true
|
||||||
|
setTimeout(async () => {
|
||||||
|
const res = await getTagList({ page: 1, pageSize: 30, keyword: query })
|
||||||
|
if (res.code === 0) {
|
||||||
|
tagOptions.value = res.data.list.map(item => item.name)
|
||||||
|
}
|
||||||
|
loadingTag.value = false
|
||||||
|
}, 200)
|
||||||
|
} else {
|
||||||
|
tagOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleSelectTag = (val) => {
|
||||||
|
if (!tagList.value.includes(val)) {
|
||||||
|
tagList.value.push(val)
|
||||||
|
}
|
||||||
|
tagInputFlag.value = false
|
||||||
|
selectTag.value = ''
|
||||||
|
}
|
||||||
|
const categoryIdList = ref([])
|
||||||
|
const channelIdList = ref([])
|
||||||
|
|
||||||
|
// 添加/保存修改
|
||||||
|
const handleFormSubmit = async valid => {
|
||||||
|
if (editForm.value.publishDate === '') {
|
||||||
|
editForm.value.publishDate = null
|
||||||
|
}
|
||||||
|
if (editForm.value.articleTime === '') {
|
||||||
|
editForm.value.articleTime = null
|
||||||
|
}
|
||||||
|
elEditFormRef.value.validate(async valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const rel = {
|
||||||
|
categoryIds: categoryIdList.value.map(item => parseInt(item, 10)),
|
||||||
|
channelIds: channelIdList.value.map(item => parseInt(item, 10)),
|
||||||
|
imgList: imgFileList.value,
|
||||||
|
tagList: tagList.value,
|
||||||
|
fetcherArticleId: fetcherArticleId.value
|
||||||
|
}
|
||||||
|
let res
|
||||||
|
if (isEdit.value) {
|
||||||
|
res = await updateArticle({ article: editForm.value, rel: rel })
|
||||||
|
} else {
|
||||||
|
res = await addArticle({ article: editForm.value, rel: rel })
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: isEdit.value ? '修改成功' : '添加成功!'
|
||||||
|
})
|
||||||
|
showDrawer.value = false
|
||||||
|
emit('on-save')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回按钮
|
||||||
|
const handleFormClose = () => {
|
||||||
|
showDrawer.value = false
|
||||||
|
emit('on-close')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对外暴露方法
|
||||||
|
defineExpose({ openPage })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style type="scss">
|
||||||
|
.admin-box .el-table td .cell {
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-truncate .cell {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 4;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-item {
|
||||||
|
width: 90px;
|
||||||
|
height: 90px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -17,6 +17,11 @@
|
||||||
<el-date-picker v-model="searchInfo.dateRange" type="daterange" value-format="YYYY-MM-DD" :clearable="false"
|
<el-date-picker v-model="searchInfo.dateRange" type="daterange" value-format="YYYY-MM-DD" :clearable="false"
|
||||||
:editable="false" />
|
:editable="false" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<!-- <el-form-item prop="state" style="width:200px">
|
||||||
|
<el-select v-model="searchInfo.state" placeholder="请选择状态" clearable style="width:300px">
|
||||||
|
<el-option v-for="item in statusOptions" :key="item.key" :label="item.label" :value="item.key" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item> -->
|
||||||
<el-form-item label="关键词" style="width:300px">
|
<el-form-item label="关键词" style="width:300px">
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>
|
<span>
|
||||||
|
|
@ -40,8 +45,8 @@
|
||||||
<!-- 由于此处菜单跟左侧列表一一对应所以不需要分页 pageSize默认999 -->
|
<!-- 由于此处菜单跟左侧列表一一对应所以不需要分页 pageSize默认999 -->
|
||||||
<el-table ref="multipleTable" :data="tableData" row-key="ID">
|
<el-table ref="multipleTable" :data="tableData" row-key="ID">
|
||||||
<el-table-column align="left" label="ID" min-width="60" prop="ID" />
|
<el-table-column align="left" label="ID" min-width="60" prop="ID" />
|
||||||
<el-table-column align="left" label="标题" min-width="270" prop="title" />
|
<el-table-column align="left" label="标题" min-width="300" prop="title" />
|
||||||
<el-table-column align="left" label="来源" min-width="120" prop="source">
|
<el-table-column align="left" label="来源" min-width="240" prop="source">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<a :href="scope.row.sourceUrl" target="_blank">{{ scope.row.source }}</a>
|
<a :href="scope.row.sourceUrl" target="_blank">{{ scope.row.source }}</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -52,6 +57,11 @@
|
||||||
{{ formatDate(scope.row.createTime) }}
|
{{ formatDate(scope.row.createTime) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column align="left" label="状态" min-width="80" prop="state">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatStatus(scope.row.state) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column align="left" label="发布时间" min-width="120" prop="createtime">
|
<el-table-column align="left" label="发布时间" min-width="120" prop="createtime">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ formatDate(scope.row.publicTime) }}
|
{{ formatDate(scope.row.publicTime) }}
|
||||||
|
|
@ -60,8 +70,8 @@
|
||||||
<el-table-column align="left" fixed="right" label="操作" width="80">
|
<el-table-column align="left" fixed="right" label="操作" width="80">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" link icon="plus" :disabled="scope.row.status === 2"
|
<el-button :type="scope.row.state === 1 ? 'info' : 'primary'" link icon="plus"
|
||||||
@click="handleRowEdit(scope.row.ID)">发布</el-button>
|
:disabled="scope.row.state === 1" @click="handleRowEdit(scope.row.ID)">导入</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
@ -72,17 +82,20 @@
|
||||||
@size-change="handleSizeChange" />
|
@size-change="handleSizeChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ArticleEdit ref="articleEditRef" title="导入文章" @on-save="handlerSaveArticle" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { formatDate } from '@/utils/format'
|
import { formatDate } from '@/utils/format'
|
||||||
import { formatTimeToStr } from '@/utils/date'
|
import { formatTimeToStr } from '@/utils/date'
|
||||||
import { getFetcherArticleList } from '@/api/fetcher'
|
import { getFetcherArticleList } from '@/api/fetcher'
|
||||||
|
import ArticleEdit from '@/view/content/components/articleEdit.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const showDrawer = ref(false)
|
||||||
|
const articleEditRef = ref(null)
|
||||||
|
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
|
|
@ -90,6 +103,15 @@ const pageSize = ref(10)
|
||||||
const searchInfo = ref({})
|
const searchInfo = ref({})
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
const elSearchFormRef = ref()
|
const elSearchFormRef = ref()
|
||||||
|
const statusOptions = ref([
|
||||||
|
{ key: 0, label: '未导入' },
|
||||||
|
{ key: 1, label: '已导入' },
|
||||||
|
])
|
||||||
|
|
||||||
|
const formatStatus = (value) => {
|
||||||
|
const rowLabel = statusOptions.value.filter(item => item.key === value)
|
||||||
|
return rowLabel && rowLabel[0] && rowLabel[0].label
|
||||||
|
}
|
||||||
|
|
||||||
const initSearchInfo = () => {
|
const initSearchInfo = () => {
|
||||||
const endDate = new Date()
|
const endDate = new Date()
|
||||||
|
|
@ -98,7 +120,7 @@ const initSearchInfo = () => {
|
||||||
dateRange: [
|
dateRange: [
|
||||||
formatTimeToStr(startDate, 'yyyy-MM-dd'),
|
formatTimeToStr(startDate, 'yyyy-MM-dd'),
|
||||||
formatTimeToStr(endDate, 'yyyy-MM-dd'),
|
formatTimeToStr(endDate, 'yyyy-MM-dd'),
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,13 +154,16 @@ const handleCurrentChange = (val) => {
|
||||||
|
|
||||||
// 修改
|
// 修改
|
||||||
const handleRowEdit = (ID) => {
|
const handleRowEdit = (ID) => {
|
||||||
const query = { fetcherId: ID }
|
// const query = { fetcherId: ID }
|
||||||
router.push({ name: 'articleEdit', query })
|
// router.push({ name: 'articleEdit', query })
|
||||||
|
showDrawer.value = true
|
||||||
|
articleEditRef.value.openPage({ fetcherId: ID })
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- 查询 -----
|
// ----- 查询 -----
|
||||||
const getTableData = async () => {
|
const getTableData = async () => {
|
||||||
const res = await getFetcherArticleList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
|
const state = 0
|
||||||
|
const res = await getFetcherArticleList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value, state })
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
tableData.value = res.data.list
|
tableData.value = res.data.list
|
||||||
total.value = res.data.total
|
total.value = res.data.total
|
||||||
|
|
@ -146,6 +171,11 @@ const getTableData = async () => {
|
||||||
}
|
}
|
||||||
getTableData()
|
getTableData()
|
||||||
|
|
||||||
|
// 保存文章后
|
||||||
|
const handlerSaveArticle = () => {
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style type="scss">
|
<style type="scss">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue