| | |
| | | <!-- 搜索表单 --> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">公告标题:</span> |
| | | <el-input |
| | | v-model="searchForm.noticeTitle" |
| | | style="width: 240px" |
| | | placeholder="请输入公告标题搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <span class="search_title ml10">公告类型:</span> |
| | | <el-select v-model="searchForm.noticeType" clearable @change="handleQuery" style="width: 240px"> |
| | | <el-option label="放假通知" value="1" /> |
| | | <el-option label="设备维修通知" value="2" /> |
| | | </el-select> |
| | | <span class="search_title ml10">状态:</span> |
| | | <el-select v-model="searchForm.status" clearable @change="handleQuery" style="width: 240px"> |
| | | <el-option label="草稿" value="0" /> |
| | | <el-option label="已发布" value="1" /> |
| | | <el-option label="已下线" value="2" /> |
| | | </el-select> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button> |
| | | <el-button @click="resetQuery" style="margin-left: 10px">重置</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">新增公告</el-button> |
| | | <el-button type="danger" plain @click="handleDelete" :disabled="!selectedIds.length">删除</el-button> |
| | | <el-button type="info" @click="openNoticeTypeDialog">公告类型配置</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 通知公告板 --> |
| | | <div class="notice-board"> |
| | | <!-- 放假通知区域 --> |
| | | <div class="notice-section" v-if="holidayNotices.length > 0"> |
| | | <div class="section-header"> |
| | | <h3>📅 放假通知</h3> |
| | | <span class="section-count">{{ holidayNotices.length }}条</span> |
| | | </div> |
| | | <div class="notice-cards"> |
| | | <div |
| | | v-for="notice in holidayNotices" |
| | | :key="notice.id" |
| | | class="notice-card holiday-card" |
| | | :class="{ 'urgent': notice.priority === '3' }" |
| | | > |
| | | <div class="card-header"> |
| | | <div class="card-title"> |
| | | <el-icon class="holiday-icon"><Calendar /></el-icon> |
| | | {{ notice.noticeTitle }} |
| | | </div> |
| | | <div class="card-actions"> |
| | | <el-button link type="primary" @click="handleEdit(notice)">编辑</el-button> |
| | | <el-button link type="danger" @click="handleDelete(notice.id)">删除</el-button> |
| | | <el-tabs v-model="activeNoticeTypeTab" @tab-change="handleNoticeTypeTabChange"> |
| | | <el-tab-pane |
| | | v-for="noticeType in noticeTypeList" |
| | | :key="noticeType.id" |
| | | :label="noticeType.noticeType" |
| | | :name="String(noticeType.id)" |
| | | > |
| | | <template #label> |
| | | <span>{{ noticeType.noticeType }} |
| | | <span class="tab-count" v-if="getNoticeCountByType(noticeType.id) > 0"> |
| | | ({{ getNoticeCountByType(noticeType.id) }}) |
| | | </span> |
| | | </span> |
| | | </template> |
| | | |
| | | <div class="notice-section"> |
| | | <div class="notice-cards"> |
| | | <div |
| | | v-for="notice in getNoticesByType(noticeType.id)" |
| | | :key="notice.id" |
| | | class="notice-card" |
| | | :class="{ 'urgent': notice.priority === '3' }" |
| | | > |
| | | <div class="card-header"> |
| | | <div class="card-title"> |
| | | <el-icon class="notice-icon"> |
| | | <Calendar/> |
| | | </el-icon> |
| | | {{ notice.title }} |
| | | </div> |
| | | <div class="card-actions"> |
| | | <el-button link type="primary" @click="handleEdit(notice)" :disabled="isNoticeExpired(notice)" v-if="notice.status !== 1">编辑</el-button> |
| | | <el-button link type="success" @click="handlePublish(notice)" v-if="notice.status === 0">发布</el-button> |
| | | <el-button link type="danger" @click="handleDelete(notice.id)" v-if="notice.status !== 1">删除</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="card-content"> |
| | | <p>{{ notice.content }}</p> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <div class="card-meta"> |
| | | <span class="priority" :class="'priority-' + notice.priority"> |
| | | {{ getPriorityText(notice.priority) }} |
| | | </span> |
| | | <span class="status" :class="'status-' + getNoticeStatus(notice)"> |
| | | {{ getStatusText(getNoticeStatus(notice)) }} |
| | | </span> |
| | | </div> |
| | | <div class="card-info"> |
| | | <span class="creator">{{ notice.createUserName }}</span> |
| | | <span class="expiration" v-if="notice.expirationDate">截止日期:{{ notice.expirationDate }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="card-remark" v-if="notice.remark"> |
| | | <el-icon> |
| | | <InfoFilled/> |
| | | </el-icon> |
| | | <span>{{ notice.remark }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="card-content"> |
| | | <p>{{ notice.noticeContent }}</p> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <div class="card-meta"> |
| | | <span class="priority" :class="'priority-' + notice.priority"> |
| | | {{ getPriorityText(notice.priority) }} |
| | | </span> |
| | | <span class="status" :class="'status-' + notice.status"> |
| | | {{ getStatusText(notice.status) }} |
| | | </span> |
| | | </div> |
| | | <div class="card-info"> |
| | | <span class="creator">{{ notice.createBy }}</span> |
| | | <span class="time">{{ notice.createTime }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="card-remark" v-if="notice.remark"> |
| | | <el-icon><InfoFilled /></el-icon> |
| | | <span>{{ notice.remark }}</span> |
| | | |
| | | <pagination |
| | | v-if="getNoticePageByType(noticeType.id).total > 0" |
| | | :total="getNoticePageByType(noticeType.id).total" |
| | | :page="getNoticePageByType(noticeType.id).current" |
| | | :limit="getNoticePageByType(noticeType.id).size" |
| | | @pagination="(val) => handleNoticeCurrentChange(noticeType.id, val)" |
| | | /> |
| | | |
| | | <!-- 空状态 --> |
| | | <div class="empty-state" v-if="getNoticesByType(noticeType.id).length === 0"> |
| | | <el-empty description="暂无通知公告"/> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 设备维修通知区域 --> |
| | | <div class="notice-section" v-if="maintenanceNotices.length > 0"> |
| | | <div class="section-header"> |
| | | <h3>🔧 设备维修通知</h3> |
| | | <span class="section-count">{{ maintenanceNotices.length }}条</span> |
| | | </div> |
| | | <div class="notice-cards"> |
| | | <div |
| | | v-for="notice in maintenanceNotices" |
| | | :key="notice.id" |
| | | class="notice-card maintenance-card" |
| | | :class="{ 'urgent': notice.priority === '3' }" |
| | | > |
| | | <div class="card-header"> |
| | | <div class="card-title"> |
| | | <el-icon class="maintenance-icon"><Tools /></el-icon> |
| | | {{ notice.noticeTitle }} |
| | | </div> |
| | | <div class="card-actions"> |
| | | <el-button link type="primary" @click="handleEdit(notice)">编辑</el-button> |
| | | <el-button link type="danger" @click="handleDelete(notice.id)">删除</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="card-content"> |
| | | <p>{{ notice.noticeContent }}</p> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <div class="card-meta"> |
| | | <span class="priority" :class="'priority-' + notice.priority"> |
| | | {{ getPriorityText(notice.priority) }} |
| | | </span> |
| | | <span class="status" :class="'status-' + notice.status"> |
| | | {{ getStatusText(notice.status) }} |
| | | </span> |
| | | </div> |
| | | <div class="card-info"> |
| | | <span class="creator">{{ notice.createBy }}</span> |
| | | <span class="time">{{ notice.createTime }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="card-remark" v-if="notice.remark"> |
| | | <el-icon><InfoFilled /></el-icon> |
| | | <span>{{ notice.remark }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 空状态 --> |
| | | <div class="empty-state" v-if="filteredNotices.length === 0"> |
| | | <el-empty description="暂无通知公告" /> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | |
| | | <!-- 新增/编辑对话框 --> |
| | | <el-dialog |
| | | :title="dialogTitle" |
| | | v-model="dialogVisible" |
| | | width="800px" |
| | | append-to-body |
| | | @close="resetForm" |
| | | <el-dialog |
| | | :title="dialogTitle" |
| | | v-model="dialogVisible" |
| | | width="800px" |
| | | append-to-body |
| | | @close="resetForm" |
| | | > |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="100px"> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="公告标题" prop="noticeTitle"> |
| | | <el-input v-model="form.noticeTitle" placeholder="请输入公告标题" /> |
| | | <el-form-item label="公告标题" prop="title"> |
| | | <el-input v-model="form.title" placeholder="请输入公告标题"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="公告类型" prop="noticeType"> |
| | | <el-select v-model="form.noticeType" placeholder="请选择公告类型" style="width: 100%"> |
| | | <el-option label="放假通知" value="1" /> |
| | | <el-option label="设备维修通知" value="2" /> |
| | | <el-form-item label="公告类型" prop="type"> |
| | | <el-select v-model="form.type" placeholder="请选择公告类型" style="width: 100%"> |
| | | <el-option |
| | | v-for="item in noticeTypeList" |
| | | :key="item.id" |
| | | :label="item.noticeType" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <el-col :span="12"> |
| | | <el-form-item label="状态"> |
| | | <el-radio-group v-model="form.status"> |
| | | <el-radio value="0">草稿</el-radio> |
| | | <el-radio value="1">已发布</el-radio> |
| | | <el-radio value="2">已下线</el-radio> |
| | | <el-radio :value="0">草稿</el-radio> |
| | | <el-radio :value="1">正式发布</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="优先级"> |
| | | <el-select v-model="form.priority" placeholder="请选择优先级" style="width: 100%"> |
| | | <el-option label="普通" value="1" /> |
| | | <el-option label="重要" value="2" /> |
| | | <el-option label="紧急" value="3" /> |
| | | <el-option label="普通" :value="1"/> |
| | | <el-option label="重要" :value="2"/> |
| | | <el-option label="紧急" :value="3"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="过期时间" prop="expirationDate"> |
| | | <el-date-picker v-model="form.expirationDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" |
| | | placeholder="请选择" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="公告内容" prop="noticeContent"> |
| | | <el-form-item label="公告内容" prop="content"> |
| | | <el-input |
| | | v-model="form.noticeContent" |
| | | type="textarea" |
| | | :rows="6" |
| | | placeholder="请输入公告内容" |
| | | maxlength="500" |
| | | show-word-limit |
| | | v-model="form.content" |
| | | type="textarea" |
| | | :rows="6" |
| | | placeholder="请输入公告内容" |
| | | maxlength="500" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入备注信息" |
| | | maxlength="200" |
| | | show-word-limit |
| | | v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入备注信息" |
| | | maxlength="200" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 公告类型配置弹框 --> |
| | | <el-dialog |
| | | v-model="noticeTypeDialogVisible" |
| | | title="公告类型配置" |
| | | width="800px" |
| | | @close="handleNoticeTypeDialogClose" |
| | | > |
| | | <div class="notice-type-container"> |
| | | <div class="notice-type-header"> |
| | | <el-button type="primary" @click="handleAddNoticeType">新增类型</el-button> |
| | | </div> |
| | | <el-table :data="noticeTypeList" border style="width: 100%"> |
| | | <el-table-column prop="id" label="ID" width="80" align="center"/> |
| | | <el-table-column prop="noticeType" label="公告类型" align="center"> |
| | | <template #default="scope"> |
| | | <el-input |
| | | v-if="scope.row.editing" |
| | | v-model="scope.row.noticeType" |
| | | placeholder="请输入公告类型" |
| | | /> |
| | | <span v-else>{{ scope.row.noticeType }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="200" align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | v-if="scope.row.editing" |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="handleSaveNoticeType(scope.row)" |
| | | > |
| | | 保存 |
| | | </el-button> |
| | | <el-button |
| | | v-if="scope.row.editing" |
| | | link |
| | | type="info" |
| | | size="small" |
| | | @click="handleCancelEdit(scope.row)" |
| | | > |
| | | 取消 |
| | | </el-button> |
| | | <el-button |
| | | v-if="!scope.row.editing" |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="handleEditNoticeType(scope.row)" |
| | | > |
| | | 编辑 |
| | | </el-button> |
| | | <el-button |
| | | v-if="!scope.row.editing" |
| | | link |
| | | type="danger" |
| | | size="small" |
| | | @click="handleDeleteNoticeType(scope.row)" |
| | | > |
| | | 删除 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search, Calendar, Tools, InfoFilled } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref, reactive, toRefs, computed } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import {Calendar, InfoFilled} from "@element-plus/icons-vue"; |
| | | import {onMounted, ref, reactive, toRefs, computed} from "vue"; |
| | | import {ElMessage, ElMessageBox} from "element-plus"; |
| | | import {useRoute} from "vue-router"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { |
| | | addNotice, |
| | | delNotice, |
| | | getCount, |
| | | listNotice, |
| | | updateNotice, |
| | | listNoticeType, |
| | | addNoticeType, |
| | | delNoticeType |
| | | } from "../../../api/collaborativeApproval/noticeManagement.js"; |
| | | import pagination from "../../../components/PIMTable/Pagination.vue"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const route = useRoute(); |
| | | |
| | | // 响应式数据 |
| | | const data = reactive({ |
| | | searchForm: { |
| | | noticeTitle: "", |
| | | noticeType: "", |
| | | status: "", |
| | | title: "", |
| | | type: undefined, |
| | | status: undefined, |
| | | }, |
| | | form: { |
| | | id: undefined, |
| | | noticeTitle: "", |
| | | noticeType: "", |
| | | noticeContent: "", |
| | | status: "0", |
| | | priority: "1", |
| | | title: "", |
| | | type: null, |
| | | content: "", |
| | | status: 0, |
| | | priority: 1, |
| | | remark: "", |
| | | createBy: "", |
| | | createTime: "", |
| | | expirationDate: "", |
| | | }, |
| | | rules: { |
| | | noticeTitle: [ |
| | | { required: true, message: "公告标题不能为空", trigger: "blur" } |
| | | title: [ |
| | | {required: true, message: "公告标题不能为空", trigger: "blur"} |
| | | ], |
| | | noticeType: [ |
| | | { required: true, message: "请选择公告类型", trigger: "change" } |
| | | type: [ |
| | | {required: true, message: "请选择公告类型", trigger: "change"} |
| | | ], |
| | | noticeContent: [ |
| | | { required: true, message: "公告内容不能为空", trigger: "blur" } |
| | | content: [ |
| | | {required: true, message: "公告内容不能为空", trigger: "blur"} |
| | | ], |
| | | expirationDate: [ |
| | | {required: true, message: "请选择日期", trigger: "change"} |
| | | ] |
| | | } |
| | | }); |
| | | |
| | | const { searchForm, form, rules } = toRefs(data); |
| | | const {searchForm, form, rules} = toRefs(data); |
| | | |
| | | // 页面状态 |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const selectedIds = ref([]); |
| | | const formRef = ref(); |
| | | |
| | | // 模拟数据 - 根据法定节假日设计 |
| | | const mockData = [ |
| | | { |
| | | id: 1, |
| | | noticeTitle: "2024年春节放假通知", |
| | | noticeType: "1", |
| | | priority: "2", |
| | | status: "1", |
| | | noticeContent: "根据国务院办公厅通知,2024年春节放假安排如下:2月10日(初一)至2月17日(初八)放假调休,共8天。2月4日(星期日)、2月18日(星期日)上班。请各部门提前做好工作安排。", |
| | | remark: "放假期间请保持手机畅通,如有紧急事务及时联系", |
| | | createBy: "人事部", |
| | | createTime: "2024-01-15 10:30:00" |
| | | }, |
| | | { |
| | | id: 2, |
| | | noticeTitle: "2024年清明节放假通知", |
| | | noticeType: "1", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "根据国务院办公厅通知,2024年清明节放假安排如下:4月4日(星期四)至4月6日(星期六)放假调休,共3天。4月7日(星期日)上班。", |
| | | remark: "请各部门做好值班安排,确保节日期间各项工作正常运转", |
| | | createBy: "行政部", |
| | | createTime: "2024-01-14 14:20:00" |
| | | }, |
| | | { |
| | | id: 3, |
| | | noticeTitle: "2024年劳动节放假通知", |
| | | noticeType: "1", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "根据国务院办公厅通知,2024年劳动节放假安排如下:5月1日(星期三)至5月5日(星期日)放假调休,共5天。4月28日(星期日)、5月11日(星期六)上班。", |
| | | remark: "放假前请关闭电源,锁好门窗,注意安全", |
| | | createBy: "行政部", |
| | | createTime: "2024-01-13 09:15:00" |
| | | }, |
| | | { |
| | | id: 4, |
| | | noticeTitle: "2024年端午节放假通知", |
| | | noticeType: "1", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "根据国务院办公厅通知,2024年端午节放假安排如下:6月8日(星期六)至6月10日(星期一)放假调休,共3天。6月11日(星期二)上班。", |
| | | remark: "祝大家端午节快乐,阖家幸福!", |
| | | createBy: "行政部", |
| | | createTime: "2024-01-12 16:30:00" |
| | | }, |
| | | { |
| | | id: 5, |
| | | noticeTitle: "2024年中秋节放假通知", |
| | | noticeType: "1", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "根据国务院办公厅通知,2024年中秋节放假安排如下:9月15日(星期日)至9月17日(星期二)放假调休,共3天。9月14日(星期六)上班。", |
| | | remark: "中秋佳节,祝大家团圆美满,幸福安康!", |
| | | createBy: "行政部", |
| | | createTime: "2024-01-11 11:20:00" |
| | | }, |
| | | { |
| | | id: 6, |
| | | noticeTitle: "2024年国庆节放假通知", |
| | | noticeType: "1", |
| | | priority: "2", |
| | | status: "1", |
| | | noticeContent: "根据国务院办公厅通知,2024年国庆节放假安排如下:10月1日(星期二)至10月7日(星期一)放假调休,共7天。9月29日(星期日)、10月12日(星期六)上班。", |
| | | remark: "国庆期间请各部门做好值班安排,确保安全稳定", |
| | | createBy: "行政部", |
| | | createTime: "2024-01-10 15:45:00" |
| | | }, |
| | | { |
| | | id: 7, |
| | | noticeTitle: "A车间生产线年度检修通知", |
| | | noticeType: "2", |
| | | priority: "2", |
| | | status: "1", |
| | | noticeContent: "A车间生产线将于2024年1月20日(周六)进行年度检修维护,预计停工8小时。检修内容包括:设备清洁、润滑保养、安全装置检查等。请生产部门提前调整生产计划。", |
| | | remark: "维修期间请相关人员配合,确保检修工作安全顺利进行", |
| | | createBy: "设备部", |
| | | createTime: "2024-01-14 14:20:00" |
| | | }, |
| | | { |
| | | id: 8, |
| | | noticeTitle: "B车间设备预防性维护通知", |
| | | noticeType: "2", |
| | | priority: "1", |
| | | status: "1", |
| | | noticeContent: "B车间关键设备将于2024年1月25日进行预防性维护,预计停工4小时。维护内容包括:设备检查、零件更换、性能测试等。请相关部门配合。", |
| | | remark: "维护完成后将进行试运行,确保设备正常运行", |
| | | createBy: "设备部", |
| | | createTime: "2024-01-13 09:15:00" |
| | | } |
| | | ]; |
| | | // 公告类型配置相关 |
| | | const noticeTypeDialogVisible = ref(false); |
| | | const noticeTypeList = ref([]); |
| | | const activeNoticeTypeTab = ref(''); |
| | | |
| | | // 通知数据 - 使用 Map 存储,key 为类型 id |
| | | const noticesMap = ref({}); |
| | | const noticePagesMap = ref({}); |
| | | |
| | | |
| | | // 计算属性 |
| | | const filteredNotices = computed(() => { |
| | | let filtered = [...mockData]; |
| | | |
| | | |
| | | if (searchForm.value.noticeTitle) { |
| | | filtered = filtered.filter(item => |
| | | item.noticeTitle.includes(searchForm.value.noticeTitle) |
| | | filtered = filtered.filter(item => |
| | | item.noticeTitle.includes(searchForm.value.noticeTitle) |
| | | ); |
| | | } |
| | | if (searchForm.value.noticeType) { |
| | | filtered = filtered.filter(item => |
| | | item.noticeType === searchForm.value.noticeType |
| | | filtered = filtered.filter(item => |
| | | item.noticeType === searchForm.value.noticeType |
| | | ); |
| | | } |
| | | if (searchForm.value.status !== "") { |
| | | filtered = filtered.filter(item => |
| | | item.status === searchForm.value.status |
| | | filtered = filtered.filter(item => |
| | | item.status === searchForm.value.status |
| | | ); |
| | | } |
| | | |
| | | |
| | | return filtered; |
| | | }); |
| | | |
| | | const holidayNotices = computed(() => { |
| | | return filteredNotices.value.filter(notice => notice.noticeType === "1"); |
| | | }); |
| | | |
| | | const maintenanceNotices = computed(() => { |
| | | return filteredNotices.value.filter(notice => notice.noticeType === "2"); |
| | | }); |
| | | |
| | | // 方法定义 |
| | |
| | | |
| | | const resetQuery = () => { |
| | | searchForm.value = { |
| | | noticeTitle: "", |
| | | noticeType: "", |
| | | title: "", |
| | | type: "", |
| | | status: "" |
| | | }; |
| | | }; |
| | | |
| | | const getPriorityText = (priority) => { |
| | | const priorityMap = { "1": "普通", "2": "重要", "3": "紧急" }; |
| | | const priorityMap = {"1": "普通", "2": "重要", "3": "紧急"}; |
| | | return priorityMap[priority] || "普通"; |
| | | }; |
| | | |
| | | const getStatusText = (status) => { |
| | | const statusMap = { "0": "草稿", "1": "已发布", "2": "已下线" }; |
| | | const statusMap = {"0": "草稿", "1": "已发布", "2": "已过期"}; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | const isNoticeExpired = (notice) => { |
| | | if (!notice || !notice.expirationDate) { |
| | | return false; |
| | | } |
| | | |
| | | const expiration = new Date(notice.expirationDate); |
| | | |
| | | if (Number.isNaN(expiration.getTime())) { |
| | | return false; |
| | | } |
| | | |
| | | expiration.setHours(23, 59, 59, 999); |
| | | |
| | | return new Date() > expiration; |
| | | }; |
| | | |
| | | const getNoticeStatus = (notice) => { |
| | | const normalizedStatus = notice && notice.status !== undefined && notice.status !== null |
| | | ? String(notice.status) |
| | | : "0"; |
| | | |
| | | return isNoticeExpired(notice) ? "2" : normalizedStatus; |
| | | }; |
| | | |
| | | const openForm = (type) => { |
| | |
| | | dialogTitle.value = "新增公告"; |
| | | form.value = { |
| | | id: undefined, |
| | | noticeTitle: "", |
| | | noticeType: "", |
| | | noticeContent: "", |
| | | status: "0", |
| | | priority: "1", |
| | | title: "", |
| | | type: undefined, |
| | | content: "", |
| | | status: 0, |
| | | priority: 1, |
| | | remark: "", |
| | | createBy: userStore.name || "当前用户", |
| | | createTime: new Date().toLocaleString() |
| | | expirationDate: "", |
| | | }; |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleEdit = (row) => { |
| | | if (isNoticeExpired(row)) { |
| | | ElMessage.warning("已过期的公告不可编辑"); |
| | | return; |
| | | } |
| | | dialogTitle.value = "编辑公告"; |
| | | form.value = { ...row }; |
| | | form.value = {...row}; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | selectedIds.value = selection.map(item => item.id); |
| | | }; |
| | | |
| | | const handleDelete = (id) => { |
| | | ElMessageBox.confirm( |
| | | "确认删除这条公告吗?", |
| | | "提示", |
| | | { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning" |
| | | } |
| | | "确认删除这条公告吗?", |
| | | "提示", |
| | | { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning" |
| | | } |
| | | ).then(() => { |
| | | const index = mockData.findIndex(item => item.id === id); |
| | | if (index > -1) { |
| | | mockData.splice(index, 1); |
| | | delNotice(id).then(res => { |
| | | ElMessage.success("删除成功"); |
| | | } |
| | | resetTable() |
| | | }) |
| | | }); |
| | | }; |
| | | |
| | | const handlePublish = (notice) => { |
| | | ElMessageBox.confirm( |
| | | "确认发布这条公告吗?", |
| | | "提示", |
| | | { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "info" |
| | | } |
| | | ).then(() => { |
| | | updateNotice({ |
| | | ...notice, |
| | | status: 1 |
| | | }).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("发布成功"); |
| | | resetTable() |
| | | } |
| | | }) |
| | | }); |
| | | }; |
| | | |
| | |
| | | if (valid) { |
| | | if (form.value.id) { |
| | | // 编辑模式 |
| | | const index = mockData.findIndex(item => item.id === form.value.id); |
| | | if (index > -1) { |
| | | mockData[index] = { ...form.value }; |
| | | } |
| | | ElMessage.success("修改成功"); |
| | | updateNotice(form.value).then(res => { |
| | | ElMessage.success("修改成功"); |
| | | resetTable() |
| | | }) |
| | | } else { |
| | | // 新增模式 |
| | | const newId = Math.max(...mockData.map(item => item.id)) + 1; |
| | | const newNotice = { |
| | | ...form.value, |
| | | id: newId, |
| | | createTime: new Date().toLocaleString() |
| | | }; |
| | | mockData.unshift(newNotice); |
| | | ElMessage.success("新增成功"); |
| | | addNotice(form.value).then(res => { |
| | | ElMessage.success("新增成功"); |
| | | resetTable() |
| | | }) |
| | | } |
| | | dialogVisible.value = false; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 初始化某个类型的分页数据 |
| | | const initNoticePage = (typeId) => { |
| | | if (!noticePagesMap.value[typeId]) { |
| | | noticePagesMap.value[typeId] = { |
| | | total: 0, |
| | | current: 1, |
| | | size: 10 |
| | | }; |
| | | } |
| | | if (!noticesMap.value[typeId]) { |
| | | noticesMap.value[typeId] = []; |
| | | } |
| | | }; |
| | | |
| | | // 获取某个类型的通知列表 |
| | | const getNoticesByType = (typeId) => { |
| | | return noticesMap.value[typeId] || []; |
| | | }; |
| | | |
| | | // 获取某个类型的分页数据 |
| | | const getNoticePageByType = (typeId) => { |
| | | return noticePagesMap.value[typeId] || { total: 0, current: 1, size: 10 }; |
| | | }; |
| | | |
| | | // 获取某个类型的数量 |
| | | const getNoticeCountByType = (typeId) => { |
| | | return getNoticePageByType(typeId).total || 0; |
| | | }; |
| | | |
| | | // 获取某个类型的通知数据 |
| | | const fetchNoticesByType = (typeId) => { |
| | | initNoticePage(typeId); |
| | | const pageData = noticePagesMap.value[typeId]; |
| | | listNotice({...pageData, type: typeId}).then(res => { |
| | | if (res.code === 200) { |
| | | noticesMap.value[typeId] = res.data.records || []; |
| | | noticePagesMap.value[typeId].total = res.data.total || 0; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 处理分页变化 |
| | | const handleNoticeCurrentChange = (typeId, val) => { |
| | | initNoticePage(typeId); |
| | | noticePagesMap.value[typeId].size = val.limit; |
| | | noticePagesMap.value[typeId].current = val.page; |
| | | fetchNoticesByType(typeId); |
| | | }; |
| | | |
| | | // 处理 tab 切换 |
| | | const handleNoticeTypeTabChange = (tabName) => { |
| | | activeNoticeTypeTab.value = tabName; |
| | | const typeId = Number(tabName); |
| | | fetchNoticesByType(typeId); |
| | | }; |
| | | |
| | | const resetTable = () => { |
| | | // 重置所有类型的分页并重新获取数据 |
| | | noticeTypeList.value.forEach(type => { |
| | | initNoticePage(type.id); |
| | | noticePagesMap.value[type.id].current = 1; |
| | | noticePagesMap.value[type.id].size = 10; |
| | | fetchNoticesByType(type.id); |
| | | }); |
| | | }; |
| | | |
| | |
| | | formRef.value?.resetFields(); |
| | | }; |
| | | |
| | | // 公告类型配置相关方法 |
| | | const openNoticeTypeDialog = () => { |
| | | noticeTypeDialogVisible.value = true; |
| | | fetchNoticeTypeList(); |
| | | }; |
| | | |
| | | const fetchNoticeTypeList = () => { |
| | | return listNoticeType().then(res => { |
| | | if (res.code === 200) { |
| | | noticeTypeList.value = res.data.map(item => ({ |
| | | ...item, |
| | | editing: false |
| | | })); |
| | | |
| | | // 检查路由参数中的 type |
| | | const routeType = route.query.type; |
| | | let targetTypeId = null; |
| | | |
| | | if (routeType) { |
| | | // 如果路由参数中有 type,查找对应的类型 |
| | | const typeId = Number(routeType); |
| | | const foundType = noticeTypeList.value.find(item => item.id === typeId); |
| | | if (foundType) { |
| | | targetTypeId = typeId; |
| | | } |
| | | } |
| | | |
| | | // 如果有类型数据 |
| | | if (noticeTypeList.value.length > 0) { |
| | | // 如果路由参数指定了类型且存在,使用路由参数的类型 |
| | | // 否则如果没有选中 tab,默认选中第一个 |
| | | if (targetTypeId !== null) { |
| | | activeNoticeTypeTab.value = String(targetTypeId); |
| | | fetchNoticesByType(targetTypeId); |
| | | } else if (!activeNoticeTypeTab.value) { |
| | | activeNoticeTypeTab.value = String(noticeTypeList.value[0].id); |
| | | fetchNoticesByType(noticeTypeList.value[0].id); |
| | | } |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleAddNoticeType = () => { |
| | | const newItem = { |
| | | id: undefined, |
| | | noticeType: '', |
| | | editing: true |
| | | }; |
| | | noticeTypeList.value.push(newItem); |
| | | }; |
| | | |
| | | const handleEditNoticeType = (row) => { |
| | | // 保存原始值 |
| | | row.originalNoticeType = row.noticeType; |
| | | row.editing = true; |
| | | }; |
| | | |
| | | const handleSaveNoticeType = (row) => { |
| | | if (!row.noticeType || row.noticeType.trim() === '') { |
| | | ElMessage.warning('公告类型不能为空'); |
| | | return; |
| | | } |
| | | |
| | | const data = { |
| | | noticeType: row.noticeType.trim() |
| | | }; |
| | | |
| | | if (row.id) { |
| | | // 编辑模式 - 先删除再添加(因为只有 add 和 del 接口) |
| | | delNoticeType(row.id).then(res => { |
| | | if (res.code === 200) { |
| | | addNoticeType(data).then(addRes => { |
| | | if (addRes.code === 200) { |
| | | ElMessage.success('编辑成功'); |
| | | row.editing = false; |
| | | delete row.originalNoticeType; |
| | | fetchNoticeTypeList().then(() => { |
| | | // 如果当前选中的类型被编辑,需要重新获取数据 |
| | | if (activeNoticeTypeTab.value === String(row.id)) { |
| | | fetchNoticesByType(addRes.data?.id || row.id); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } else { |
| | | // 新增模式 |
| | | addNoticeType(data).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success('新增成功'); |
| | | row.editing = false; |
| | | fetchNoticeTypeList(); |
| | | } |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const handleDeleteNoticeType = (row) => { |
| | | // 如果没有id,说明是新增但未保存的行,直接从前端删除 |
| | | if (!row.id) { |
| | | const index = noticeTypeList.value.indexOf(row); |
| | | if (index > -1) { |
| | | noticeTypeList.value.splice(index, 1); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | // 如果有id,调用后端接口删除 |
| | | ElMessageBox.confirm( |
| | | "确认删除这个公告类型吗?", |
| | | "提示", |
| | | { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning" |
| | | } |
| | | ).then(() => { |
| | | delNoticeType(row.id).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("删除成功"); |
| | | // 如果删除的是当前选中的类型,切换到第一个类型 |
| | | if (activeNoticeTypeTab.value === String(row.id)) { |
| | | fetchNoticeTypeList().then(() => { |
| | | if (noticeTypeList.value.length > 0) { |
| | | activeNoticeTypeTab.value = String(noticeTypeList.value[0].id); |
| | | fetchNoticesByType(noticeTypeList.value[0].id); |
| | | } else { |
| | | activeNoticeTypeTab.value = ''; |
| | | } |
| | | }); |
| | | } else { |
| | | fetchNoticeTypeList(); |
| | | } |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const handleCancelEdit = (row) => { |
| | | if (!row.id) { |
| | | // 如果是新增但未保存的行,移除它 |
| | | const index = noticeTypeList.value.indexOf(row); |
| | | if (index > -1) { |
| | | noticeTypeList.value.splice(index, 1); |
| | | } |
| | | } else { |
| | | // 如果是编辑中的行,取消编辑状态并恢复原值 |
| | | row.editing = false; |
| | | if (row.originalNoticeType !== undefined) { |
| | | row.noticeType = row.originalNoticeType; |
| | | delete row.originalNoticeType; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleNoticeTypeDialogClose = () => { |
| | | // 关闭弹框时,取消所有编辑状态 |
| | | noticeTypeList.value.forEach(item => { |
| | | if (item.editing && !item.id) { |
| | | // 如果是新增但未保存的行,移除它 |
| | | const index = noticeTypeList.value.indexOf(item); |
| | | if (index > -1) { |
| | | noticeTypeList.value.splice(index, 1); |
| | | } |
| | | } else if (item.editing) { |
| | | // 如果是编辑中的行,取消编辑状态并恢复原值 |
| | | item.editing = false; |
| | | if (item.originalNoticeType !== undefined) { |
| | | item.noticeType = item.originalNoticeType; |
| | | delete item.originalNoticeType; |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 生命周期 |
| | | onMounted(() => { |
| | | // 页面加载完成 |
| | | // 先获取公告类型列表,然后根据类型获取通知数据 |
| | | fetchNoticeTypeList(); |
| | | }); |
| | | </script> |
| | | |
| | |
| | | box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .holiday-card { |
| | | border-left-color: #67c23a; |
| | | .notice-icon { |
| | | color: #409eff; |
| | | margin-right: 8px; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .maintenance-card { |
| | | border-left-color: #e6a23c; |
| | | .tab-count { |
| | | color: #909399; |
| | | font-size: 12px; |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | .urgent { |
| | |
| | | flex: 1; |
| | | } |
| | | |
| | | .holiday-icon { |
| | | color: #67c23a; |
| | | margin-right: 8px; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .maintenance-icon { |
| | | color: #e6a23c; |
| | | margin-right: 8px; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .card-actions { |
| | | display: flex; |
| | |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .priority-1 { background: #f0f9ff; color: #0369a1; } |
| | | .priority-2 { background: #fef3c7; color: #d97706; } |
| | | .priority-3 { background: #fef2f2; color: #dc2626; } |
| | | .priority-1 { |
| | | background: #f0f9ff; |
| | | color: #0369a1; |
| | | } |
| | | |
| | | .status-0 { background: #f3f4f6; color: #6b7280; } |
| | | .status-1 { background: #d1fae5; color: #059669; } |
| | | .status-2 { background: #fef3c7; color: #d97706; } |
| | | .priority-2 { |
| | | background: #fef3c7; |
| | | color: #d97706; |
| | | } |
| | | |
| | | .priority-3 { |
| | | background: #fef2f2; |
| | | color: #dc2626; |
| | | } |
| | | |
| | | .status-0 { |
| | | background: #f3f4f6; |
| | | color: #6b7280; |
| | | } |
| | | |
| | | .status-1 { |
| | | background: #d1fae5; |
| | | color: #059669; |
| | | } |
| | | |
| | | .status-2 { |
| | | background: #fef3c7; |
| | | color: #d97706; |
| | | } |
| | | |
| | | .card-info { |
| | | display: flex; |
| | |
| | | .creator { |
| | | font-weight: 500; |
| | | margin-bottom: 2px; |
| | | } |
| | | |
| | | .expiration { |
| | | margin-top: 2px; |
| | | } |
| | | |
| | | .card-remark { |
| | |
| | | text-align: right; |
| | | } |
| | | |
| | | .notice-type-container { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .notice-type-header { |
| | | margin-bottom: 15px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | /* 响应式设计 */ |
| | | @media (max-width: 768px) { |
| | | .notice-cards { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | |
| | | |
| | | .search_form { |
| | | flex-direction: column; |
| | | gap: 15px; |
| | | } |
| | | |
| | | |
| | | .search_form > div { |
| | | width: 100%; |
| | | } |