增加通知公告功能
This commit is contained in:
@@ -93,8 +93,42 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧 40%:工作日程提醒 -->
|
||||
<!-- 右侧 30%:通知公告 + 工作日程提醒 -->
|
||||
<div class="col-right">
|
||||
<div class="chart-card notice-card">
|
||||
<div class="chart-title notice-title">
|
||||
<span>通知公告</span>
|
||||
<el-button link type="primary" @click="goNotice"
|
||||
>查看全部</el-button
|
||||
>
|
||||
</div>
|
||||
<div v-loading="noticeLoading" class="notice-list">
|
||||
<div
|
||||
v-for="item in notices"
|
||||
:key="item.id"
|
||||
class="notice-item"
|
||||
@click="openNoticeDetail(item)"
|
||||
>
|
||||
<el-tag
|
||||
:type="noticeTypeTag(item.notice_type)"
|
||||
size="small"
|
||||
effect="light"
|
||||
class="notice-type"
|
||||
>
|
||||
{{ noticeTypeLabel(item.notice_type) }}
|
||||
</el-tag>
|
||||
<span v-if="item.is_top === 1" class="notice-top">置顶</span>
|
||||
<span class="notice-text">{{ item.title }}</span>
|
||||
<span class="notice-time">{{ noticeTime(item) }}</span>
|
||||
</div>
|
||||
<el-empty
|
||||
v-if="!notices.length && !noticeLoading"
|
||||
description="暂无通知公告"
|
||||
:image-size="60"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-card schedule-card">
|
||||
<div class="chart-title schedule-title">
|
||||
<span>工作日程提醒</span>
|
||||
@@ -179,6 +213,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 通知公告详情 -->
|
||||
<el-dialog
|
||||
v-model="noticeDetailVisible"
|
||||
:title="noticeDetailItem?.title"
|
||||
width="620px"
|
||||
>
|
||||
<div v-if="noticeDetailItem" class="notice-detail-body">
|
||||
<div class="notice-detail-meta">
|
||||
<el-tag
|
||||
:type="noticeTypeTag(noticeDetailItem.notice_type)"
|
||||
size="small"
|
||||
effect="light"
|
||||
>
|
||||
{{ noticeTypeLabel(noticeDetailItem.notice_type) }}
|
||||
</el-tag>
|
||||
<span class="notice-detail-text"
|
||||
>{{ noticeDetailItem.publisher_name }} ·
|
||||
{{ formatDateTime(noticeDetailItem.publish_time) }}</span
|
||||
>
|
||||
<span class="notice-detail-text"
|
||||
>阅读 {{ noticeDetailItem.read_count || 0 }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="notice-detail-content">
|
||||
{{ noticeDetailItem.content || "暂无内容" }}
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 日程详情抽屉:点击任务后由用户明确选择延期 / 已完成 / 反审核 -->
|
||||
<ScheduleDetail
|
||||
v-model="scheduleDetailVisible"
|
||||
@@ -205,6 +268,10 @@ import { ElMessage } from "element-plus";
|
||||
import * as echarts from "echarts";
|
||||
import { Refresh } from "@element-plus/icons-vue";
|
||||
import { getReimbursementDashboard } from "@/api/reimburse";
|
||||
import {
|
||||
getNoticeDetail,
|
||||
getNoticePortal
|
||||
} from "@/api/oaNotice";
|
||||
import { finishSchedule, getScheduleList } from "@/api/oaSchedule";
|
||||
import ScheduleDetail from "../schedule/components/detail.vue";
|
||||
import SchedulePostpone from "../schedule/components/postponeDialog.vue";
|
||||
@@ -459,6 +526,62 @@ const editSchedule = (item) => {
|
||||
router.push("/apps/oa/schedule");
|
||||
};
|
||||
|
||||
// ---------- 通知公告 ----------
|
||||
const notices = ref([]);
|
||||
const noticeLoading = ref(false);
|
||||
const noticeDetailVisible = ref(false);
|
||||
const noticeDetailItem = ref(null);
|
||||
|
||||
const noticeTypeLabel = (type) => ({ 1: "公告", 2: "活动" }[type] || "通知");
|
||||
const noticeTypeTag = (type) => ({ 1: "primary", 2: "success" }[type] || "info");
|
||||
|
||||
const formatDateTime = (value) => {
|
||||
if (!value) return "-";
|
||||
const d = new Date(value);
|
||||
if (Number.isNaN(d.getTime())) return String(value);
|
||||
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(
|
||||
d.getHours()
|
||||
)}:${pad2(d.getMinutes())}`;
|
||||
};
|
||||
|
||||
// 列表右侧时间:今天显示 HH:mm,本年显示 M/D,跨年显示 YYYY-M-D
|
||||
const noticeTime = (item) => {
|
||||
const d = item.publish_time ? new Date(item.publish_time) : null;
|
||||
if (!d || Number.isNaN(d.getTime())) return "";
|
||||
const now = new Date();
|
||||
if (fmtDate(d) === fmtDate(now)) {
|
||||
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
||||
}
|
||||
if (d.getFullYear() === now.getFullYear()) {
|
||||
return `${d.getMonth() + 1}/${d.getDate()}`;
|
||||
}
|
||||
return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
|
||||
};
|
||||
|
||||
const loadNotices = async () => {
|
||||
noticeLoading.value = true;
|
||||
try {
|
||||
const res = await getNoticePortal({ limit: 6 });
|
||||
notices.value = res?.data?.list || [];
|
||||
} catch (error) {
|
||||
console.warn("加载通知公告失败:", error?.message);
|
||||
} finally {
|
||||
noticeLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const openNoticeDetail = async (item) => {
|
||||
const res = await getNoticeDetail(item.id);
|
||||
if (res?.code === 200) {
|
||||
noticeDetailItem.value = res.data;
|
||||
noticeDetailVisible.value = true;
|
||||
} else {
|
||||
ElMessage.error(res?.msg || "加载公告失败");
|
||||
}
|
||||
};
|
||||
|
||||
const goNotice = () => router.push("/apps/oa/notice");
|
||||
|
||||
const loadDashboard = async () => {
|
||||
try {
|
||||
const data = responseData(await getReimbursementDashboard());
|
||||
@@ -473,6 +596,7 @@ const loadDashboard = async () => {
|
||||
}
|
||||
renderTrendChart();
|
||||
loadScheduleReminders();
|
||||
loadNotices();
|
||||
};
|
||||
|
||||
const handleResize = () => trendChart?.resize();
|
||||
@@ -629,6 +753,90 @@ onBeforeUnmount(() => {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.notice-card {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.notice-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
min-height: 80px;
|
||||
max-height: 240px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.notice-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
background: #fafbfc;
|
||||
|
||||
&:hover {
|
||||
background: #eef2f7;
|
||||
}
|
||||
|
||||
.notice-type {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.notice-top {
|
||||
flex-shrink: 0;
|
||||
font-size: 11px;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.notice-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
color: #303133;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.notice-time {
|
||||
flex-shrink: 0;
|
||||
color: #c0c4cc;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-detail-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.notice-detail-text {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.notice-detail-content {
|
||||
margin-top: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
color: #303133;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
max-height: 420px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.schedule-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user