259 lines
9.2 KiB
Vue
259 lines
9.2 KiB
Vue
<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>
|