完善文章管理

This commit is contained in:
Double-_-Z 2024-10-31 17:11:17 +08:00
parent 5a4d9181cb
commit be2914146e
4 changed files with 161 additions and 20 deletions

View File

@ -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'}
})
} }
}; };
} }

View File

@ -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 '';

View File

@ -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() => {
// 01truefalse // 01truefalse
// 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();
}) })

View File

@ -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,