import dayjs from "dayjs"; /** 新闻分类:统一信息出口 */ export const NEWS_TYPE_OPTIONS = [ { value: "announcement", label: "企业公告", color: "#409eff" }, { value: "policy", label: "政策解读", color: "#e6a23c" }, { value: "industry", label: "行业动态", color: "#909399" }, { value: "culture", label: "文化活动", color: "#67c23a" }, ]; /** 发布状态 */ export const PUBLISH_STATUS_OPTIONS = [ { value: "draft", label: "草稿", tag: "info" }, { value: "pending_review", label: "待审核", tag: "warning" }, { value: "published", label: "已发布", tag: "success" }, { value: "archived", label: "已归档", tag: "" }, ]; /** 排版模板 */ export const LAYOUT_TEMPLATE_OPTIONS = [ { value: "standard", label: "标准图文" }, { value: "policy", label: "政策条文" }, { value: "gallery", label: "图集相册" }, { value: "briefing", label: "简报摘要" }, ]; /** 阅读可见范围 */ export const READ_SCOPE_OPTIONS = [ { value: "all", label: "全员可见" }, { value: "management", label: "管理层" }, { value: "department", label: "指定部门" }, { value: "custom", label: "自定义名单" }, ]; /** 编辑/审核角色(发布权限) */ export const PUBLISH_ROLE_OPTIONS = [ { value: "hr", label: "HR(人事政策)" }, { value: "admin", label: "管理员(外部新闻审核)" }, { value: "dept_manager", label: "部门负责人" }, { value: "editor", label: "内容编辑" }, ]; export const STORAGE_KEY = "oa_enterprise_news_v1"; /** 演示用目标受众(后期对接组织架构) */ export const MOCK_AUDIENCE = [ { userId: "u1", employeeNo: "zhangsan", name: "张三", deptName: "研发部", isManagement: false }, { userId: "u2", employeeNo: "lisi", name: "李四", deptName: "研发部", isManagement: false }, { userId: "u3", employeeNo: "wangwu", name: "王五", deptName: "行政部", isManagement: false }, { userId: "u4", employeeNo: "zhaoliu", name: "赵六", deptName: "销售部", isManagement: false }, { userId: "u5", employeeNo: "sunqi", name: "孙七", deptName: "财务部", isManagement: false }, { userId: "u6", employeeNo: "zhouba", name: "周八", deptName: "总经办", isManagement: true }, { userId: "u7", employeeNo: "wujiu", name: "吴九", deptName: "总经办", isManagement: true }, { userId: "u8", employeeNo: "zhengshi", name: "郑十", deptName: "人力资源部", isManagement: false }, ]; const DEPT_OPTIONS = [ { value: "101", label: "研发部" }, { value: "102", label: "销售部" }, { value: "103", label: "行政部" }, { value: "104", label: "财务部" }, { value: "105", label: "总经办" }, { value: "106", label: "人力资源部" }, ]; export { DEPT_OPTIONS }; export function newsTypeLabel(v) { return NEWS_TYPE_OPTIONS.find((x) => x.value === v)?.label || v || "—"; } export function newsTypeColor(v) { return NEWS_TYPE_OPTIONS.find((x) => x.value === v)?.color || "#909399"; } export function publishStatusLabel(v) { return PUBLISH_STATUS_OPTIONS.find((x) => x.value === v)?.label || v || "—"; } export function publishStatusTag(v) { return PUBLISH_STATUS_OPTIONS.find((x) => x.value === v)?.tag || "info"; } export function layoutTemplateLabel(v) { return LAYOUT_TEMPLATE_OPTIONS.find((x) => x.value === v)?.label || v || "—"; } export function readScopeLabel(v) { return READ_SCOPE_OPTIONS.find((x) => x.value === v)?.label || v || "—"; } export function publishRoleLabel(v) { return PUBLISH_ROLE_OPTIONS.find((x) => x.value === v)?.label || v || "—"; } export function createEmptyForm() { return { id: "", newsNo: "", title: "", summary: "", newsType: "announcement", layoutTemplate: "standard", contentHtml: "", coverImage: "", mediaList: [], attachmentList: [], editorRole: "hr", reviewerRole: "admin", readScope: "all", targetDeptIds: [], targetUserIds: [], publishStatus: "draft", publisherName: "", publishTime: "", readRecords: [], remindLogs: [], likes: [], comments: [], versions: [], versionNo: 1, requireReadConfirm: false, }; } function buildReadRecords(readUserIds = []) { const set = new Set(readUserIds); return MOCK_AUDIENCE.map((u) => ({ userId: u.userId, employeeNo: u.employeeNo, name: u.name, deptName: u.deptName, readAt: set.has(u.userId) ? dayjs().subtract(2, "day").format("YYYY-MM-DD HH:mm:ss") : "", lastRemindAt: "", })); } function createVersionSnapshot(row, changeNote = "发布") { return { versionNo: row.versionNo || 1, title: row.title, summary: row.summary, contentHtml: row.contentHtml, newsType: row.newsType, publishTime: row.publishTime || dayjs().format("YYYY-MM-DD HH:mm:ss"), archivedAt: dayjs().format("YYYY-MM-DD HH:mm:ss"), changeNote, publisherName: row.publisherName || "系统", }; } export function createInitialMockNews() { const policyContent = "

2026 年考勤管理制度(试行)

一、上班时间 9:00,弹性打卡窗口 8:30–9:30。

二、请假须提前在 OA 提交审批。

三、本制度自 2026-06-01 起执行。

"; const cultureContent = "

2026 企业年会圆满落幕!感谢每一位同事的参与,以下为精彩瞬间图集。

"; const strategyContent = "

2026 下半年战略方向(内部)

聚焦核心产品线升级与海外市场拓展,具体指标见附件。

"; const policyRow = { id: "news_1", newsNo: "EN202605150001", title: "关于发布新考勤制度的通知", summary: "请全体员工认真阅读并确认知悉,自 2026-06-01 起执行。", newsType: "policy", layoutTemplate: "policy", contentHtml: policyContent, coverImage: "", mediaList: [], attachmentList: [{ name: "考勤制度2026.pdf", url: "/mock/attendance-policy.pdf" }], editorRole: "hr", reviewerRole: "admin", readScope: "all", targetDeptIds: [], targetUserIds: [], publishStatus: "published", publisherName: "人力资源部", publishTime: "2026-05-15 10:00:00", readRecords: buildReadRecords(["u6", "u7", "u8"]), remindLogs: [], likes: [], comments: [], versions: [ { versionNo: 1, title: "关于发布新考勤制度的通知(征求意见稿)", summary: "征求意见稿", contentHtml: "

征求意见稿:上班时间 9:00……

", newsType: "policy", publishTime: "2026-05-10 09:00:00", archivedAt: "2026-05-15 09:55:00", changeNote: "定稿发布", publisherName: "人力资源部", }, ], versionNo: 2, requireReadConfirm: true, createTime: "2026-05-10 09:00:00", updateTime: "2026-05-15 10:00:00", }; const cultureRow = { id: "news_2", newsNo: "EN202605200002", title: "2026 企业年会精彩瞬间", summary: "年会图集上线,欢迎点赞留言,共建企业文化。", newsType: "culture", layoutTemplate: "gallery", contentHtml: cultureContent, coverImage: "/mock/annual-cover.jpg", mediaList: [ { type: "image", name: "开场.jpg", url: "/mock/annual-1.jpg" }, { type: "image", name: "颁奖.jpg", url: "/mock/annual-2.jpg" }, { type: "video", name: "年会花絮.mp4", url: "/mock/annual.mp4" }, ], attachmentList: [], editorRole: "dept_manager", reviewerRole: "admin", readScope: "all", targetDeptIds: [], targetUserIds: [], publishStatus: "published", publisherName: "行政部", publishTime: "2026-05-20 14:30:00", readRecords: buildReadRecords(["u1", "u2", "u3", "u4", "u5", "u6", "u7"]), remindLogs: [], likes: [ { userId: "u1", name: "张三", time: "2026-05-20 15:01:00" }, { userId: "u2", name: "李四", time: "2026-05-20 15:05:00" }, { userId: "u4", name: "赵六", time: "2026-05-20 16:20:00" }, ], comments: [ { id: "c1", userId: "u1", name: "张三", content: "节目太精彩了!", time: "2026-05-20 15:10:00" }, { id: "c2", userId: "u3", name: "王五", content: "期待明年再聚!", time: "2026-05-20 17:00:00" }, ], versions: [], versionNo: 1, requireReadConfirm: false, createTime: "2026-05-20 14:00:00", updateTime: "2026-05-20 14:30:00", }; const strategyRow = { id: "news_3", newsNo: "EN202605220003", title: "2026 下半年战略规划要点", summary: "仅限管理层阅读,请勿对外传播。", newsType: "announcement", layoutTemplate: "briefing", contentHtml: strategyContent, coverImage: "", mediaList: [], attachmentList: [{ name: "战略指标.pdf", url: "/mock/strategy.pdf" }], editorRole: "admin", reviewerRole: "admin", readScope: "management", targetDeptIds: [], targetUserIds: [], publishStatus: "published", publisherName: "总经办", publishTime: "2026-05-22 09:00:00", readRecords: buildReadRecords(["u6", "u7"]), remindLogs: [], likes: [], comments: [], versions: [], versionNo: 1, requireReadConfirm: false, createTime: "2026-05-22 08:30:00", updateTime: "2026-05-22 09:00:00", }; const industryDraft = { id: "news_4", newsNo: "EN202605250004", title: "制造业数字化转型趋势简报", summary: "行业动态草稿,待管理员审核后发布。", newsType: "industry", layoutTemplate: "standard", contentHtml: "

本期简报梳理工业互联网与 AI 质检应用案例……

", coverImage: "", mediaList: [], attachmentList: [], editorRole: "editor", reviewerRole: "admin", readScope: "all", targetDeptIds: [], targetUserIds: [], publishStatus: "pending_review", publisherName: "市场部", publishTime: "", readRecords: [], remindLogs: [], likes: [], comments: [], versions: [], versionNo: 1, requireReadConfirm: false, createTime: "2026-05-25 11:00:00", updateTime: "2026-05-25 11:00:00", }; return [policyRow, cultureRow, strategyRow, industryDraft]; } export function loadStoredNews() { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return null; const data = JSON.parse(raw); return Array.isArray(data) ? data : null; } catch { return null; } } export function saveStoredNews(rows) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(rows)); } catch { /* ignore */ } } /** 按阅读范围解析目标受众 */ export function resolveTargetAudience(row) { const scope = row.readScope || "all"; if (scope === "management") { return MOCK_AUDIENCE.filter((u) => u.isManagement); } if (scope === "department" && row.targetDeptIds?.length) { const names = DEPT_OPTIONS.filter((d) => row.targetDeptIds.includes(d.value)).map((d) => d.label); return MOCK_AUDIENCE.filter((u) => names.includes(u.deptName)); } if (scope === "custom" && row.targetUserIds?.length) { return MOCK_AUDIENCE.filter((u) => row.targetUserIds.includes(u.userId)); } return [...MOCK_AUDIENCE]; } export function getUnreadEmployees(row) { const audience = resolveTargetAudience(row); const readSet = new Set( (row.readRecords || []).filter((r) => r.readAt).map((r) => r.userId) ); return audience.filter((u) => !readSet.has(u.userId)); } export function readRate(row) { const audience = resolveTargetAudience(row); if (!audience.length) return 0; const readCount = (row.readRecords || []).filter((r) => r.readAt).length; return Math.round((readCount / audience.length) * 100); } export function nextNewsNo() { return `EN${dayjs().format("YYYYMMDD")}${String(Math.floor(Math.random() * 9000) + 1000)}`; } export function pushVersionBeforeUpdate(row, changeNote) { const versions = row.versions || []; versions.unshift(createVersionSnapshot(row, changeNote)); row.versions = versions; row.versionNo = (row.versionNo || 1) + 1; } export function validateNewsForm(form) { const title = (form.title || "").trim(); if (!title) return { ok: false, message: "请填写新闻标题" }; if (!form.newsType) return { ok: false, message: "请选择新闻分类" }; if (form.readScope === "department" && !(form.targetDeptIds || []).length) { return { ok: false, message: "请选择可见部门" }; } return { ok: true, title }; }