视图修改、接口修改
This commit is contained in:
332
schoolNewsWeb/src/views/public/article/components/ArticleAdd.vue
Normal file
332
schoolNewsWeb/src/views/public/article/components/ArticleAdd.vue
Normal file
@@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<div class="article-add-view">
|
||||
<div class="page-header">
|
||||
<el-button @click="handleBack" :icon="ArrowLeft">返回</el-button>
|
||||
<h1 class="page-title">{{ isEdit ? '编辑文章' : '创建文章' }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="article-form">
|
||||
<el-form ref="formRef" :model="articleForm" :rules="rules" label-width="100px" label-position="top">
|
||||
<!-- 标题 -->
|
||||
<el-form-item label="文章标题" prop="resource.title">
|
||||
<el-input v-model="articleForm.resource.title" placeholder="请输入文章标题" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 分类和标签 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="文章分类" prop="resource.tagID">
|
||||
<el-select v-model="articleForm.resource.tagID" placeholder="请选择分类" style="width: 100%" :loading="categoryLoading" value-key="tagID">
|
||||
<el-option
|
||||
v-for="category in categoryList"
|
||||
:key="category.tagID || category.id"
|
||||
:label="category.name"
|
||||
:value="category.tagID||''"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 封面图 -->
|
||||
<el-form-item label="封面图片">
|
||||
<FileUpload
|
||||
:as-dialog="false"
|
||||
list-type="cover"
|
||||
v-model:cover-url="articleForm.resource.coverImage"
|
||||
accept="image/*"
|
||||
:max-size="2"
|
||||
module="article"
|
||||
tip="建议尺寸 800x450"
|
||||
@success="handleCoverUploadSuccess"
|
||||
@remove="removeCover"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 文章内容 -->
|
||||
<el-form-item label="文章内容" prop="resource.content">
|
||||
<RichTextComponent ref="editorRef" v-model="articleForm.resource.content" height="500px"
|
||||
placeholder="请输入文章内容..." />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 发布设置 -->
|
||||
<el-form-item label="发布设置">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-checkbox v-model="articleForm.resource.allowComment">允许评论</el-checkbox>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-checkbox v-model="articleForm.resource.isTop">置顶文章</el-checkbox>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-checkbox v-model="articleForm.resource.isRecommend">推荐文章</el-checkbox>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handlePublish" :loading="publishing">
|
||||
{{ isEdit ? '保存修改' : '立即发布' }}
|
||||
</el-button>
|
||||
<el-button @click="handleSaveDraft" :loading="savingDraft">
|
||||
保存草稿
|
||||
</el-button>
|
||||
<el-button @click="handlePreview">
|
||||
预览
|
||||
</el-button>
|
||||
<el-button @click="handleBack">
|
||||
取消
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 文章预览组件 -->
|
||||
<ArticleShow
|
||||
v-model="previewVisible"
|
||||
:as-dialog="true"
|
||||
title="文章预览"
|
||||
width="900px"
|
||||
:article-data="articleForm.resource"
|
||||
:category-list="categoryList"
|
||||
:show-edit-button="false"
|
||||
@close="previewVisible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import {
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElButton,
|
||||
ElRow,
|
||||
ElCol,
|
||||
ElCheckbox,
|
||||
ElMessage
|
||||
} from 'element-plus';
|
||||
import { ArrowLeft } from '@element-plus/icons-vue';
|
||||
import { RichTextComponent } from '@/components/text';
|
||||
import { FileUpload } from '@/components/file';
|
||||
import { ArticleShow } from '.';
|
||||
import { resourceTagApi, resourceApi } from '@/apis/resource';
|
||||
import { ResourceVO, Tag, TagType } from '@/types/resource';
|
||||
|
||||
defineOptions({
|
||||
name: 'ArticleAdd'
|
||||
});
|
||||
|
||||
interface Props {
|
||||
articleId?: string;
|
||||
showBackButton?: boolean;
|
||||
backButtonText?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showBackButton: true,
|
||||
backButtonText: '返回'
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
'back': [];
|
||||
'publish-success': [id: string];
|
||||
'save-draft-success': [];
|
||||
}>();
|
||||
|
||||
const formRef = ref();
|
||||
const editorRef = ref();
|
||||
const publishing = ref(false);
|
||||
const savingDraft = ref(false);
|
||||
const previewVisible = ref(false);
|
||||
|
||||
// 是否编辑模式
|
||||
const isEdit = ref(false);
|
||||
|
||||
// 数据状态
|
||||
const categoryList = ref<Tag[]>([]);
|
||||
const tagList = ref<Tag[]>([]);
|
||||
const categoryLoading = ref(false);
|
||||
const tagLoading = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const articleForm = ref<ResourceVO>({
|
||||
resource: {},
|
||||
tags: [],
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
'resource.title': [
|
||||
{ required: true, message: '请输入文章标题', trigger: 'blur' },
|
||||
{ min: 5, max: 100, message: '标题长度在 5 到 100 个字符', trigger: 'blur' }
|
||||
],
|
||||
'resource.tagID': [
|
||||
{ required: true, message: '请选择文章分类', trigger: 'change' }
|
||||
],
|
||||
'resource.content': [
|
||||
{ required: true, message: '请输入文章内容', trigger: 'blur' }
|
||||
]
|
||||
};
|
||||
|
||||
// 加载分类列表
|
||||
async function loadCategoryList() {
|
||||
try {
|
||||
categoryLoading.value = true;
|
||||
const result = await resourceTagApi.getTagsByType(TagType.ARTICLE_CATEGORY);
|
||||
if (result.success) {
|
||||
categoryList.value = result.dataList || [];
|
||||
} else {
|
||||
ElMessage.error(result.message || '加载分类失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载分类失败:', error);
|
||||
ElMessage.error('加载分类失败');
|
||||
} finally {
|
||||
categoryLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载标签列表
|
||||
async function loadTagList() {
|
||||
try {
|
||||
tagLoading.value = true;
|
||||
const result = await resourceTagApi.getTagList();
|
||||
if (result.success) {
|
||||
tagList.value = result.dataList || [];
|
||||
} else {
|
||||
ElMessage.error(result.message || '加载标签失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载标签失败:', error);
|
||||
ElMessage.error('加载标签失败');
|
||||
} finally {
|
||||
tagLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回
|
||||
function handleBack() {
|
||||
emit('back');
|
||||
}
|
||||
|
||||
// 发布文章
|
||||
async function handlePublish() {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
|
||||
publishing.value = true;
|
||||
|
||||
const result = await resourceApi.createResource(articleForm.value);
|
||||
if (result.success) {
|
||||
ElMessage.success('发布成功');
|
||||
emit('publish-success', result.data?.resource?.resourceID || '');
|
||||
} else {
|
||||
ElMessage.error(result.message || '发布失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('发布失败:', error);
|
||||
} finally {
|
||||
publishing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存草稿
|
||||
async function handleSaveDraft() {
|
||||
savingDraft.value = true;
|
||||
|
||||
try {
|
||||
// TODO: 调用API保存草稿
|
||||
console.log('保存草稿:', articleForm);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
ElMessage.success('草稿已保存');
|
||||
emit('save-draft-success');
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error);
|
||||
ElMessage.error('保存失败');
|
||||
} finally {
|
||||
savingDraft.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 预览
|
||||
function handlePreview() {
|
||||
if (!articleForm.value.resource.title) {
|
||||
ElMessage.warning('请先输入文章标题');
|
||||
return;
|
||||
}
|
||||
previewVisible.value = true;
|
||||
}
|
||||
|
||||
// 封面上传成功
|
||||
function handleCoverUploadSuccess(files: any[]) {
|
||||
if (files && files.length > 0) {
|
||||
console.log('封面上传成功:', files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除封面
|
||||
function removeCover() {
|
||||
articleForm.value.resource.coverImage = '';
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 并行加载分类和标签数据
|
||||
await Promise.all([
|
||||
loadCategoryList(),
|
||||
loadTagList()
|
||||
]);
|
||||
|
||||
// 如果是编辑模式,加载文章数据
|
||||
if (props.articleId) {
|
||||
try {
|
||||
isEdit.value = true;
|
||||
const result = await resourceApi.getResourceById(props.articleId);
|
||||
if (result.success && result.data) {
|
||||
articleForm.value = result.data;
|
||||
} else {
|
||||
ElMessage.error(result.message || '加载文章失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载文章失败:', error);
|
||||
ElMessage.error('加载文章失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.article-add-view {
|
||||
min-height: 100vh;
|
||||
background: #f5f7fa;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
.page-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.article-form {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 32px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user