This commit is contained in:
李远 2026-03-19 13:13:33 +08:00
parent 2a83395858
commit c9f943f8dd
5 changed files with 209 additions and 53 deletions

View File

@ -6,5 +6,13 @@ export const getNoticeListApi = (params: any) => {
}
//创建公告
export const createNoticeApi = (params: any) => {
return request.get('/notice/createNotice', {...params})
return request.post('/notice/createNotice', {...params})
}
//编辑草稿
export const editNoticeApi = (params: any) => {
return request.post('/notice/editNotice', {...params})
}
//删除草稿
export const deleteNoticeApi = (params: any) => {
return request.post('/notice/delNotice', {...params})
}

27
src/api/modules/upload.ts Normal file
View File

@ -0,0 +1,27 @@
import { request } from '@/api'; // 引入你已封装的request
/**
*
* @param file
* @returns Promise<{ url: string }> URL
*/
export const uploadImageApi = (file: File): Promise<{ url: string }> => {
// 构建FormData图片上传必须用FormData格式
const formData = new FormData();
formData.append('image', file); // 对应后端接收字段名image
// 使用你已封装的post请求禁用loading富文本自身有状态提示
return request.post<{ url: string }>(
'/upload/image',
formData,
{
// 覆盖默认配置:图片上传需要特殊的请求头
headers: {
'Content-Type': 'multipart/form-data', // 表单上传必须的头
showLoading: false // 禁用全局loading避免和编辑器loading冲突
},
timeout: 30 * 1000, // 图片上传超时时间30秒
noRetry: false // 允许重试(网络波动时自动重试)
}
);
};

View File

@ -7,24 +7,34 @@
<el-button type="primary" @click="handleCreate">新建</el-button>
</template>
</NoticeSearch>
<div class="tab">
<el-button :type="activeTab === 2 ? 'primary' : 'default'" @click="handleTabChange(2)">已发布</el-button>
<el-button :type="activeTab === 1 ? 'primary' : 'default'" @click="handleTabChange(1)">草稿</el-button>
</div>
<!-- 表格 -->
<NoticeTable :table-data="noticeTree" :columns="noticeTableColumnsOptions" row-key="id">
<template #status="{ row }">
{{ row.status === 1 ? '已发布' : '未发布' }}
<!-- 修正状态显示1=草稿2=已发布 -->
{{ row.status === 1 ? '草稿' : '已发布' }}
</template>
<template #action="{ row }">
<el-button type="primary" size="small" @click="handleView(row)">查看</el-button>
<el-button type="primary" size="small" v-if="row.status === 2" @click="handleView(row)">查看</el-button>
<!-- 草稿才显示编辑/删除已发布仅查看 -->
<template v-if="row.status === 1">
<el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button>
</template>
</template>
</NoticeTable>
<!-- 分页 -->
<NoticePagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
@pagination-change="handlePaginationChange" />
<!-- dialog -->
<NoticeDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" @confirm="handleSubmit"
<NoticeDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :show-footer="dialogType !== 'view'" @confirm="handleSubmit"
@cancel="handleCancel" @close="handleClose">
<NoticeForm :form-data="noticeForm" :form-rules="noticeRules" :form-items="noticeFormItem"
:options-map="noticeFormOptionsMap" ref="noticeFormRef" v-if="dialogType !== 'view'">
<template #noticeContent="{ row }">
<template #noticeContent="{ formData }">
<div style="border: 1px solid #e6e6e6; margin: 10px 0;">
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" mode="default"
style="border-bottom: 1px solid #ccc;" />
@ -33,7 +43,10 @@
</div>
</template>
</NoticeForm>
<div v-html="valueHtml"></div>
<div v-else class="view-content">
<h2>公告名称{{ noticeForm.noticeName }}</h2>
<div v-html="noticeForm.noticeContent"></div>
</div>
</NoticeDialog>
<!-- dialog表格 -->
</div>
@ -51,45 +64,55 @@ import { search, tableColumns, formItem } from './options'
import { rules } from './rules'
//
import { getNoticeListApi, createNoticeApi } from '@/api/modules/notice'
import { getNoticeListApi, createNoticeApi, editNoticeApi, deleteNoticeApi } from '@/api/modules/notice'
import { uploadImageApi } from '@/api/modules/upload'
import { DomEditor } from '@wangeditor/editor'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import '@wangeditor/editor/dist/css/style.css'
//
//
const editorRef = shallowRef()
// DomEditor
const toolbarRef = shallowRef()
// HTML
// HTML
const valueHtml = ref('')
//
const toolbarConfig = {
excludeKeys: ["group-video"]
excludeKeys: ["group-video", "fullScreen"]
}
const editorConfig = { placeholder: '请输入内容...' }
//
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
//
const handleCreated = (editor) => {
editorRef.value = editor
//
nextTick(() => {
const toolbar = DomEditor.getToolbar(editor)
if (toolbar) {
const curToolbarConfig = toolbar.getConfig()
console.log('curToolbarConfig.toolbarKeys', curToolbarConfig.toolbarKeys)
toolbarRef.value = toolbar
console.log('toolbarRef.value', valueHtml.value)
}
//
const editorConfig = {
placeholder: '请输入内容...',
MENU_CONF: {
uploadImage: {
customUpload: async (file: any, insertFn: any) => {
try {
// API
const res = await uploadImageApi(file);
//
insertFn(res.url);
} catch (error: any) {
// ElNotification
const errMsg = error?.message || error?.data?.msg || '图片上传失败';
alert(`图片${file.name}上传失败:${errMsg}`);
ElNotification({
message: errMsg || '未知的错误!',
type: 'error',
duration: 2000,
customClass: 'global-notification'
})
}
},
//
fieldName: 'image',
timeout: 30 * 1000,
maxFileSize: 5 * 1024 * 1024,
allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
}
}
}
/**
@ -99,10 +122,15 @@ const searchForm = ref({
noticeName: '',
startDateTime: '',
endDateTime: '',
status: 1,
status: 2,
})
const searchOptions = ref(search)
/**
* @description: 定义tab数据
*/
const activeTab = ref(2)
/**
* @description: 定义表格数据
*/
@ -131,20 +159,18 @@ const noticeForm = ref({
publishStartTime: '',
publishEndTime: '',
status: 1,
id: 0,
})
const noticeFormRef = ref()
const noticeRules = ref(rules())
const noticeFormItem = ref(formItem)
const noticeFormOptionsMap = ref({
status: [
{ label: '发布', value: 1 },
{ label: '草稿', value: 2 },
{ label: '发布', value: 2 },
{ label: '草稿', value: 1 },
],
})
/**
* @description: 定义公共数据处理方法
*/
/**
* @description: 定义请求数据方法
*/
@ -161,17 +187,16 @@ const getNoticeList = async () => {
const data: any = await getNoticeListApi(params)
//
noticeTree.value = data.data.map((item: any) => ({
id: item.id,
noticeName: item.name,
noticeContent: item.content,
startDateTime: item.publish_start_time,
publishTime: item.publish_time,
status: item.status,
}))
//
total.value = data.total
currentPage.value = data.current_page
pageSize.value = Number(data.per_page)
console.log(noticeTree.value)
}
/**
@ -192,8 +217,9 @@ const handleReset = () => {
noticeName: '',
startDateTime: '',
endDateTime: '',
status: 1,
status: 2,
}
activeTab.value = 2
getNoticeList()
}
const handleCreate = () => {
@ -201,14 +227,54 @@ const handleCreate = () => {
dialogTitle.value = '新建公告'
dialogType.value = 'create'
}
/**
* @description: 定义tab方法
*/
const handleTabChange = (tab: number) => {
activeTab.value = tab
searchForm.value.status = tab
currentPage.value = 1
getNoticeList()
}
/**
* @description: 定义表格方法
*/
const handleView = (row: any) => {
console.log(row)
dialogVisible.value = true
dialogTitle.value = '查看公告'
dialogType.value = 'view'
noticeForm.value = {
noticeName: row.noticeName,
noticeContent: row.noticeContent,
} as any
}
const handleEdit = (row: any) => {
dialogVisible.value = true
dialogTitle.value = '编辑公告'
dialogType.value = 'edit'
noticeForm.value = {
noticeName: row.noticeName,
noticeContent: row.noticeContent,
publishStartTime: row.startDateTime,
publishEndTime: row.endDateTime,
status: row.status,
id: row.id,
}
}
//
const handleDelete = async (row: any) => {
const params = {
id: row.id,
}
await deleteNoticeApi(params)
getNoticeList()
}
/**
* @description: 定义分页方法
*/
@ -221,24 +287,79 @@ const handlePaginationChange = async (page: { currentPage: number, pageSize: num
* @description: 定义弹窗方法
*/
const handleSubmit = async () => {
await noticeFormRef.value.validate()
if (dialogType.value === 'create') {
noticeForm.value.noticeContent = valueHtml.value
const params = {
name: noticeForm.value.noticeName,
content: noticeForm.value.noticeContent,
status: noticeForm.value.status,
}
await createNoticeApi(params)
}
dialogVisible.value = false
getNoticeList()
}else {
noticeForm.value.noticeContent = valueHtml.value
const params = {
id: noticeForm.value.id,
name: noticeForm.value.noticeName,
content: noticeForm.value.noticeContent,
status: noticeForm.value.status,
}
await editNoticeApi(params)
dialogVisible.value = false
getNoticeList()
}
}
const handleCancel = () => {
dialogVisible.value = false
}
const handleClose = () => {
dialogVisible.value = false
}
/**
* @description: 定义弹窗表格方法
* @description: 定义富文本编辑器方法
*/
//
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
//
const handleCreated = (editor: any) => {
editorRef.value = editor
//
nextTick(() => {
const toolbar = DomEditor.getToolbar(editor)
if (toolbar) {
const curToolbarConfig = toolbar.getConfig()
console.log('curToolbarConfig.toolbarKeys', curToolbarConfig.toolbarKeys)
toolbarRef.value = toolbar
console.log('toolbarRef.value', valueHtml.value)
}
})
}
</script>
<style scoped>
.tab {
display: flex;
justify-content: center;
margin-bottom: 10px;
.el-button {
margin-left: 0px;
}
}
.view-content {
h2{
margin-bottom: 10px;
}
}
</style>

View File

@ -2,7 +2,7 @@
* @description:
*/
export const search = [
{ prop: 'username', label: '公告名称', type: 'input' as const, placeholder: '请输入公告名称', width: '120px' },
{ prop: 'noticeName', label: '公告名称', type: 'input' as const, placeholder: '请输入公告名称', width: '120px' },
{
prop: "startDateTime", label: '发布时间', type: 'date' as const, dateType: 'date' as const, // 时间范围选择器
valueFormat: 'YYYY-MM-DD',
@ -20,8 +20,8 @@ export const search = [
*/
export const tableColumns = [
{ prop: 'noticeName', label: '公告名称'},
{ prop: 'noticeContent', label: '公告内容'},
{ prop: 'startDateTime', label: '发布时间'},
// { prop: 'noticeContent', label: '公告内容'},
{ prop: 'publishTime', label: '发布时间'},
{ slotName: 'status', label: '状态'},
{ slotName: 'action', label: '操作'},
]
@ -32,6 +32,6 @@ export const tableColumns = [
export const formItem = [
{ prop: 'noticeName', label: '公告名称', type: 'input' as const, placeholder: '请输入公告名称'},
// 公告内容
{ slotName: 'noticeContent',label: '公告内容', type: 'edit' as const},
{ slotName: 'noticeContent', label: '公告内容', type: 'editor' as const},
{ prop: 'status', label: '状态', type: 'radio' as const, placeholder: '请选择状态'},
]

View File

@ -1,7 +1,7 @@
import type { FormRules } from 'element-plus'
export const rules = (): FormRules => {
return {
name: [
noticeName: [
{ required: true, message: '请输入名称', trigger: ['blur'] }
],
}