| | |
| | | <template> |
| | | <view class="notice-page"> |
| | | <PageHeader title="通知公告" @back="goBack" /> |
| | | <PageHeader title="通知公告" |
| | | @back="goBack" /> |
| | | <!-- 搜索表单 --> |
| | | <!-- <view class="search_form"> |
| | | <up-button type="primary" size="small" @click="openForm('add')">新增公告</up-button> |
| | |
| | | 删除 |
| | | </up-button> |
| | | </view> --> |
| | | |
| | | <!-- 通知公告板 --> |
| | | <view class="notice-board"> |
| | | <!-- 统一通知区域 --> |
| | | <view class="notice-section" v-if="totalNoticeCount > 0"> |
| | | <view class="notice-section" |
| | | v-if="totalNoticeCount > 0"> |
| | | <view class="section-header"> |
| | | <h3>� 通知公告</h3> |
| | | <text class="section-count">{{ totalNoticeCount }}条</text> |
| | | </view> |
| | | <view class="notice-cards"> |
| | | <!-- 放假通知 --> |
| | | <view |
| | | v-for="notice in holidayNotices" |
| | | :key="'holiday-' + notice.id" |
| | | class="notice-card holiday-card" |
| | | :class="{ 'urgent': notice.priority === '3' }" |
| | | > |
| | | <view v-for="notice in holidayNotices" |
| | | :key="'holiday-' + notice.id" |
| | | class="notice-card holiday-card" |
| | | :class="{ 'urgent': notice.priority === '3' }"> |
| | | <view class="card-header"> |
| | | <view class="card-title"> |
| | | <view class="holiday-icon"> |
| | | <up-icon name="calendar" size="18" color="#67c23a" /> |
| | | <up-icon name="calendar" |
| | | size="18" |
| | | color="#67c23a" /> |
| | | </view> |
| | | <text>{{ notice.title }}</text> |
| | | </view> |
| | | <!-- <view class="card-actions">--> |
| | | <!-- <up-button--> |
| | | <!-- text--> |
| | | <!-- type="primary"--> |
| | | <!-- size="mini"--> |
| | | <!-- @click="handleEdit(notice)"--> |
| | | <!-- :disabled="isNoticeExpired(notice)"--> |
| | | <!-- >--> |
| | | <!-- 编辑--> |
| | | <!-- </up-button>--> |
| | | <!-- <up-button--> |
| | | <!-- text--> |
| | | <!-- type="error"--> |
| | | <!-- size="mini"--> |
| | | <!-- @click="handleDelete(notice.id)"--> |
| | | <!-- >--> |
| | | <!-- 删除--> |
| | | <!-- </up-button>--> |
| | | <!-- </view>--> |
| | | <!-- <view class="card-actions">--> |
| | | <!-- <up-button--> |
| | | <!-- text--> |
| | | <!-- type="primary"--> |
| | | <!-- size="mini"--> |
| | | <!-- @click="handleEdit(notice)"--> |
| | | <!-- :disabled="isNoticeExpired(notice)"--> |
| | | <!-- >--> |
| | | <!-- 编辑--> |
| | | <!-- </up-button>--> |
| | | <!-- <up-button--> |
| | | <!-- text--> |
| | | <!-- type="error"--> |
| | | <!-- size="mini"--> |
| | | <!-- @click="handleDelete(notice.id)"--> |
| | | <!-- >--> |
| | | <!-- 删除--> |
| | | <!-- </up-button>--> |
| | | <!-- </view>--> |
| | | </view> |
| | | <view class="card-content"> |
| | | <text>{{ notice.content }}</text> |
| | | </view> |
| | | <view class="card-footer"> |
| | | <view class="card-meta"> |
| | | <text class="type" :class="'type-' + notice.type"> |
| | | <text class="type" |
| | | :class="'type-' + notice.type"> |
| | | {{ notice.type }} |
| | | </text> |
| | | <text class="priority" :class="'priority-' + notice.priority"> |
| | | <text class="priority" |
| | | :class="'priority-' + notice.priority"> |
| | | {{ getPriorityText(notice.priority) }} |
| | | </text> |
| | | <text class="status" :class="'status-' + getNoticeStatus(notice)"> |
| | | <text class="status" |
| | | :class="'status-' + getNoticeStatus(notice)"> |
| | | {{ getStatusText(getNoticeStatus(notice)) }} |
| | | </text> |
| | | </view> |
| | | <view class="card-info"> |
| | | <text class="creator">{{ notice.createUserName }}</text> |
| | | <text class="expiration" v-if="notice.expirationDate">截止日期:{{ notice.expirationDate }}</text> |
| | | <text class="expiration" |
| | | v-if="notice.expirationDate">截止日期:{{ notice.expirationDate }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="card-remark" v-if="notice.remark"> |
| | | <up-icon name="info-circle" size="16" color="#409eff" /> |
| | | <view class="card-remark" |
| | | v-if="notice.remark"> |
| | | <up-icon name="info-circle" |
| | | size="16" |
| | | color="#409eff" /> |
| | | <text>{{ notice.remark }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 设备维修通知 --> |
| | | <view |
| | | v-for="notice in maintenanceNotices" |
| | | :key="'maintenance-' + notice.id" |
| | | class="notice-card maintenance-card" |
| | | :class="{ 'urgent': notice.priority === '3' }" |
| | | > |
| | | <view v-for="notice in maintenanceNotices" |
| | | :key="'maintenance-' + notice.id" |
| | | class="notice-card maintenance-card" |
| | | :class="{ 'urgent': notice.priority === '3' }"> |
| | | <view class="card-header"> |
| | | <view class="card-title"> |
| | | <view class="maintenance-icon"> |
| | | <up-icon name="wrench" size="18" color="#e6a23c" /> |
| | | <up-icon name="wrench" |
| | | size="18" |
| | | color="#e6a23c" /> |
| | | </view> |
| | | <text>{{ notice.title }}</text> |
| | | </view> |
| | | <view class="card-actions"> |
| | | <up-button |
| | | text |
| | | type="primary" |
| | | size="mini" |
| | | @click="handleEdit(notice)" |
| | | :disabled="isNoticeExpired(notice)" |
| | | > |
| | | <up-button text |
| | | type="primary" |
| | | size="mini" |
| | | @click="handleEdit(notice)" |
| | | :disabled="isNoticeExpired(notice)"> |
| | | 编辑 |
| | | </up-button> |
| | | <up-button |
| | | text |
| | | type="error" |
| | | size="mini" |
| | | @click="handleDelete(notice.id)" |
| | | > |
| | | <up-button text |
| | | type="error" |
| | | size="mini" |
| | | @click="handleDelete(notice.id)"> |
| | | 删除 |
| | | </up-button> |
| | | </view> |
| | |
| | | </view> |
| | | <view class="card-footer"> |
| | | <view class="card-meta"> |
| | | <text class="priority" :class="'priority-' + notice.priority"> |
| | | <text class="priority" |
| | | :class="'priority-' + notice.priority"> |
| | | {{ getPriorityText(notice.priority) }} |
| | | </text> |
| | | <text class="status" :class="'status-' + getNoticeStatus(notice)"> |
| | | <text class="status" |
| | | :class="'status-' + getNoticeStatus(notice)"> |
| | | {{ getStatusText(getNoticeStatus(notice)) }} |
| | | </text> |
| | | </view> |
| | | <view class="card-info"> |
| | | <text class="creator">{{ notice.createUserName }}</text> |
| | | <text class="expiration" v-if="notice.expirationDate">截止日期:{{ notice.expirationDate }}</text> |
| | | <text class="expiration" |
| | | v-if="notice.expirationDate">截止日期:{{ notice.expirationDate }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="card-remark" v-if="notice.remark"> |
| | | <up-icon name="info-circle" size="16" color="#409eff" /> |
| | | <view class="card-remark" |
| | | v-if="notice.remark"> |
| | | <up-icon name="info-circle" |
| | | size="16" |
| | | color="#409eff" /> |
| | | <text>{{ notice.remark }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 空状态 --> |
| | | <view class="empty-state" v-if="holidayNotices.length === 0 && maintenanceNotices.length === 0"> |
| | | <view class="empty-state" |
| | | v-if="holidayNotices.length === 0 && maintenanceNotices.length === 0"> |
| | | <text>暂无通知公告</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 新增/编辑弹窗 --> |
| | | <up-popup |
| | | v-model:show="dialogVisible" |
| | | mode="bottom" |
| | | :round="18" |
| | | :safeAreaInsetBottom="true" |
| | | @close="resetForm" |
| | | > |
| | | <up-popup v-model:show="dialogVisible" |
| | | mode="bottom" |
| | | :round="18" |
| | | :safeAreaInsetBottom="true" |
| | | @close="resetForm"> |
| | | <view class="dialog-container"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">{{ dialogTitle }}</text> |
| | | </view> |
| | | <view class="dialog-body"> |
| | | <up-form |
| | | ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | labelWidth="80" |
| | | > |
| | | <up-form-item label="公告标题" prop="title"> |
| | | <up-input v-model="form.title" placeholder="请输入公告标题" /> |
| | | <up-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | labelWidth="80"> |
| | | <up-form-item label="公告标题" |
| | | prop="title"> |
| | | <up-input v-model="form.title" |
| | | placeholder="请输入公告标题" /> |
| | | </up-form-item> |
| | | <up-form-item label="公告类型" prop="type"> |
| | | <up-input v-model="form.type" placeholder="请输入公告类型" /> |
| | | <up-form-item label="公告类型" |
| | | prop="type"> |
| | | <up-input v-model="form.type" |
| | | placeholder="请输入公告类型" /> |
| | | </up-form-item> |
| | | <up-form-item label="状态"> |
| | | <up-radio-group v-model="form.status"> |
| | |
| | | <up-radio :name="1">正式发布</up-radio> |
| | | </up-radio-group> |
| | | </up-form-item> |
| | | <up-form-item label="优先级" prop="priority"> |
| | | <up-select |
| | | v-model="form.priority" |
| | | :options="priorityOptions" |
| | | placeholder="请选择优先级" |
| | | /> |
| | | <up-form-item label="优先级" |
| | | prop="priority"> |
| | | <up-select v-model="form.priority" |
| | | :options="priorityOptions" |
| | | placeholder="请选择优先级" /> |
| | | </up-form-item> |
| | | <up-form-item label="过期时间" prop="expirationDate"> |
| | | <up-datetime-picker |
| | | v-model="form.expirationDate" |
| | | mode="date" |
| | | @confirm="onExpireConfirm" |
| | | > |
| | | <up-input |
| | | :value="form.expirationDate" |
| | | placeholder="请选择日期" |
| | | readonly |
| | | /> |
| | | <up-form-item label="过期时间" |
| | | prop="expirationDate"> |
| | | <up-datetime-picker v-model="form.expirationDate" |
| | | mode="date" |
| | | @confirm="onExpireConfirm"> |
| | | <up-input :value="form.expirationDate" |
| | | placeholder="请选择日期" |
| | | readonly /> |
| | | </up-datetime-picker> |
| | | </up-form-item> |
| | | <up-form-item label="公告内容" prop="content"> |
| | | <up-textarea |
| | | v-model="form.content" |
| | | placeholder="请输入公告内容" |
| | | :maxlength="500" |
| | | count |
| | | /> |
| | | <up-form-item label="公告内容" |
| | | prop="content"> |
| | | <up-textarea v-model="form.content" |
| | | placeholder="请输入公告内容" |
| | | :maxlength="500" |
| | | count /> |
| | | </up-form-item> |
| | | <up-form-item label="备注"> |
| | | <up-textarea |
| | | v-model="form.remark" |
| | | placeholder="请输入备注信息" |
| | | :maxlength="200" |
| | | count |
| | | /> |
| | | <up-textarea v-model="form.remark" |
| | | placeholder="请输入备注信息" |
| | | :maxlength="200" |
| | | count /> |
| | | </up-form-item> |
| | | </up-form> |
| | | </view> |
| | | <view class="dialog-footer"> |
| | | <up-button |
| | | text="取消" |
| | | type="info" |
| | | plain |
| | | @click="dialogVisible = false" |
| | | :customStyle="{ marginRight: '10px', flex: 1 }" |
| | | /> |
| | | <up-button |
| | | text="确定" |
| | | type="primary" |
| | | @click="submitForm" |
| | | :customStyle="{ flex: 1 }" |
| | | /> |
| | | <up-button text="取消" |
| | | type="info" |
| | | plain |
| | | @click="dialogVisible = false" |
| | | :customStyle="{ marginRight: '10px', flex: 1 }" /> |
| | | <up-button text="确定" |
| | | type="primary" |
| | | @click="submitForm" |
| | | :customStyle="{ flex: 1 }" /> |
| | | </view> |
| | | </view> |
| | | </up-popup> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref, reactive, toRefs } from "vue"; |
| | | import { onReachBottom } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { |
| | | addNotice, |
| | | delNotice, |
| | | getCount, |
| | | listNotice, |
| | | updateNotice |
| | | } from "@/api/collaborativeApproval/noticeManagement.js"; |
| | | import { onMounted, ref, reactive, toRefs } from "vue"; |
| | | import { onReachBottom } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { |
| | | addNotice, |
| | | delNotice, |
| | | getCount, |
| | | listNotice, |
| | | updateNotice, |
| | | } from "@/api/collaborativeApproval/noticeManagement.js"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const userStore = useUserStore(); |
| | | |
| | | // 响应式数据 |
| | | const data = reactive({ |
| | | searchForm: { |
| | | title: "", |
| | | type: undefined, |
| | | status: undefined, |
| | | }, |
| | | form: { |
| | | id: undefined, |
| | | title: "", |
| | | type: null, |
| | | content: "", |
| | | status: 0, |
| | | priority: 1, |
| | | remark: "", |
| | | expirationDate: "", |
| | | }, |
| | | rules: { |
| | | title: [ |
| | | {required: true, message: "公告标题不能为空", trigger: "blur"} |
| | | ], |
| | | type: [ |
| | | {required: true, message: "请选择公告类型", trigger: "change"} |
| | | ], |
| | | content: [ |
| | | {required: true, message: "公告内容不能为空", trigger: "blur"} |
| | | ], |
| | | expirationDate: [ |
| | | {required: true, message: "请选择日期", trigger: "change"} |
| | | ] |
| | | } |
| | | }); |
| | | |
| | | const {searchForm, form, rules} = toRefs(data); |
| | | |
| | | // 页面状态 |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const selectedIds = ref([]); |
| | | const formRef = ref(); |
| | | |
| | | const priorityOptions = [ |
| | | { label: "普通", value: 1 }, |
| | | { label: "重要", value: 2 }, |
| | | { label: "紧急", value: 3 }, |
| | | ]; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | const onExpireConfirm = (e) => { |
| | | if (!e) return; |
| | | // uview-plus datetime-picker confirm 事件返回的 value |
| | | const value = e.value || e; |
| | | form.value.expirationDate = value; |
| | | }; |
| | | |
| | | |
| | | const getPriorityText = (priority) => { |
| | | const priorityMap = {"1": "普通", "2": "重要", "3": "紧急"}; |
| | | return priorityMap[priority] || "普通"; |
| | | }; |
| | | |
| | | const getStatusText = (status) => { |
| | | 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) => { |
| | | if (type === 'add') { |
| | | dialogTitle.value = "新增公告"; |
| | | form.value = { |
| | | id: undefined, |
| | | // 响应式数据 |
| | | const data = reactive({ |
| | | searchForm: { |
| | | title: "", |
| | | type: undefined, |
| | | status: undefined, |
| | | }, |
| | | form: { |
| | | id: undefined, |
| | | title: "", |
| | | type: null, |
| | | content: "", |
| | | status: 0, |
| | | priority: 1, |
| | | remark: "", |
| | | expirationDate: "", |
| | | }; |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | }, |
| | | rules: { |
| | | title: [{ required: true, message: "公告标题不能为空", trigger: "blur" }], |
| | | type: [{ required: true, message: "请选择公告类型", trigger: "change" }], |
| | | content: [{ required: true, message: "公告内容不能为空", trigger: "blur" }], |
| | | expirationDate: [ |
| | | { required: true, message: "请选择日期", trigger: "change" }, |
| | | ], |
| | | }, |
| | | }); |
| | | |
| | | const handleEdit = (row) => { |
| | | if (isNoticeExpired(row)) { |
| | | uni.showToast({ |
| | | title: "已过期的公告不可编辑", |
| | | icon: "none" |
| | | const { searchForm, form, rules } = toRefs(data); |
| | | |
| | | // 页面状态 |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const selectedIds = ref([]); |
| | | const formRef = ref(); |
| | | |
| | | const priorityOptions = [ |
| | | { label: "普通", value: 1 }, |
| | | { label: "重要", value: 2 }, |
| | | { label: "紧急", value: 3 }, |
| | | ]; |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | const onExpireConfirm = e => { |
| | | if (!e) return; |
| | | // uview-plus datetime-picker confirm 事件返回的 value |
| | | const value = e.value || e; |
| | | form.value.expirationDate = value; |
| | | }; |
| | | |
| | | const getPriorityText = priority => { |
| | | const priorityMap = { 1: "普通", 2: "重要", 3: "紧急" }; |
| | | return priorityMap[priority] || "普通"; |
| | | }; |
| | | |
| | | const getStatusText = status => { |
| | | 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 => { |
| | | if (type === "add") { |
| | | dialogTitle.value = "新增公告"; |
| | | form.value = { |
| | | id: undefined, |
| | | title: "", |
| | | type: undefined, |
| | | content: "", |
| | | status: 0, |
| | | priority: 1, |
| | | remark: "", |
| | | expirationDate: "", |
| | | }; |
| | | } |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleEdit = row => { |
| | | if (isNoticeExpired(row)) { |
| | | uni.showToast({ |
| | | title: "已过期的公告不可编辑", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | dialogTitle.value = "编辑公告"; |
| | | form.value = { ...row }; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleDelete = id => { |
| | | if (!id) return; |
| | | uni.showModal({ |
| | | title: "提示", |
| | | content: "确认删除这条公告吗?", |
| | | success: res => { |
| | | if (res.confirm) { |
| | | delNotice(id).then(() => { |
| | | uni.showToast({ |
| | | title: "删除成功", |
| | | icon: "success", |
| | | }); |
| | | resetTable(); |
| | | }); |
| | | } |
| | | }, |
| | | }); |
| | | return; |
| | | } |
| | | dialogTitle.value = "编辑公告"; |
| | | form.value = {...row}; |
| | | dialogVisible.value = true; |
| | | }; |
| | | }; |
| | | |
| | | const handleDelete = (id) => { |
| | | if (!id) return; |
| | | uni.showModal({ |
| | | title: "提示", |
| | | content: "确认删除这条公告吗?", |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | delNotice(id).then(() => { |
| | | uni.showToast({ |
| | | title: "删除成功", |
| | | icon: "success" |
| | | // 预留批量删除(目前未实现选中逻辑,仅占位) |
| | | const handleDeleteBatch = () => { |
| | | if (!selectedIds.value.length) return; |
| | | uni.showModal({ |
| | | title: "提示", |
| | | content: "确认删除选中的公告吗?", |
| | | success: res => { |
| | | if (res.confirm) { |
| | | // 根据selectedIds执行批量删除逻辑(可按需扩展) |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (form.value.id) { |
| | | // 编辑模式 |
| | | updateNotice(form.value).then(res => { |
| | | uni.showToast({ |
| | | title: "修改成功", |
| | | icon: "success", |
| | | }); |
| | | resetTable(); |
| | | }); |
| | | resetTable(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 预留批量删除(目前未实现选中逻辑,仅占位) |
| | | const handleDeleteBatch = () => { |
| | | if (!selectedIds.value.length) return; |
| | | uni.showModal({ |
| | | title: "提示", |
| | | content: "确认删除选中的公告吗?", |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | // 根据selectedIds执行批量删除逻辑(可按需扩展) |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | if (form.value.id) { |
| | | // 编辑模式 |
| | | updateNotice(form.value).then(res => { |
| | | uni.showToast({ |
| | | title: "修改成功", |
| | | icon: "success" |
| | | } else { |
| | | // 新增模式 |
| | | addNotice(form.value).then(res => { |
| | | uni.showToast({ |
| | | title: "新增成功", |
| | | icon: "success", |
| | | }); |
| | | resetTable(); |
| | | }); |
| | | resetTable(); |
| | | }) |
| | | } |
| | | dialogVisible.value = false; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const totalNoticeCount = ref(0); |
| | | const fetchCount = () => { |
| | | getCount().then(res => { |
| | | totalNoticeCount.value = res.data.reduce( |
| | | (total, item) => total + item.count, |
| | | 0 |
| | | ); |
| | | }); |
| | | }; |
| | | |
| | | const holidayNotices = ref([]); |
| | | const maintenanceNotices = ref([]); |
| | | const holidayNoticePage = ref({ |
| | | total: 0, |
| | | current: 1, |
| | | size: 9, |
| | | }); |
| | | |
| | | const maintenanceNoticePage = ref({ |
| | | total: 0, |
| | | current: 1, |
| | | size: 9, |
| | | }); |
| | | |
| | | const isLoadingMore = ref(false); |
| | | |
| | | const fetchHolidayNotices = (append = false) => { |
| | | listNotice({ ...holidayNoticePage.value }).then(res => { |
| | | const records = res?.data?.records || []; |
| | | holidayNoticePage.value.total = res?.data?.total || 0; |
| | | if (append && holidayNotices.value.length) { |
| | | holidayNotices.value = [...holidayNotices.value, ...records]; |
| | | } else { |
| | | // 新增模式 |
| | | addNotice(form.value).then(res => { |
| | | uni.showToast({ |
| | | title: "新增成功", |
| | | icon: "success" |
| | | }); |
| | | resetTable(); |
| | | }) |
| | | holidayNotices.value = records; |
| | | } |
| | | dialogVisible.value = false; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const fetchMaintenanceNotices = (append = false) => { |
| | | listNotice({ ...holidayNoticePage.value, type: 2 }).then(res => { |
| | | const records = res?.data?.records || []; |
| | | maintenanceNoticePage.value.total = res?.data?.total || 0; |
| | | if (append && maintenanceNotices.value.length) { |
| | | maintenanceNotices.value = [...maintenanceNotices.value, ...records]; |
| | | } else { |
| | | maintenanceNotices.value = records; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCurrentChange = val => { |
| | | holidayNoticePage.value.size = val.limit; |
| | | holidayNoticePage.value.current = val.page; |
| | | maintenanceNoticePage.value.size = val.limit; |
| | | maintenanceNoticePage.value.current = val.page; |
| | | fetchHolidayNotices(); |
| | | fetchMaintenanceNotices(); |
| | | }; |
| | | |
| | | const resetTable = () => { |
| | | holidayNoticePage.value.current = 1; |
| | | holidayNoticePage.value.size = 9; |
| | | maintenanceNoticePage.value.current = 1; |
| | | maintenanceNoticePage.value.size = 9; |
| | | fetchHolidayNotices(); |
| | | fetchMaintenanceNotices(); |
| | | fetchCount(); |
| | | }; |
| | | |
| | | const resetForm = () => { |
| | | formRef.value?.resetFields(); |
| | | }; |
| | | |
| | | // 生命周期 |
| | | onMounted(() => { |
| | | fetchCount(); |
| | | fetchHolidayNotices(); |
| | | fetchMaintenanceNotices(); |
| | | }); |
| | | }; |
| | | |
| | | const totalNoticeCount = ref(0) |
| | | const fetchCount = () => { |
| | | getCount().then(res => { |
| | | totalNoticeCount.value = res.data.reduce((total, item) => total + item.count, 0); |
| | | // 上划加载更多 |
| | | onReachBottom(() => { |
| | | if (isLoadingMore.value) return; |
| | | isLoadingMore.value = true; |
| | | |
| | | holidayNoticePage.value.current += 1; |
| | | maintenanceNoticePage.value.current += 1; |
| | | |
| | | Promise.all([ |
| | | new Promise(resolve => { |
| | | fetchHolidayNotices(true); |
| | | resolve(); |
| | | }), |
| | | new Promise(resolve => { |
| | | fetchMaintenanceNotices(true); |
| | | resolve(); |
| | | }), |
| | | ]).finally(() => { |
| | | isLoadingMore.value = false; |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | const holidayNotices = ref([]) |
| | | const maintenanceNotices = ref([]) |
| | | const holidayNoticePage = ref({ |
| | | total: 0, |
| | | current: 1, |
| | | size: 9 |
| | | }) |
| | | |
| | | const maintenanceNoticePage = ref({ |
| | | total: 0, |
| | | current: 1, |
| | | size: 9 |
| | | }) |
| | | |
| | | const isLoadingMore = ref(false) |
| | | |
| | | const fetchHolidayNotices = (append = false) => { |
| | | listNotice({...holidayNoticePage.value}).then(res => { |
| | | const records = res?.data?.records || [] |
| | | holidayNoticePage.value.total = res?.data?.total || 0 |
| | | if (append && holidayNotices.value.length) { |
| | | holidayNotices.value = [...holidayNotices.value, ...records] |
| | | } else { |
| | | holidayNotices.value = records |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const fetchMaintenanceNotices = (append = false) => { |
| | | listNotice({...holidayNoticePage.value, type: 2}).then(res => { |
| | | const records = res?.data?.records || [] |
| | | maintenanceNoticePage.value.total = res?.data?.total || 0 |
| | | if (append && maintenanceNotices.value.length) { |
| | | maintenanceNotices.value = [...maintenanceNotices.value, ...records] |
| | | } else { |
| | | maintenanceNotices.value = records |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCurrentChange = (val) => { |
| | | holidayNoticePage.value.size = val.limit |
| | | holidayNoticePage.value.current = val.page |
| | | maintenanceNoticePage.value.size = val.limit |
| | | maintenanceNoticePage.value.current = val.page |
| | | fetchHolidayNotices() |
| | | fetchMaintenanceNotices() |
| | | }; |
| | | |
| | | const resetTable = () => { |
| | | holidayNoticePage.value.current = 1 |
| | | holidayNoticePage.value.size = 9 |
| | | maintenanceNoticePage.value.current = 1 |
| | | maintenanceNoticePage.value.size = 9 |
| | | fetchHolidayNotices() |
| | | fetchMaintenanceNotices() |
| | | fetchCount() |
| | | }; |
| | | |
| | | const resetForm = () => { |
| | | formRef.value?.resetFields(); |
| | | }; |
| | | |
| | | // 生命周期 |
| | | onMounted(() => { |
| | | fetchCount() |
| | | fetchHolidayNotices() |
| | | fetchMaintenanceNotices() |
| | | }); |
| | | |
| | | // 上划加载更多 |
| | | onReachBottom(() => { |
| | | if (isLoadingMore.value) return; |
| | | isLoadingMore.value = true; |
| | | |
| | | holidayNoticePage.value.current += 1; |
| | | maintenanceNoticePage.value.current += 1; |
| | | |
| | | Promise.all([ |
| | | new Promise((resolve) => { |
| | | fetchHolidayNotices(true); |
| | | resolve(); |
| | | }), |
| | | new Promise((resolve) => { |
| | | fetchMaintenanceNotices(true); |
| | | resolve(); |
| | | }) |
| | | ]).finally(() => { |
| | | isLoadingMore.value = false; |
| | | }); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .notice-page { |
| | | min-height: 100vh; |
| | | background: #f5f7fa; |
| | | padding-bottom: 16px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .search_form { |
| | | background: #ffffff; |
| | | padding: 12px 16px; |
| | | margin: 8px 12px 12px; |
| | | border-radius: 10px; |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04); |
| | | display: flex; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | } |
| | | |
| | | .search_title { |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .ml10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .notice-board { |
| | | padding: 0 12px 16px; |
| | | } |
| | | |
| | | .notice-section { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .section-header { |
| | | display: flex; |
| | | align-items: center; |
| | | margin: 4px 4px 12px; |
| | | } |
| | | |
| | | .section-header h3 { |
| | | margin: 0; |
| | | color: #303133; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .section-count { |
| | | margin-left: 10px; |
| | | background: #409eff; |
| | | color: white; |
| | | padding: 2px 8px; |
| | | border-radius: 12px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .notice-cards { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .notice-card { |
| | | background: white; |
| | | border-radius: 12px; |
| | | padding: 14px 14px 10px; |
| | | box-shadow: 0 4px 10px rgba(15, 23, 42, 0.06); |
| | | transition: all 0.3s ease; |
| | | border-left: 4px solid transparent; |
| | | } |
| | | |
| | | .notice-card:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .holiday-card { |
| | | border-left-color: #67c23a; |
| | | } |
| | | |
| | | .maintenance-card { |
| | | border-left-color: #e6a23c; |
| | | } |
| | | |
| | | .urgent { |
| | | border-left-color: #f56c6c; |
| | | background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .card-title { |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 15px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | 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; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .card-content { |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .card-content text { |
| | | color: #606266; |
| | | line-height: 1.6; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .card-footer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .card-meta { |
| | | display: flex; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .type, .priority, .status { |
| | | padding: 2px 8px; |
| | | border-radius: 12px; |
| | | font-size: 12px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .type-1 { |
| | | background: #f0f9ff; |
| | | color: #0369a1; |
| | | } |
| | | |
| | | .type-2 { |
| | | background: #fef3c7; |
| | | color: #d97706; |
| | | } |
| | | |
| | | .priority-1 { |
| | | background: #f0f9ff; |
| | | color: #0369a1; |
| | | } |
| | | |
| | | .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; |
| | | flex-direction: column; |
| | | align-items: flex-end; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .creator { |
| | | font-weight: 500; |
| | | margin-bottom: 2px; |
| | | } |
| | | |
| | | .expiration { |
| | | margin-top: 2px; |
| | | } |
| | | |
| | | .card-remark { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 6px; |
| | | padding: 8px 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | font-size: 12px; |
| | | color: #606266; |
| | | border-left: 3px solid #409eff; |
| | | } |
| | | |
| | | .empty-state { |
| | | text-align: center; |
| | | padding: 48px 16px; |
| | | color: #999; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | |
| | | /* 移动端弹窗样式 */ |
| | | .dialog-container { |
| | | background: #ffffff; |
| | | border-radius: 18px 18px 0 0; |
| | | max-height: 80vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .dialog-header { |
| | | padding: 16px 20px 8px 20px; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .dialog-body { |
| | | flex: 1; |
| | | padding: 0 16px 12px 16px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | padding: 12px 16px 16px 16px; |
| | | border-top: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | /* 响应式设计 */ |
| | | @media (max-width: 768px) { |
| | | .search_form { |
| | | flex-direction: column; |
| | | gap: 15px; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .search_form > div { |
| | | width: 100%; |
| | | .notice-page { |
| | | min-height: 100vh; |
| | | background: #f5f7fa; |
| | | padding-bottom: 16px; |
| | | display: flex; |
| | | gap: 10px; |
| | | flex-direction: column; |
| | | } |
| | | } |
| | | |
| | | .search_form { |
| | | background: #ffffff; |
| | | padding: 12px 16px; |
| | | margin: 8px 12px 12px; |
| | | border-radius: 10px; |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04); |
| | | display: flex; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | } |
| | | |
| | | .search_title { |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .ml10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .notice-board { |
| | | padding: 0 12px 16px; |
| | | } |
| | | |
| | | .notice-section { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .section-header { |
| | | display: flex; |
| | | align-items: center; |
| | | margin: 4px 4px 12px; |
| | | } |
| | | |
| | | .section-header h3 { |
| | | margin: 0; |
| | | color: #303133; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .section-count { |
| | | margin-left: 10px; |
| | | background: #409eff; |
| | | color: white; |
| | | padding: 2px 8px; |
| | | border-radius: 12px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .notice-cards { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .notice-card { |
| | | background: white; |
| | | border-radius: 12px; |
| | | padding: 14px 14px 10px; |
| | | box-shadow: 0 4px 10px rgba(15, 23, 42, 0.06); |
| | | transition: all 0.3s ease; |
| | | border-left: 4px solid transparent; |
| | | } |
| | | |
| | | .notice-card:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .holiday-card { |
| | | border-left-color: #67c23a; |
| | | } |
| | | |
| | | .maintenance-card { |
| | | border-left-color: #e6a23c; |
| | | } |
| | | |
| | | .urgent { |
| | | border-left-color: #f56c6c; |
| | | background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .card-title { |
| | | display: flex; |
| | | align-items: center; |
| | | font-size: 15px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | 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; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .card-content { |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .card-content text { |
| | | color: #606266; |
| | | line-height: 1.6; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .card-footer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .card-meta { |
| | | display: flex; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .type, |
| | | .priority, |
| | | .status { |
| | | padding: 2px 8px; |
| | | border-radius: 12px; |
| | | font-size: 12px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .type-1 { |
| | | background: #f0f9ff; |
| | | color: #0369a1; |
| | | } |
| | | |
| | | .type-2 { |
| | | background: #fef3c7; |
| | | color: #d97706; |
| | | } |
| | | |
| | | .priority-1 { |
| | | background: #f0f9ff; |
| | | color: #0369a1; |
| | | } |
| | | |
| | | .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; |
| | | flex-direction: column; |
| | | align-items: flex-end; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .creator { |
| | | font-weight: 500; |
| | | margin-bottom: 2px; |
| | | } |
| | | |
| | | .expiration { |
| | | margin-top: 2px; |
| | | } |
| | | |
| | | .card-remark { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 6px; |
| | | padding: 8px 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | font-size: 12px; |
| | | color: #606266; |
| | | border-left: 3px solid #409eff; |
| | | } |
| | | |
| | | .empty-state { |
| | | text-align: center; |
| | | padding: 48px 16px; |
| | | color: #999; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | |
| | | /* 移动端弹窗样式 */ |
| | | .dialog-container { |
| | | background: #ffffff; |
| | | border-radius: 18px 18px 0 0; |
| | | max-height: 80vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .dialog-header { |
| | | padding: 16px 20px 8px 20px; |
| | | } |
| | | |
| | | .dialog-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .dialog-body { |
| | | flex: 1; |
| | | padding: 0 16px 12px 16px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | padding: 12px 16px 16px 16px; |
| | | border-top: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | /* 响应式设计 */ |
| | | @media (max-width: 768px) { |
| | | .search_form { |
| | | flex-direction: column; |
| | | gap: 15px; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .search_form > div { |
| | | width: 100%; |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | } |
| | | </style> |