This commit is contained in:
parent
2a83395858
commit
c9f943f8dd
|
|
@ -6,5 +6,13 @@ export const getNoticeListApi = (params: any) => {
|
||||||
}
|
}
|
||||||
//创建公告
|
//创建公告
|
||||||
export const createNoticeApi = (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})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 // 允许重试(网络波动时自动重试)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -7,24 +7,34 @@
|
||||||
<el-button type="primary" @click="handleCreate">新建</el-button>
|
<el-button type="primary" @click="handleCreate">新建</el-button>
|
||||||
</template>
|
</template>
|
||||||
</NoticeSearch>
|
</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">
|
<NoticeTable :table-data="noticeTree" :columns="noticeTableColumnsOptions" row-key="id">
|
||||||
<template #status="{ row }">
|
<template #status="{ row }">
|
||||||
{{ row.status === 1 ? '已发布' : '未发布' }}
|
<!-- 修正状态显示:1=草稿,2=已发布 -->
|
||||||
|
{{ row.status === 1 ? '草稿' : '已发布' }}
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ row }">
|
<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>
|
</template>
|
||||||
</NoticeTable>
|
</NoticeTable>
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<NoticePagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
|
<NoticePagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
|
||||||
@pagination-change="handlePaginationChange" />
|
@pagination-change="handlePaginationChange" />
|
||||||
<!-- dialog -->
|
<!-- 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">
|
@cancel="handleCancel" @close="handleClose">
|
||||||
<NoticeForm :form-data="noticeForm" :form-rules="noticeRules" :form-items="noticeFormItem"
|
<NoticeForm :form-data="noticeForm" :form-rules="noticeRules" :form-items="noticeFormItem"
|
||||||
:options-map="noticeFormOptionsMap" ref="noticeFormRef" v-if="dialogType !== 'view'">
|
:options-map="noticeFormOptionsMap" ref="noticeFormRef" v-if="dialogType !== 'view'">
|
||||||
<template #noticeContent="{ row }">
|
<template #noticeContent="{ formData }">
|
||||||
<div style="border: 1px solid #e6e6e6; margin: 10px 0;">
|
<div style="border: 1px solid #e6e6e6; margin: 10px 0;">
|
||||||
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" mode="default"
|
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" mode="default"
|
||||||
style="border-bottom: 1px solid #ccc;" />
|
style="border-bottom: 1px solid #ccc;" />
|
||||||
|
|
@ -33,7 +43,10 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</NoticeForm>
|
</NoticeForm>
|
||||||
<div v-html="valueHtml"></div>
|
<div v-else class="view-content">
|
||||||
|
<h2>公告名称:{{ noticeForm.noticeName }}</h2>
|
||||||
|
<div v-html="noticeForm.noticeContent"></div>
|
||||||
|
</div>
|
||||||
</NoticeDialog>
|
</NoticeDialog>
|
||||||
<!-- dialog表格 -->
|
<!-- dialog表格 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -51,45 +64,55 @@ import { search, tableColumns, formItem } from './options'
|
||||||
import { rules } from './rules'
|
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 { DomEditor } from '@wangeditor/editor'
|
||||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||||
import '@wangeditor/editor/dist/css/style.css'
|
import '@wangeditor/editor/dist/css/style.css'
|
||||||
|
|
||||||
// 编辑器实例
|
// 富文本编辑器实例
|
||||||
const editorRef = shallowRef()
|
const editorRef = shallowRef()
|
||||||
// 工具栏实例(通过 DomEditor 获取)
|
// 工具栏实例(通过 DomEditor 获取)
|
||||||
const toolbarRef = shallowRef()
|
const toolbarRef = shallowRef()
|
||||||
// 内容 HTML
|
// 富文本编辑器内容 HTML
|
||||||
const valueHtml = ref('')
|
const valueHtml = ref('')
|
||||||
|
// 工具栏配置
|
||||||
const toolbarConfig = {
|
const toolbarConfig = {
|
||||||
excludeKeys: ["group-video"]
|
excludeKeys: ["group-video", "fullScreen"]
|
||||||
}
|
}
|
||||||
const editorConfig = { placeholder: '请输入内容...' }
|
// 富文本编辑器配置
|
||||||
|
const editorConfig = {
|
||||||
// 组件销毁时销毁编辑器
|
placeholder: '请输入内容...',
|
||||||
onBeforeUnmount(() => {
|
MENU_CONF: {
|
||||||
const editor = editorRef.value
|
uploadImage: {
|
||||||
if (editor == null) return
|
customUpload: async (file: any, insertFn: any) => {
|
||||||
editor.destroy()
|
try {
|
||||||
})
|
// 调用封装后的上传API
|
||||||
|
const res = await uploadImageApi(file);
|
||||||
// 编辑器创建完成后回调
|
// 插入图片到富文本
|
||||||
const handleCreated = (editor) => {
|
insertFn(res.url);
|
||||||
editorRef.value = editor
|
} catch (error: any) {
|
||||||
|
// 复用你已有的错误提示逻辑(ElNotification)
|
||||||
// ✅ 在这里获取工具栏实例(必须等编辑器和工具栏都挂载后)
|
const errMsg = error?.message || error?.data?.msg || '图片上传失败';
|
||||||
nextTick(() => {
|
alert(`图片${file.name}上传失败:${errMsg}`);
|
||||||
const toolbar = DomEditor.getToolbar(editor)
|
ElNotification({
|
||||||
if (toolbar) {
|
message: errMsg || '未知的错误!',
|
||||||
const curToolbarConfig = toolbar.getConfig()
|
type: 'error',
|
||||||
console.log('curToolbarConfig.toolbarKeys', curToolbarConfig.toolbarKeys)
|
duration: 2000,
|
||||||
toolbarRef.value = toolbar
|
customClass: 'global-notification'
|
||||||
console.log('toolbarRef.value', valueHtml.value)
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 保留基础配置
|
||||||
|
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: '',
|
noticeName: '',
|
||||||
startDateTime: '',
|
startDateTime: '',
|
||||||
endDateTime: '',
|
endDateTime: '',
|
||||||
status: 1,
|
status: 2,
|
||||||
})
|
})
|
||||||
const searchOptions = ref(search)
|
const searchOptions = ref(search)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 定义tab数据
|
||||||
|
*/
|
||||||
|
const activeTab = ref(2)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 定义表格数据
|
* @description: 定义表格数据
|
||||||
*/
|
*/
|
||||||
|
|
@ -131,20 +159,18 @@ const noticeForm = ref({
|
||||||
publishStartTime: '',
|
publishStartTime: '',
|
||||||
publishEndTime: '',
|
publishEndTime: '',
|
||||||
status: 1,
|
status: 1,
|
||||||
|
id: 0,
|
||||||
})
|
})
|
||||||
const noticeFormRef = ref()
|
const noticeFormRef = ref()
|
||||||
const noticeRules = ref(rules())
|
const noticeRules = ref(rules())
|
||||||
const noticeFormItem = ref(formItem)
|
const noticeFormItem = ref(formItem)
|
||||||
const noticeFormOptionsMap = ref({
|
const noticeFormOptionsMap = ref({
|
||||||
status: [
|
status: [
|
||||||
{ label: '发布', value: 1 },
|
{ label: '发布', value: 2 },
|
||||||
{ label: '草稿', value: 2 },
|
{ label: '草稿', value: 1 },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* @description: 定义公共数据处理方法
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* @description: 定义请求数据方法
|
* @description: 定义请求数据方法
|
||||||
*/
|
*/
|
||||||
|
|
@ -161,17 +187,16 @@ const getNoticeList = async () => {
|
||||||
const data: any = await getNoticeListApi(params)
|
const data: any = await getNoticeListApi(params)
|
||||||
// 表格数据
|
// 表格数据
|
||||||
noticeTree.value = data.data.map((item: any) => ({
|
noticeTree.value = data.data.map((item: any) => ({
|
||||||
|
id: item.id,
|
||||||
noticeName: item.name,
|
noticeName: item.name,
|
||||||
noticeContent: item.content,
|
noticeContent: item.content,
|
||||||
startDateTime: item.publish_start_time,
|
publishTime: item.publish_time,
|
||||||
status: item.status,
|
status: item.status,
|
||||||
}))
|
}))
|
||||||
// 分页数据
|
// 分页数据
|
||||||
total.value = data.total
|
total.value = data.total
|
||||||
currentPage.value = data.current_page
|
currentPage.value = data.current_page
|
||||||
pageSize.value = Number(data.per_page)
|
pageSize.value = Number(data.per_page)
|
||||||
|
|
||||||
console.log(noticeTree.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -192,8 +217,9 @@ const handleReset = () => {
|
||||||
noticeName: '',
|
noticeName: '',
|
||||||
startDateTime: '',
|
startDateTime: '',
|
||||||
endDateTime: '',
|
endDateTime: '',
|
||||||
status: 1,
|
status: 2,
|
||||||
}
|
}
|
||||||
|
activeTab.value = 2
|
||||||
getNoticeList()
|
getNoticeList()
|
||||||
}
|
}
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
|
|
@ -201,14 +227,54 @@ const handleCreate = () => {
|
||||||
dialogTitle.value = '新建公告'
|
dialogTitle.value = '新建公告'
|
||||||
dialogType.value = 'create'
|
dialogType.value = 'create'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 定义tab方法
|
||||||
|
*/
|
||||||
|
const handleTabChange = (tab: number) => {
|
||||||
|
activeTab.value = tab
|
||||||
|
searchForm.value.status = tab
|
||||||
|
currentPage.value = 1
|
||||||
|
getNoticeList()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 定义表格方法
|
* @description: 定义表格方法
|
||||||
*/
|
*/
|
||||||
const handleView = (row: any) => {
|
const handleView = (row: any) => {
|
||||||
|
console.log(row)
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
dialogTitle.value = '查看公告'
|
dialogTitle.value = '查看公告'
|
||||||
dialogType.value = 'view'
|
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: 定义分页方法
|
* @description: 定义分页方法
|
||||||
*/
|
*/
|
||||||
|
|
@ -221,24 +287,79 @@ const handlePaginationChange = async (page: { currentPage: number, pageSize: num
|
||||||
* @description: 定义弹窗方法
|
* @description: 定义弹窗方法
|
||||||
*/
|
*/
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
await noticeFormRef.value.validate()
|
||||||
if (dialogType.value === 'create') {
|
if (dialogType.value === 'create') {
|
||||||
|
noticeForm.value.noticeContent = valueHtml.value
|
||||||
const params = {
|
const params = {
|
||||||
name: noticeForm.value.noticeName,
|
name: noticeForm.value.noticeName,
|
||||||
content: noticeForm.value.noticeContent,
|
content: noticeForm.value.noticeContent,
|
||||||
status: noticeForm.value.status,
|
status: noticeForm.value.status,
|
||||||
}
|
}
|
||||||
await createNoticeApi(params)
|
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()
|
||||||
}
|
}
|
||||||
dialogVisible.value = false
|
|
||||||
getNoticeList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
}
|
}
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 定义弹窗表格方法
|
* @description: 定义富文本编辑器方法
|
||||||
*/
|
*/
|
||||||
</script>
|
// 组件销毁时销毁编辑器
|
||||||
|
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>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
* @description: 搜索配置
|
* @description: 搜索配置
|
||||||
*/
|
*/
|
||||||
export const search = [
|
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, // 时间范围选择器
|
prop: "startDateTime", label: '发布时间', type: 'date' as const, dateType: 'date' as const, // 时间范围选择器
|
||||||
valueFormat: 'YYYY-MM-DD',
|
valueFormat: 'YYYY-MM-DD',
|
||||||
|
|
@ -20,8 +20,8 @@ export const search = [
|
||||||
*/
|
*/
|
||||||
export const tableColumns = [
|
export const tableColumns = [
|
||||||
{ prop: 'noticeName', label: '公告名称'},
|
{ prop: 'noticeName', label: '公告名称'},
|
||||||
{ prop: 'noticeContent', label: '公告内容'},
|
// { prop: 'noticeContent', label: '公告内容'},
|
||||||
{ prop: 'startDateTime', label: '发布时间'},
|
{ prop: 'publishTime', label: '发布时间'},
|
||||||
{ slotName: 'status', label: '状态'},
|
{ slotName: 'status', label: '状态'},
|
||||||
{ slotName: 'action', label: '操作'},
|
{ slotName: 'action', label: '操作'},
|
||||||
]
|
]
|
||||||
|
|
@ -32,6 +32,6 @@ export const tableColumns = [
|
||||||
export const formItem = [
|
export const formItem = [
|
||||||
{ prop: 'noticeName', label: '公告名称', type: 'input' as const, placeholder: '请输入公告名称'},
|
{ 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: '请选择状态'},
|
{ prop: 'status', label: '状态', type: 'radio' as const, placeholder: '请选择状态'},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { FormRules } from 'element-plus'
|
import type { FormRules } from 'element-plus'
|
||||||
export const rules = (): FormRules => {
|
export const rules = (): FormRules => {
|
||||||
return {
|
return {
|
||||||
name: [
|
noticeName: [
|
||||||
{ required: true, message: '请输入名称', trigger: ['blur'] }
|
{ required: true, message: '请输入名称', trigger: ['blur'] }
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue