完善文章管理
This commit is contained in:
parent
5a4d9181cb
commit
be2914146e
@ -11,6 +11,8 @@ import { baseUrlHost } from '../baseUrlHost';
|
|||||||
* @method getLabelList 获取标签列表
|
* @method getLabelList 获取标签列表
|
||||||
* @method deleteArticle 删除文章
|
* @method deleteArticle 删除文章
|
||||||
* @method getArticleDetail 获取文章详情
|
* @method getArticleDetail 获取文章详情
|
||||||
|
* @method saveArticle 保存文章
|
||||||
|
* @method updateArticle 更新文章
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function articleApi() {
|
export function articleApi() {
|
||||||
@ -45,6 +47,28 @@ export function articleApi() {
|
|||||||
url: baseUrlHost + `/cpArticle/${id}`,
|
url: baseUrlHost + `/cpArticle/${id}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
saveArticle: (data: object) => {
|
||||||
|
return request({
|
||||||
|
url: baseUrlHost + '/cpArticle',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateArticle: (data: object) => {
|
||||||
|
return request({
|
||||||
|
url: baseUrlHost + '/cpArticle',
|
||||||
|
method: 'put',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
uploadFile: (data: object) => {
|
||||||
|
return request({
|
||||||
|
url: baseUrlHost + '/enAttachment/upload',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded'}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
<el-form-item label="封面:">
|
<el-form-item label="封面:">
|
||||||
<el-image
|
<el-image
|
||||||
style="width: 100px;height: 100px;border-radius: 4px;"
|
style="width: 100px;height: 100px;border-radius: 4px;"
|
||||||
:src="state.data.photo"
|
:src="encodeURI(viteUrl+state.data.photo)"
|
||||||
:zoom-rate="1.2"
|
:zoom-rate="1.2"
|
||||||
:preview-src-list="[state.data.photo]"
|
:preview-src-list="[encodeURI(viteUrl+state.data.photo)]"
|
||||||
preview-teleported
|
preview-teleported
|
||||||
fit="cover"/>
|
fit="cover"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -52,7 +52,15 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<!-- <div class="mt5 mb20 ml10">文章内容:</div> -->
|
<!-- <div class="mt5 mb20 ml10">文章内容:</div> -->
|
||||||
<Editor v-model:get-html="state.data.text" v-model:get-text="state.data.text" :disable="true" />
|
<Editor v-model:get-html="state.data.text" :disable="true" />
|
||||||
|
<el-row class="flex mt15">
|
||||||
|
<div class="flex-margin" style="width: 100%;display: flex;justify-content: flex-end;">
|
||||||
|
<el-button size="larger" type="info" @click="cancel">
|
||||||
|
<SvgIcon name="ele-RefreshLeft" />
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
@ -61,12 +69,16 @@
|
|||||||
<script setup lang="ts" name="articleDetail">
|
<script setup lang="ts" name="articleDetail">
|
||||||
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
|
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
|
||||||
import { articleApi } from '/@/api/article';
|
import { articleApi } from '/@/api/article';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
|
import mittBus from '/@/utils/mitt';
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'));
|
const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'));
|
||||||
|
|
||||||
|
// 封面基本路径
|
||||||
|
const viteUrl = import.meta.env.VITE_API_URL;
|
||||||
|
|
||||||
// 引入 api 请求接口
|
// 引入 api 请求接口
|
||||||
const artApi = articleApi();
|
const artApi = articleApi();
|
||||||
|
|
||||||
@ -75,6 +87,7 @@ const moduleList = ref([]);
|
|||||||
|
|
||||||
// 获取模块名称和标签名称
|
// 获取模块名称和标签名称
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
const moduleName:any = route.query.moduleName;
|
const moduleName:any = route.query.moduleName;
|
||||||
const labelName:any = route.query.labelName;
|
const labelName:any = route.query.labelName;
|
||||||
|
|
||||||
@ -105,6 +118,7 @@ const getArticleDetail = async() => {
|
|||||||
});
|
});
|
||||||
if(res?.success) {
|
if(res?.success) {
|
||||||
state.data = res.data.records[0];
|
state.data = res.data.records[0];
|
||||||
|
console.log(encodeURI(viteUrl+state.data.photo))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('获取文章详情失败');
|
ElMessage.error('获取文章详情失败');
|
||||||
@ -128,6 +142,12 @@ const getModuleList = async() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
const cancel = () =>{
|
||||||
|
mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route }));
|
||||||
|
router.push('/article');
|
||||||
|
}
|
||||||
|
|
||||||
// 日期格式化
|
// 日期格式化
|
||||||
function dateFormatter(){
|
function dateFormatter(){
|
||||||
if(!state.data.createtime) return '';
|
if(!state.data.createtime) return '';
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||||
<el-form-item label="封面:" class="coverImg">
|
<el-form-item label="封面:" class="coverImg">
|
||||||
<!-- 上传文件 -->
|
<!-- 上传文件 -->
|
||||||
<el-upload ref="uploadRef" class="h100 personal-user-left-upload" action="#" list-type="picture-card"
|
<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">
|
:auto-upload="false" :limit="1" :class="{ hide: state.coverHide }" @change="handleUploadChange" accept='image/*'>
|
||||||
<template #default>
|
<template #default>
|
||||||
<el-icon><Plus /></el-icon>
|
<el-icon><Plus /></el-icon>
|
||||||
</template>
|
</template>
|
||||||
@ -21,13 +21,13 @@
|
|||||||
>
|
>
|
||||||
<el-icon><zoom-in /></el-icon>
|
<el-icon><zoom-in /></el-icon>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<!-- <span
|
||||||
v-if="!disabled"
|
v-if="!disabled"
|
||||||
class="el-upload-list__item-delete"
|
class="el-upload-list__item-delete"
|
||||||
@click="handleDownload(file)"
|
@click="handleDownload(file)"
|
||||||
>
|
>
|
||||||
<el-icon><Download /></el-icon>
|
<el-icon><Download /></el-icon>
|
||||||
</span>
|
</span> -->
|
||||||
<span
|
<span
|
||||||
v-if="!disabled"
|
v-if="!disabled"
|
||||||
class="el-upload-list__item-delete"
|
class="el-upload-list__item-delete"
|
||||||
@ -43,14 +43,16 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||||
<el-form-item label="模块名称:" prop="moduleName">
|
<el-form-item label="模块名称:" prop="moduleName">
|
||||||
<el-select size="default" v-model="state.data.moduleName" placeholder="请选择模块" clearable>
|
<el-select size="default" @change="handleChangeModule" v-model="state.data.moduleName" placeholder="请选择模块" clearable>
|
||||||
<el-option :data-op="item.id" v-for="(item,index) in moduleList" :key="index" :label="item.moduleName" :value="item.moduleName"></el-option>
|
<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-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||||
<el-form-item label="标签名称:" prop="labelName">
|
<el-form-item label="标签名称:" prop="labelName">
|
||||||
<el-input v-model="state.data.labelName" placeholder="请输入标签名称" clearable></el-input>
|
<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>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
|
||||||
@ -72,10 +74,14 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-form-item prop="text" label-width="0px">
|
<el-form-item prop="text" label-width="0px">
|
||||||
<Editor v-model:get-html="state.data.text" v-model:get-text="state.data.text" :disable="true" />
|
<Editor v-model:get-html="state.data.text" :disable="true" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-row class="flex mt15">
|
<el-row class="flex mt15">
|
||||||
<div class="flex-margin" style="width: 100%;display: flex;justify-content: flex-end;">
|
<div class="flex-margin" style="width: 100%;display: flex;justify-content: flex-end;">
|
||||||
|
<el-button size="larger" type="info" @click="cancel">
|
||||||
|
<SvgIcon name="ele-RefreshLeft" />
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
<el-button size="larger" type="primary" @click="onSubmitForm">
|
<el-button size="larger" type="primary" @click="onSubmitForm">
|
||||||
<SvgIcon name="iconfont icon-shuxing" />
|
<SvgIcon name="iconfont icon-shuxing" />
|
||||||
保存
|
保存
|
||||||
@ -94,21 +100,29 @@
|
|||||||
<script setup lang="ts" name="articleDetail">
|
<script setup lang="ts" name="articleDetail">
|
||||||
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
|
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
|
||||||
import { articleApi } from '/@/api/article';
|
import { articleApi } from '/@/api/article';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { ElMessage, UploadFile, UploadFiles } from 'element-plus';
|
import { ElMessage, UploadFile, UploadFiles } from 'element-plus';
|
||||||
import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue'
|
import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue';
|
||||||
|
import mittBus from '/@/utils/mitt';
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'));
|
const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'));
|
||||||
|
|
||||||
|
// 封面基本路径
|
||||||
|
const viteUrl = import.meta.env.VITE_API_URL;
|
||||||
|
|
||||||
// 引入 api 请求接口
|
// 引入 api 请求接口
|
||||||
const artApi = articleApi();
|
const artApi = articleApi();
|
||||||
|
|
||||||
// 模块列表
|
// 模块列表
|
||||||
const moduleList = ref([]);
|
const moduleList = ref([]);
|
||||||
|
|
||||||
|
// 标签列表
|
||||||
|
const labelList = ref([]);
|
||||||
|
|
||||||
// 获取文章id
|
// 获取文章id
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
const moduleName:any = route.query.moduleName;
|
const moduleName:any = route.query.moduleName;
|
||||||
const labelName:any = route.query.labelName;
|
const labelName:any = route.query.labelName;
|
||||||
|
|
||||||
@ -133,10 +147,29 @@ const state = reactive({
|
|||||||
top: { required: true, message: '请选择是否置顶', trigger: 'blur' },
|
top: { required: true, message: '请选择是否置顶', trigger: 'blur' },
|
||||||
text: { required: true, message: '请输入文章内容', trigger: 'blur' },
|
text: { required: true, message: '请输入文章内容', trigger: 'blur' },
|
||||||
},
|
},
|
||||||
|
fileArray:[],// 编辑进来时,如果已经上传了图片,则保存图片地址
|
||||||
coverFile: {},
|
coverFile: {},
|
||||||
coverHide: false
|
coverHide: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 模块选择
|
||||||
|
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 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取文章详情
|
// 获取文章详情
|
||||||
const getArticleDetail = async() => {
|
const getArticleDetail = async() => {
|
||||||
try {
|
try {
|
||||||
@ -152,6 +185,12 @@ const getArticleDetail = async() => {
|
|||||||
// 下面这里处理一下,因为后端返回的是0和1,而前端需要的是true和false,到时候提交的时候再转换回来
|
// 下面这里处理一下,因为后端返回的是0和1,而前端需要的是true和false,到时候提交的时候再转换回来
|
||||||
// 这个报错没关系的,只是ts的语法检查,不影响运行
|
// 这个报错没关系的,只是ts的语法检查,不影响运行
|
||||||
state.data.top = state.data.top === 1;
|
state.data.top = state.data.top === 1;
|
||||||
|
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) {
|
} catch (error) {
|
||||||
ElMessage.error('获取文章详情失败');
|
ElMessage.error('获取文章详情失败');
|
||||||
@ -180,12 +219,63 @@ const articleFormRef = ref();
|
|||||||
// 保存文章
|
// 保存文章
|
||||||
const onSubmitForm = () => {
|
const onSubmitForm = () => {
|
||||||
articleFormRef.value.validate((valid: boolean) => {
|
articleFormRef.value.validate((valid: boolean) => {
|
||||||
if (valid){
|
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('请完善信息!');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 封面上传禁用
|
// 封面上传禁用
|
||||||
const disabled = ref(false);
|
const disabled = ref(false);
|
||||||
// 封面弹窗
|
// 封面弹窗
|
||||||
@ -197,8 +287,8 @@ const uploadRef = ref();
|
|||||||
// 封面删除
|
// 封面删除
|
||||||
const handleRemove = (file: UploadFile) => {
|
const handleRemove = (file: UploadFile) => {
|
||||||
state.coverFile = {};
|
state.coverFile = {};
|
||||||
state.coverHide = false;
|
|
||||||
uploadRef.value.clearFiles();
|
uploadRef.value.clearFiles();
|
||||||
|
state.coverHide = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 封面预览
|
// 封面预览
|
||||||
@ -209,7 +299,9 @@ const handlePictureCardPreview = (file: UploadFile) => {
|
|||||||
|
|
||||||
// 封面下载
|
// 封面下载
|
||||||
const handleDownload = (file: UploadFile) => {
|
const handleDownload = (file: UploadFile) => {
|
||||||
console.log(file)
|
console.log(file);
|
||||||
|
const fileUrl:any = file.url;
|
||||||
|
// window.location.href = fileUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 封面图片更改
|
// 封面图片更改
|
||||||
@ -217,11 +309,11 @@ const handleUploadChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) =>
|
|||||||
const file:any = uploadFile.raw;
|
const file:any = uploadFile.raw;
|
||||||
state.coverFile = file;
|
state.coverFile = file;
|
||||||
state.coverHide = true;
|
state.coverHide = true;
|
||||||
|
console.log(uploadRef.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if(moduleName && labelName && route.path == '/article/edit')
|
if(moduleName && labelName && route.path == '/article/edit') getArticleDetail();
|
||||||
getArticleDetail();
|
|
||||||
getModuleList();
|
getModuleList();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -36,6 +36,11 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/vueAdminApi/, ''),
|
rewrite: (path) => path.replace(/^\/vueAdminApi/, ''),
|
||||||
},
|
},
|
||||||
|
'/upload': {
|
||||||
|
target: 'http://8.138.171.103',
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
'/gitee': {
|
'/gitee': {
|
||||||
target: 'https://gitee.com',
|
target: 'https://gitee.com',
|
||||||
ws: true,
|
ws: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user