Files
NoticeFront/src/views/notice/NoticeList.vue
T
chenxing c3ff58fd94
build / build (push) Successful in 5m1s
先审后发
2026-09-15 22:09:27 +08:00

259 lines
9.2 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.
<script setup lang="ts">
import { ref, computed, onMounted, watch, h } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useRequest } from "alova/client";
import { Message } from "view-ui-plus";
import { noticeApi } from "@/api/module/notice";
import type { NoticeVo, NoticeQuery } from "@/types/notice";
import MyConfirm from "@/components/MyConfirm";
import Utils from "@/utils";
const route = useRoute();
const router = useRouter();
/* 全部通知(noticelist) / 已发送(list) */
const isManage = computed(() => route.path === "/noticelist");
/* ============ 搜索条件(规范 §1:日期 fl + 下拉 fl + 关键字 fr 宽200 即搜) ============ */
const keyword = ref("");
const deptType = ref("");
const startDt = ref("");
const endDt = ref("");
const { data: deptOptions } = useRequest(noticeApi.getSysDic());
function fmtDate(d: Date) {
const pad = (n: number) => String(n).padStart(2, "0");
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}
/* ============ 分页(规范 §3:customPageCount 页长记忆) ============ */
const pageNum = ref(1);
const pageSize = ref(parseInt(localStorage.getItem("customPageCount") || "10"));
const { data: page, loading, send: reload } = useRequest(
(params: NoticeQuery) => noticeApi.sendList(params),
{ immediate: false, initialData: { content: [], total: 0, pageIndex: 1, pageSize: 10, totalPages: 0, hasNext: false } }
);
async function run() {
const params: NoticeQuery = {
keyword: keyword.value || "",
deptId: deptType.value || "",
pageIndex: pageNum.value,
pageSize: pageSize.value,
orderByTime: "1",
};
if (startDt.value) params.startTime = startDt.value;
if (endDt.value) params.endTime = endDt.value;
if (!isManage.value) {
params.scope = "user";
}
try {
await reload(params);
} catch (e: any) {
Message.error(e.message || "获取通知列表失败");
}
}
function doSearch() {
pageNum.value = 1;
run();
}
function pageUpdate(p: number) {
pageNum.value = p;
run();
}
function pageSizeUpdate(size: number) {
pageNum.value = 1;
pageSize.value = size;
localStorage.setItem("customPageCount", String(size));
run();
}
onMounted(() => {
// 旧版:已发通知默认筛选近一个月,通知列表不默认
if (!isManage.value) {
const start = new Date();
start.setMonth(start.getMonth() - 1);
startDt.value = fmtDate(start);
endDt.value = fmtDate(new Date());
} else {
startDt.value = "";
endDt.value = "";
}
run();
});
/* 同一组件复用于 /list 和 /manage,切换路由时重置条件并重新加载 */
watch(
() => route.path,
() => {
pageNum.value = 1;
keyword.value = "";
deptType.value = "";
if (!isManage.value) {
const start = new Date();
start.setMonth(start.getMonth() - 1);
startDt.value = fmtDate(start);
endDt.value = fmtDate(new Date());
} else {
startDt.value = "";
endDt.value = "";
}
run();
}
);
/* ============ 操作(规范 §2:operating-btn iconfont;§6:删除 myConfirm) ============ */
function openDetail(row: NoticeVo) {
// 详情路由随入口:已发送 → /list/detail(高亮已发送),全部通知 → /noticelist/detail(高亮全部通知)
const detailPath = route.path.startsWith("/noticelist") ? "/noticelist/detail" : "/list/detail";
router.push({ path: detailPath, query: { serid: row.id, type: 0 } });
}
function del(row: NoticeVo) {
MyConfirm({
title: "删除",
msg: `确定删除“ ${row.title} ”这条通知吗?`,
info: "删除后不可恢复",
async onOk() {
try {
await noticeApi.deleteNotice(String(row.id)).send();
Message.success("操作成功!");
// 删除后智能翻页:当前页仅剩一条则回退一页
if ((page.value?.content?.length || 0) <= 1 && pageNum.value > 1) pageNum.value -= 1;
run();
} catch (e: any) {
Message.error(e.message || "删除失败");
}
},
});
}
/* 附件下载:逐个触发(间隔 500ms,同旧版) */
function downloadFiles(row: NoticeVo & { UpFile?: string }) {
if (!row.UpFile) return;
row.UpFile.split(",").forEach((id: string, i: number) => {
setTimeout(() => {
window.location.href = noticeApi.fileDownUrl(Number(id));
}, i * 500);
});
}
/* 表格列(对照旧版 notice_list.vue;操作列图标式 width 180 fixed right) */
const columns = [
{ title: "序号", type: "index", width: 90, align: "center" },
{ title: "标题", key: "title", align: "left", tooltip: true },
{ title: "来源部门", key: "deptName", width: 130, align: "left" },
{ title: "发布人", key: "publisherName", width: 110, align: "left", tooltip: true },
{
title: "发布时间",
width: 170,
align: "left",
render: (h: any, p: any) => h("span", {}, String(p.row.createTime || "").slice(0, 16)),
},
{
title: "审核状态",
width: 100,
align: "center",
render: (h: any, p: any) => {
const st = p.row.auditStatus;
if (st === null || st === undefined) return h("span", { style: "color:#999;" }, "—");
const colors: Record<number, string> = { 0: "default", 10: "blue", 20: "green", 30: "red", 40: "orange" };
return h("Tag", { color: colors[st] || "default" }, () => p.row.auditStatusDesc || "—");
},
},
{ title: "已读人数", key: "readCount", width: 110, align: "left" },
{
title: "未读人数",
width: 110,
align: "left",
render: (h: any, p: any) => h("span", {}, (p.row.allowCount || 0) - (p.row.readCount || 0)),
},
{
title: "附件",
width: 70,
align: "center",
render: (h: any, p: any) =>
p.row.UpFile
? Utils.operatingHandle(h, "附件下载", "icon-attachment", () => downloadFiles(p.row))
: h("span", { style: "color:#999;" }, "无"),
},
{
title: "操作",
width: 180,
align: "center",
fixed: "right",
render: (h: any, p: any) => {
// BP-R5-1:审批中(audit=10)禁止删除(后端 403)——不再渲染死按钮,审批通过/驳回/撤回后恢复
const ops = [Utils.operatingHandle(h, "查看通知", "icon-browse", () => openDetail(p.row))];
if (p.row.auditStatus !== 10) {
ops.push(Utils.operatingHandle(h, "删除通知", "icon-ashbin", () => del(p.row)));
}
return ops;
},
},
];
const tableHeight = computed(() => Utils.useTableHeight({ isSearch: true, isOperat: true, isPage: true }));
</script>
<template>
<div class="page-wrapper">
<div class="main-container">
<!-- 1. 操作按钮区 -->
<div class="fix mb-12">
<Button class="fl" type="primary" size="large" @click="router.push('/list/new')">发通知</Button>
</div>
<!-- 2. 搜索区 -->
<div class="search-wrap fix">
<div class="search-wrap_date fl">
<DatePicker type="date" placeholder="开始时间" v-model="startDt" @on-change="doSearch"></DatePicker>
<span class="ml-8 mr-8">至</span>
<DatePicker type="date" placeholder="结束时间" v-model="endDt" @on-change="doSearch"></DatePicker>
</div>
<Select v-model="deptType" class="search-wrap_sel ml-12" clearable placeholder="来源部门" @on-change="doSearch">
<Option v-for="d in deptOptions" :key="d.k" :value="d.k">{{ d.v }}</Option>
</Select>
<Input
class="fr"
style="width: 200px"
search
suffix="ios-search"
placeholder="输入关键字搜索"
v-model="keyword"
@on-search="doSearch"
@on-clear="doSearch"
/>
</div>
<!-- 3. 表格 + 分页 -->
<Table
:columns="columns"
:data="page.content"
class="link-table table-bottom-box"
:height="tableHeight"
:loading="loading"
no-data-text="暂无数据"
></Table>
<div class="page-panel">
<div class="fl ml-15 align-middle"></div>
<Page
class="page-box fr"
:total="page.total"
:current="pageNum"
:page-size="pageSize"
:page-size-opts="[10, 20, 30, 40]"
@on-page-size-change="pageSizeUpdate"
@on-change="pageUpdate"
placement="top"
show-total
show-sizer
></Page>
</div>
</div>
</div>
</template>
<style scoped></style>