foreign-admin-ui/src/views/notice/index.vue

369 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<!-- 搜索 -->
<NoticeSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch"
@reset="handleReset">
<template #button>
<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 }">
<!-- 修正状态显示1=草稿2=已发布 -->
{{ row.status === 1 ? '草稿' : '已发布' }}
</template>
<template #action="{ row }">
<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"
: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="{ formData }">
<div style="border: 1px solid #e6e6e6; margin: 10px 0;">
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" mode="default"
style="border-bottom: 1px solid #ccc;" />
<Editor v-model="valueHtml" :defaultConfig="editorConfig" mode="default"
@onCreated="handleCreated" style="height: 300px; overflow-y: auto;" />
</div>
</template>
</NoticeForm>
<div v-else class="view-content">
<h2>公告名称:{{ noticeForm.noticeName }}</h2>
<div v-html="noticeForm.noticeContent"></div>
</div>
</NoticeDialog>
<!-- dialog表格 -->
</div>
</template>
<script setup lang="ts">
// 组件
import NoticeSearch from '@/components/common/Search.vue'
import NoticeTable from '@/components/common/Table.vue'
import NoticePagination from '@/components/common/Pagination.vue'
import NoticeDialog from '@/components/common/Dialog.vue'
import NoticeForm from '@/components/common/Form.vue'
// 配置以及表单验证
import { search, tableColumns, formItem } from './options'
import { rules } from './rules'
// 接口
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
const valueHtml = ref('')
// 工具栏配置
const toolbarConfig = {
excludeKeys: ["group-video", "fullScreen"]
}
// 富文本编辑器配置
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'],
}
}
}
/**
* @description: 定义搜索数据
*/
const searchForm = ref({
noticeName: '',
startDateTime: '',
endDateTime: '',
status: 2,
})
const searchOptions = ref(search)
/**
* @description: 定义tab数据
*/
const activeTab = ref(2)
/**
* @description: 定义表格数据
*/
const noticeTree = ref([])
const noticeTableColumnsOptions = ref(tableColumns)
/**
* @description: 定义分页数据
*/
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(10)
/**
* @description: 定义弹窗数据
*/
const dialogVisible = ref(false)
const dialogTitle = ref('')
const dialogType = ref('')
/**
* @description: 定义弹窗表格数据
*/
const noticeForm = ref({
noticeName: '',
noticeContent: '',
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 },
],
})
/**
* @description: 定义请求数据方法
*/
//获取公告列表
const getNoticeList = async () => {
const params = {
page: currentPage.value,
page_size: pageSize.value,
name: searchForm.value.noticeName,
status: searchForm.value.status,
publish_start_time: searchForm.value.startDateTime,
publish_end_time: searchForm.value.endDateTime,
}
const data: any = await getNoticeListApi(params)
// 表格数据
noticeTree.value = data.data.map((item: any) => ({
id: item.id,
noticeName: item.name,
noticeContent: item.content,
publishTime: item.publish_time,
status: item.status,
}))
// 分页数据
total.value = data.total
currentPage.value = data.current_page
pageSize.value = Number(data.per_page)
}
/**
* @description: 页面加载完成需要请求的数据
*/
onMounted(() => {
getNoticeList()
})
/**
* @description: 定义搜索方法
*/
const handleSearch = () => {
getNoticeList()
}
const handleReset = () => {
searchForm.value = {
noticeName: '',
startDateTime: '',
endDateTime: '',
status: 2,
}
activeTab.value = 2
getNoticeList()
}
const handleCreate = () => {
dialogVisible.value = true
dialogTitle.value = '新建公告'
dialogType.value = 'create'
noticeForm.value = {
noticeName: '',
noticeContent: '',
status: 1,
id: 0,
} as any
}
/**
* @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,
status: row.status,
id: row.id,
} as any
valueHtml.value = row.noticeContent
}
// 删除公告
const handleDelete = async (row: any) => {
const params = {
id: row.id,
}
await deleteNoticeApi(params)
getNoticeList()
}
/**
* @description: 定义分页方法
*/
const handlePaginationChange = async (page: { currentPage: number, pageSize: number }) => {
currentPage.value = page.currentPage
pageSize.value = page.pageSize
await getNoticeList()
}
/**
* @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: 定义富文本编辑器方法
*/
// 组件销毁时销毁编辑器
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>