2024-10-31 09:56:55 +08:00
|
|
|
|
<template>
|
|
|
|
|
<div class="layout-pd">
|
|
|
|
|
<el-card v-loading="state.loading" shadow="hover" header="文章详情">
|
|
|
|
|
<el-form ref="articleFormRef" :model="state.data" :rules="state.rules" size="large" label-width="100px" class="mt20 mb20">
|
|
|
|
|
<el-row :gutter="35">
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
|
|
|
|
<el-form-item label="封面:" class="coverImg">
|
|
|
|
|
<!-- 上传文件 -->
|
2024-10-31 17:11:17 +08:00
|
|
|
|
<el-upload ref="uploadRef" class="h100 personal-user-left-upload" action="#" list-type="picture-card" :file-list="state.fileArray"
|
|
|
|
|
:auto-upload="false" :limit="1" :class="{ hide: state.coverHide }" @change="handleUploadChange" accept='image/*'>
|
2024-10-31 09:56:55 +08:00
|
|
|
|
<template #default>
|
|
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
|
|
</template>
|
|
|
|
|
<template #file="{ file }">
|
|
|
|
|
<div>
|
|
|
|
|
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
|
|
|
|
|
<span class="el-upload-list__item-actions">
|
|
|
|
|
<span
|
|
|
|
|
class="el-upload-list__item-preview"
|
|
|
|
|
@click="handlePictureCardPreview(file)"
|
|
|
|
|
>
|
|
|
|
|
<el-icon><zoom-in /></el-icon>
|
|
|
|
|
</span>
|
2024-10-31 17:11:17 +08:00
|
|
|
|
<!-- <span
|
2024-10-31 09:56:55 +08:00
|
|
|
|
v-if="!disabled"
|
|
|
|
|
class="el-upload-list__item-delete"
|
|
|
|
|
@click="handleDownload(file)"
|
|
|
|
|
>
|
|
|
|
|
<el-icon><Download /></el-icon>
|
2024-10-31 17:11:17 +08:00
|
|
|
|
</span> -->
|
2024-10-31 09:56:55 +08:00
|
|
|
|
<span
|
|
|
|
|
v-if="!disabled"
|
|
|
|
|
class="el-upload-list__item-delete"
|
|
|
|
|
@click="handleRemove(file)"
|
|
|
|
|
>
|
|
|
|
|
<el-icon><Delete /></el-icon>
|
|
|
|
|
</span>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-upload>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
|
|
|
|
<el-form-item label="模块名称:" prop="moduleName">
|
2024-10-31 17:11:17 +08:00
|
|
|
|
<el-select size="default" @change="handleChangeModule" v-model="state.data.moduleName" placeholder="请选择模块" clearable>
|
2024-10-31 09:56:55 +08:00
|
|
|
|
<el-option :data-op="item.id" v-for="(item,index) in moduleList" :key="index" :label="item.moduleName" :value="item.moduleName"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
|
|
|
|
<el-form-item label="标签名称:" prop="labelName">
|
2024-10-31 17:11:17 +08:00
|
|
|
|
<el-select size="default" v-model="state.data.labelName" placeholder="请选择标签" clearable class="ml10">
|
|
|
|
|
<el-option v-for="(item,index) in labelList" :key="index" :label="item.name" :value="item.name"></el-option>
|
|
|
|
|
</el-select>
|
2024-10-31 09:56:55 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
|
|
|
|
<el-form-item label="标题:" prop="title">
|
|
|
|
|
<el-input v-model="state.data.title" placeholder="请输入标题" clearable></el-input>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
|
|
|
|
<el-form-item label="文章类型:" prop="articletype">
|
|
|
|
|
<el-select size="default" v-model="state.data.articletype" placeholder="请选择文章类型" clearable>
|
|
|
|
|
<el-option label="图文" :value="0"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
|
|
|
|
<el-form-item label="是否置顶:" prop="top">
|
|
|
|
|
<el-switch v-model="state.data.top" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-form-item prop="text" label-width="0px">
|
2024-11-01 23:31:28 +08:00
|
|
|
|
<Editor v-model:get-html="state.data.text" style="width: 100%;"/>
|
2024-10-31 09:56:55 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-row class="flex mt15">
|
|
|
|
|
<div class="flex-margin" style="width: 100%;display: flex;justify-content: flex-end;">
|
2024-10-31 17:11:17 +08:00
|
|
|
|
<el-button size="larger" type="info" @click="cancel">
|
|
|
|
|
<SvgIcon name="ele-RefreshLeft" />
|
|
|
|
|
取消
|
|
|
|
|
</el-button>
|
2024-10-31 09:56:55 +08:00
|
|
|
|
<el-button size="larger" type="primary" @click="onSubmitForm">
|
|
|
|
|
<SvgIcon name="iconfont icon-shuxing" />
|
|
|
|
|
保存
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</el-row>
|
|
|
|
|
</el-form>
|
|
|
|
|
</el-card>
|
|
|
|
|
<!-- 封面预览弹窗 -->
|
|
|
|
|
<el-dialog v-model="dialogVisible">
|
2024-11-01 23:31:28 +08:00
|
|
|
|
<img width="100%" w-full :src="dialogImageUrl" alt="预览" />
|
2024-10-31 09:56:55 +08:00
|
|
|
|
</el-dialog>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts" name="articleDetail">
|
|
|
|
|
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
|
|
|
|
|
import { articleApi } from '/@/api/article';
|
2024-10-31 17:11:17 +08:00
|
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
2024-10-31 09:56:55 +08:00
|
|
|
|
import { ElMessage, UploadFile, UploadFiles } from 'element-plus';
|
2024-10-31 17:11:17 +08:00
|
|
|
|
import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue';
|
|
|
|
|
import mittBus from '/@/utils/mitt';
|
2024-10-31 09:56:55 +08:00
|
|
|
|
|
|
|
|
|
// 引入组件
|
|
|
|
|
const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'));
|
|
|
|
|
|
2024-10-31 17:11:17 +08:00
|
|
|
|
// 封面基本路径
|
|
|
|
|
const viteUrl = import.meta.env.VITE_API_URL;
|
|
|
|
|
|
2024-10-31 09:56:55 +08:00
|
|
|
|
// 引入 api 请求接口
|
|
|
|
|
const artApi = articleApi();
|
|
|
|
|
|
|
|
|
|
// 模块列表
|
|
|
|
|
const moduleList = ref([]);
|
|
|
|
|
|
2024-10-31 17:11:17 +08:00
|
|
|
|
// 标签列表
|
|
|
|
|
const labelList = ref([]);
|
|
|
|
|
|
2024-10-31 09:56:55 +08:00
|
|
|
|
// 获取文章id
|
|
|
|
|
const route = useRoute();
|
2024-10-31 17:11:17 +08:00
|
|
|
|
const router = useRouter();
|
2024-10-31 09:56:55 +08:00
|
|
|
|
const moduleName:any = route.query.moduleName;
|
|
|
|
|
const labelName:any = route.query.labelName;
|
2024-11-12 17:35:06 +08:00
|
|
|
|
const id:any = route.query.id;
|
2024-10-31 09:56:55 +08:00
|
|
|
|
|
|
|
|
|
const state = reactive({
|
|
|
|
|
data: {
|
|
|
|
|
labelName: '',
|
|
|
|
|
moduleName: '',
|
|
|
|
|
title: '',
|
|
|
|
|
articletype: '',
|
|
|
|
|
photo: '',
|
|
|
|
|
text: '',
|
|
|
|
|
top: false,// 是否置顶,这里提交的时候要换成0或1
|
|
|
|
|
},
|
|
|
|
|
loading: false,
|
|
|
|
|
rules: {
|
|
|
|
|
moduleName: { required: true, message: '请选择模块', trigger: 'blur' },
|
|
|
|
|
labelName: { required: true, message: '请输入标签名称', trigger: 'blur' },
|
|
|
|
|
title: { required: true, message: '请输入标题', trigger: 'blur' },
|
|
|
|
|
articletype: { required: true, message: '请选择文章类型', trigger: 'blur' },
|
|
|
|
|
photo: { required: true, message: '请上传图片', trigger: 'blur' },
|
|
|
|
|
top: { required: true, message: '请选择是否置顶', trigger: 'blur' },
|
|
|
|
|
text: { required: true, message: '请输入文章内容', trigger: 'blur' },
|
|
|
|
|
},
|
2024-10-31 17:11:17 +08:00
|
|
|
|
fileArray:[],// 编辑进来时,如果已经上传了图片,则保存图片地址
|
2024-10-31 09:56:55 +08:00
|
|
|
|
coverFile: {},
|
|
|
|
|
coverHide: false
|
|
|
|
|
})
|
|
|
|
|
|
2024-10-31 17:11:17 +08:00
|
|
|
|
// 模块选择
|
|
|
|
|
const handleChangeModule = async(val: any) => {
|
|
|
|
|
try {
|
|
|
|
|
state.data.labelName = '';
|
|
|
|
|
if(!val){labelList.value=[]; return;}
|
|
|
|
|
const op:any = event?.currentTarget;
|
|
|
|
|
let res = await artApi.getLabelList(op.dataset.op);
|
|
|
|
|
if(res?.success){
|
|
|
|
|
labelList.value = res.data;
|
|
|
|
|
}else{
|
|
|
|
|
ElMessage.error('标签列表获取失败!');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
ElMessage.error('标签列表获取失败!');
|
|
|
|
|
} finally {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 09:56:55 +08:00
|
|
|
|
// 获取文章详情
|
|
|
|
|
const getArticleDetail = async() => {
|
|
|
|
|
try {
|
|
|
|
|
state.loading = true;
|
|
|
|
|
let res = await artApi.getArticleList({
|
|
|
|
|
moduleName: moduleName,
|
|
|
|
|
labelName: labelName,
|
|
|
|
|
size: 1,
|
|
|
|
|
current: 1
|
|
|
|
|
});
|
|
|
|
|
if(res?.success) {
|
|
|
|
|
state.data = res.data.records[0];
|
|
|
|
|
// 下面这里处理一下,因为后端返回的是0和1,而前端需要的是true和false,到时候提交的时候再转换回来
|
|
|
|
|
// 这个报错没关系的,只是ts的语法检查,不影响运行
|
|
|
|
|
state.data.top = state.data.top === 1;
|
2024-10-31 17:11:17 +08:00
|
|
|
|
state.data.textid = state.data.id;
|
|
|
|
|
if(state.data.photo){
|
|
|
|
|
const never:any = [{name:state.data.title,url:viteUrl+state.data.photo}];
|
|
|
|
|
state.fileArray = never;
|
|
|
|
|
state.coverHide = true;
|
|
|
|
|
}
|
2024-10-31 09:56:55 +08:00
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
ElMessage.error('获取文章详情失败');
|
|
|
|
|
} finally {
|
|
|
|
|
state.loading = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-12 17:35:06 +08:00
|
|
|
|
// 获取文章详情-根据id
|
|
|
|
|
const getArticleDetailById = async(id:number) => {
|
|
|
|
|
try {
|
|
|
|
|
state.loading = true;
|
|
|
|
|
let res = await artApi.getArticleDetail(id);
|
|
|
|
|
if(res?.success) {
|
|
|
|
|
state.data = res.data;
|
|
|
|
|
// 下面这里处理一下,因为后端返回的是0和1,而前端需要的是true和false,到时候提交的时候再转换回来
|
|
|
|
|
// 这个报错没关系的,只是ts的语法检查,不影响运行
|
|
|
|
|
state.data.top = state.data.top === 1;
|
|
|
|
|
state.data.moduleName = moduleName;
|
|
|
|
|
state.data.labelName = labelName;
|
|
|
|
|
state.data.textid = state.data.id;
|
|
|
|
|
if(state.data.photo){
|
|
|
|
|
const never:any = [{name:state.data.title,url:viteUrl+state.data.photo}];
|
|
|
|
|
state.fileArray = never;
|
|
|
|
|
state.coverHide = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
ElMessage.error('获取文章详情失败');
|
|
|
|
|
} finally {
|
|
|
|
|
state.loading = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 09:56:55 +08:00
|
|
|
|
// 获取模块列表
|
|
|
|
|
const getModuleList = async() => {
|
|
|
|
|
try {
|
|
|
|
|
let res = await artApi.getModuleList();
|
|
|
|
|
if(res?.success){
|
|
|
|
|
moduleList.value = res.data;
|
|
|
|
|
}else{
|
|
|
|
|
ElMessage.error('模块列表获取失败!');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
ElMessage.error('模块列表获取失败!');
|
|
|
|
|
} finally {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const articleFormRef = ref();
|
|
|
|
|
|
|
|
|
|
// 保存文章
|
|
|
|
|
const onSubmitForm = () => {
|
|
|
|
|
articleFormRef.value.validate((valid: boolean) => {
|
2024-10-31 17:11:17 +08:00
|
|
|
|
if (valid && state.coverHide){
|
|
|
|
|
const form = {...state.data,top:state.data.top ? 1 : 0};
|
|
|
|
|
if(state.coverFile.type === undefined){
|
|
|
|
|
realSubmit(form);
|
|
|
|
|
}else{
|
|
|
|
|
uploadFile(state.coverFile,form);
|
|
|
|
|
}
|
|
|
|
|
}else{
|
|
|
|
|
ElMessage.error('请完善信息!');
|
|
|
|
|
}
|
2024-10-31 09:56:55 +08:00
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-31 17:11:17 +08:00
|
|
|
|
// 返回
|
|
|
|
|
const cancel = () =>{
|
|
|
|
|
mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route }));
|
|
|
|
|
router.push('/article');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 上传文件
|
|
|
|
|
const uploadFile = async(file: any,form: any) => {
|
|
|
|
|
try {
|
|
|
|
|
state.loading = true;
|
|
|
|
|
const formdata = new FormData();
|
|
|
|
|
formdata.append('file', file);
|
|
|
|
|
console.log(file)
|
|
|
|
|
let res = await artApi.uploadFile(formdata);
|
|
|
|
|
if(res?.success){
|
|
|
|
|
form.photo = res.data.path;
|
|
|
|
|
realSubmit(form);
|
|
|
|
|
}else{
|
|
|
|
|
ElMessage.error('封面上传失败!');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
state.loading = false;
|
|
|
|
|
ElMessage.error('封面上传失败!');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 文章上传-真正的上传操作
|
|
|
|
|
const realSubmit = async(form: any) => {
|
|
|
|
|
try {
|
|
|
|
|
state.loading = true;
|
|
|
|
|
let res = route.path == '/article/add' ? await artApi.saveArticle(form) : await artApi.updateArticle(form);
|
|
|
|
|
if(res?.success){
|
|
|
|
|
ElMessage.success('文章保存成功!');
|
|
|
|
|
cancel();
|
|
|
|
|
}else{
|
|
|
|
|
ElMessage.error('文章保存失败!');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
ElMessage.error('处理失败!');
|
|
|
|
|
} finally {
|
|
|
|
|
state.loading = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 09:56:55 +08:00
|
|
|
|
// 封面上传禁用
|
|
|
|
|
const disabled = ref(false);
|
|
|
|
|
// 封面弹窗
|
|
|
|
|
const dialogImageUrl = ref('');
|
|
|
|
|
const dialogVisible = ref(false);
|
|
|
|
|
|
|
|
|
|
const uploadRef = ref();
|
|
|
|
|
|
|
|
|
|
// 封面删除
|
|
|
|
|
const handleRemove = (file: UploadFile) => {
|
|
|
|
|
state.coverFile = {};
|
|
|
|
|
uploadRef.value.clearFiles();
|
2024-10-31 17:11:17 +08:00
|
|
|
|
state.coverHide = false;
|
2024-10-31 09:56:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 封面预览
|
|
|
|
|
const handlePictureCardPreview = (file: UploadFile) => {
|
|
|
|
|
dialogImageUrl.value = file.url!
|
|
|
|
|
dialogVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 封面下载
|
|
|
|
|
const handleDownload = (file: UploadFile) => {
|
2024-10-31 17:11:17 +08:00
|
|
|
|
console.log(file);
|
|
|
|
|
const fileUrl:any = file.url;
|
|
|
|
|
// window.location.href = fileUrl;
|
2024-10-31 09:56:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 封面图片更改
|
|
|
|
|
const handleUploadChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
|
2024-11-01 01:10:27 +08:00
|
|
|
|
if(uploadFile.raw.type.includes('image')){
|
|
|
|
|
const file:any = uploadFile.raw;
|
|
|
|
|
state.coverFile = file;
|
|
|
|
|
state.coverHide = true;
|
|
|
|
|
}else{
|
|
|
|
|
ElMessage.error('请上传图片文件!');
|
|
|
|
|
uploadRef.value.clearFiles();
|
|
|
|
|
}
|
2024-10-31 09:56:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
2024-11-12 17:35:06 +08:00
|
|
|
|
if(moduleName && labelName && route.path == '/article/edit') {
|
|
|
|
|
if(moduleName && moduleName !== '人才模块' && moduleName !== '简历模块'){
|
|
|
|
|
getArticleDetailById(id);
|
|
|
|
|
}else{
|
|
|
|
|
getArticleDetail();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-31 09:56:55 +08:00
|
|
|
|
getModuleList();
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.coverImg :deep(.hide .el-upload--picture-card) {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
</style>
|