import { Search } from "@element-plus/icons-vue";
|
import dayjs from "dayjs";
|
import { ElMessageBox } from "element-plus";
|
import { computed, reactive, ref, watch } from "vue";
|
import {
|
NOTICE_TYPE_OPTIONS,
|
PRIORITY_OPTIONS,
|
PUBLISH_STATUS_OPTIONS,
|
READ_SCOPE_OPTIONS,
|
DEPT_OPTIONS,
|
createEmptyForm,
|
createInitialMockNotices,
|
loadStoredNotices,
|
saveStoredNotices,
|
nextNoticeNo,
|
validateNoticeForm,
|
noticeTypeLabel,
|
priorityLabel,
|
publishStatusLabel,
|
isExpired,
|
} from "./noticeAnnouncementUtils.js";
|
|
export function useNoticeAnnouncement() {
|
const stored = loadStoredNotices();
|
const allRows = ref(stored?.length ? stored : createInitialMockNotices());
|
|
const searchForm = reactive({
|
keyword: "",
|
noticeType: "",
|
priority: "",
|
publishStatus: "",
|
publishDateRange: [],
|
});
|
|
const tableLoading = ref(false);
|
const page = reactive({ current: 1, size: 10, total: 0 });
|
|
const formDialog = reactive({ visible: false, title: "", mode: "add", readonly: false });
|
const form = reactive(createEmptyForm());
|
const formRef = ref();
|
|
const detailDialog = reactive({ visible: false });
|
const detailRow = ref({});
|
|
const filteredList = computed(() => {
|
let list = [...allRows.value];
|
const kw = (searchForm.keyword || "").trim().toLowerCase();
|
if (kw) {
|
list = list.filter((r) => (r.title || "").toLowerCase().includes(kw) || (r.noticeNo || "").toLowerCase().includes(kw));
|
}
|
if (searchForm.noticeType) list = list.filter((r) => r.noticeType === searchForm.noticeType);
|
if (searchForm.priority) list = list.filter((r) => r.priority === searchForm.priority);
|
if (searchForm.publishStatus) list = list.filter((r) => r.publishStatus === searchForm.publishStatus);
|
const range = searchForm.publishDateRange;
|
if (range?.length === 2 && range[0] && range[1]) {
|
const start = dayjs(range[0]).startOf("day");
|
const end = dayjs(range[1]).endOf("day");
|
list = list.filter((r) => {
|
if (!r.publishDate) return false;
|
const t = dayjs(r.publishDate);
|
return !t.isBefore(start) && !t.isAfter(end);
|
});
|
}
|
return list.sort((a, b) => (String(a.updateTime) < String(b.updateTime) ? 1 : -1));
|
});
|
|
watch(
|
filteredList,
|
(list) => {
|
page.total = list.length;
|
const maxPage = Math.max(1, Math.ceil(list.length / page.size) || 1);
|
if (page.current > maxPage) page.current = maxPage;
|
},
|
{ immediate: true }
|
);
|
|
const tableData = computed(() => {
|
const start = (page.current - 1) * page.size;
|
return filteredList.value.slice(start, start + page.size);
|
});
|
|
const formRules = {
|
title: [{ required: true, message: "请输入公告标题", trigger: "blur" }],
|
publishDate: [{ required: true, message: "请选择发布日期", trigger: "change" }],
|
noticeType: [{ required: true, message: "请选择公告类型", trigger: "change" }],
|
};
|
|
const tableColumn = ref([
|
{ label: "编号", prop: "noticeNo", width: 150 },
|
{ label: "标题", prop: "title", minWidth: 200, showOverflowTooltip: true },
|
{
|
label: "类型",
|
prop: "noticeType",
|
width: 100,
|
dataType: "slot",
|
slot: "noticeType",
|
},
|
{
|
label: "优先级",
|
prop: "priority",
|
width: 90,
|
dataType: "tag",
|
formatData: (v) => priorityLabel(v),
|
formatType: (v) => {
|
const hit = PRIORITY_OPTIONS.find((x) => x.value === v);
|
return hit?.tag || "info";
|
},
|
},
|
{
|
label: "状态",
|
prop: "publishStatus",
|
width: 90,
|
dataType: "tag",
|
formatData: (v, row) => (isExpired(row) && v === "published" ? "已过期" : publishStatusLabel(v)),
|
formatType: (v, row) => {
|
if (isExpired(row) && v === "published") return "";
|
const hit = PUBLISH_STATUS_OPTIONS.find((x) => x.value === v);
|
return hit?.tag || "info";
|
},
|
},
|
{ label: "发布日期", prop: "publishDate", width: 120 },
|
{ label: "发布人", prop: "publisherName", width: 110 },
|
{ label: "阅读量", prop: "readCount", width: 80, align: "center" },
|
{
|
dataType: "action",
|
label: "操作",
|
align: "center",
|
fixed: "right",
|
width: 220,
|
operation: [
|
{ name: "详情", type: "text", clickFun: (row) => openDetail(row) },
|
{
|
name: "修改",
|
type: "text",
|
disabled: (row) => row.publishStatus === "withdrawn",
|
clickFun: (row) => openFormDialog("edit", row),
|
},
|
{
|
name: "发布",
|
type: "text",
|
disabled: (row) => row.publishStatus === "published",
|
clickFun: (row) => publishNotice(row),
|
},
|
{
|
name: "撤回",
|
type: "text",
|
disabled: (row) => row.publishStatus !== "published",
|
clickFun: (row) => withdrawNotice(row),
|
},
|
{ name: "删除", type: "text", clickFun: (row) => deleteNotice(row) },
|
],
|
},
|
]);
|
|
function persist() {
|
saveStoredNotices(allRows.value);
|
}
|
|
function handleQuery() {
|
tableLoading.value = true;
|
page.current = 1;
|
setTimeout(() => {
|
tableLoading.value = false;
|
}, 200);
|
}
|
|
function resetSearch() {
|
searchForm.keyword = "";
|
searchForm.noticeType = "";
|
searchForm.priority = "";
|
searchForm.publishStatus = "";
|
searchForm.publishDateRange = [];
|
handleQuery();
|
}
|
|
function pagination({ page: p, limit }) {
|
page.current = p;
|
page.size = limit;
|
}
|
|
function resetForm(target = createEmptyForm()) {
|
Object.assign(form, createEmptyForm(), target);
|
}
|
|
function openFormDialog(mode, row) {
|
formDialog.mode = mode;
|
formDialog.readonly = mode === "view";
|
formDialog.title =
|
mode === "add" ? "添加公告" : mode === "edit" ? "修改公告" : "查看公告";
|
if (mode === "add") {
|
resetForm({ publisherName: "当前用户", priority: "normal" });
|
} else {
|
resetForm({
|
...JSON.parse(JSON.stringify(row)),
|
targetDeptIds: [...(row.targetDeptIds || [])],
|
});
|
}
|
formDialog.visible = true;
|
}
|
|
function openDetail(row) {
|
detailRow.value = { ...row };
|
detailDialog.visible = true;
|
}
|
|
function saveForm(publish = false) {
|
const v = validateNoticeForm(form);
|
if (!v.ok) return { ok: false, message: v.message };
|
|
const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
const payload = {
|
...JSON.parse(JSON.stringify(form)),
|
title: v.title,
|
updateTime: now,
|
};
|
|
if (form.noticeType === "emergency" && payload.priority === "normal") {
|
payload.priority = "urgent";
|
}
|
|
if (formDialog.mode === "add") {
|
payload.id = `notice_${Date.now()}`;
|
payload.noticeNo = nextNoticeNo();
|
payload.createTime = now;
|
payload.readCount = 0;
|
if (publish) {
|
payload.publishStatus = "published";
|
payload.publishTime = now;
|
} else {
|
payload.publishStatus = "draft";
|
}
|
allRows.value.unshift(payload);
|
} else {
|
const idx = allRows.value.findIndex((r) => r.id === form.id);
|
if (idx < 0) return { ok: false, message: "记录不存在" };
|
const prev = allRows.value[idx];
|
if (publish) {
|
payload.publishStatus = "published";
|
payload.publishTime = payload.publishTime || now;
|
}
|
allRows.value[idx] = { ...prev, ...payload };
|
}
|
persist();
|
formDialog.visible = false;
|
return { ok: true };
|
}
|
|
async function publishNotice(row) {
|
try {
|
await ElMessageBox.confirm(`确认发布「${row.title}」?`, "发布公告", {
|
type: "warning",
|
confirmButtonText: "发布",
|
cancelButtonText: "取消",
|
});
|
const hit = allRows.value.find((r) => r.id === row.id);
|
if (!hit) return;
|
const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
hit.publishStatus = "published";
|
hit.publishTime = now;
|
hit.updateTime = now;
|
if (hit.noticeType === "emergency") hit.priority = "urgent";
|
persist();
|
return true;
|
} catch {
|
return false;
|
}
|
}
|
|
async function withdrawNotice(row) {
|
try {
|
await ElMessageBox.confirm(`确认撤回「${row.title}」?撤回后员工端将不再展示。`, "撤回公告", {
|
type: "warning",
|
confirmButtonText: "撤回",
|
cancelButtonText: "取消",
|
});
|
const hit = allRows.value.find((r) => r.id === row.id);
|
if (!hit) return;
|
hit.publishStatus = "withdrawn";
|
hit.updateTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
persist();
|
return true;
|
} catch {
|
return false;
|
}
|
}
|
|
async function deleteNotice(row) {
|
try {
|
await ElMessageBox.confirm(`确认删除「${row.title}」?此操作不可恢复。`, "删除公告", {
|
type: "warning",
|
confirmButtonText: "删除",
|
cancelButtonText: "取消",
|
});
|
allRows.value = allRows.value.filter((r) => r.id !== row.id);
|
persist();
|
return true;
|
} catch {
|
return false;
|
}
|
}
|
|
return {
|
Search,
|
NOTICE_TYPE_OPTIONS,
|
PRIORITY_OPTIONS,
|
PUBLISH_STATUS_OPTIONS,
|
READ_SCOPE_OPTIONS,
|
DEPT_OPTIONS,
|
noticeTypeLabel,
|
searchForm,
|
tableLoading,
|
page,
|
tableData,
|
tableColumn,
|
formDialog,
|
form,
|
formRef,
|
formRules,
|
detailDialog,
|
detailRow,
|
isExpired,
|
handleQuery,
|
resetSearch,
|
pagination,
|
openFormDialog,
|
openDetail,
|
saveForm,
|
publishNotice,
|
withdrawNotice,
|
deleteNotice,
|
};
|
}
|