yyb
7 小时以前 a1df9699594b0a0e46d26a0394eafb1eb030c68b
src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsUtils.js
@@ -8,13 +8,36 @@
  { 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: "" },
  { value: "DRAFT", label: "草稿", tag: "info" },
  { value: "PENDING", label: "待审批", tag: "warning" },
  { value: "PUBLISHED", label: "已发布", tag: "success" },
  { value: "REJECTED", label: "驳回", tag: "danger" },
  { value: "OFFLINE", label: "已下线", tag: "info" },
];
/** 企业新闻列表筛选 */
export const ENTERPRISE_NEWS_STATUS_SEARCH_OPTIONS = [...PUBLISH_STATUS_OPTIONS];
const LEGACY_PUBLISH_STATUS_MAP = {
  draft: "DRAFT",
  pending_review: "PENDING",
  published: "PUBLISHED",
  archived: "OFFLINE",
};
/** 统一为后端状态枚举值 */
export function normalizeEnterpriseNewsStatus(v) {
  if (v == null || v === "") return "DRAFT";
  const upper = String(v).trim().toUpperCase();
  if (upper === "APPROVED") return "PUBLISHED";
  const hit = PUBLISH_STATUS_OPTIONS.find((x) => x.value === upper);
  if (hit) return hit.value;
  const legacy = LEGACY_PUBLISH_STATUS_MAP[String(v).trim().toLowerCase()];
  if (legacy) return legacy;
  return upper;
}
/** 排版模板 */
export const LAYOUT_TEMPLATE_OPTIONS = [
@@ -40,19 +63,8 @@
  { 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 },
];
/** 目标受众(对接组织架构 API 前为空) */
export const MOCK_AUDIENCE = [];
const DEPT_OPTIONS = [
  { value: "101", label: "研发部" },
@@ -74,11 +86,13 @@
}
export function publishStatusLabel(v) {
  return PUBLISH_STATUS_OPTIONS.find((x) => x.value === v)?.label || v || "—";
  const key = normalizeEnterpriseNewsStatus(v);
  return PUBLISH_STATUS_OPTIONS.find((x) => x.value === key)?.label || v || "—";
}
export function publishStatusTag(v) {
  return PUBLISH_STATUS_OPTIONS.find((x) => x.value === v)?.tag || "info";
  const key = normalizeEnterpriseNewsStatus(v);
  return PUBLISH_STATUS_OPTIONS.find((x) => x.value === key)?.tag || "info";
}
export function layoutTemplateLabel(v) {
@@ -110,7 +124,7 @@
    readScope: "all",
    targetDeptIds: [],
    targetUserIds: [],
    publishStatus: "draft",
    publishStatus: "DRAFT",
    publisherName: "",
    publishTime: "",
    readRecords: [],
@@ -120,206 +134,9 @@
    versions: [],
    versionNo: 1,
    requireReadConfirm: false,
    templateId: null,
    templateName: "",
  };
}
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 =
    "<p><strong>2026 年考勤管理制度(试行)</strong></p><p>一、上班时间 9:00,弹性打卡窗口 8:30–9:30。</p><p>二、请假须提前在 OA 提交审批。</p><p>三、本制度自 2026-06-01 起执行。</p>";
  const cultureContent =
    "<p>2026 企业年会圆满落幕!感谢每一位同事的参与,以下为精彩瞬间图集。</p>";
  const strategyContent =
    "<p><strong>2026 下半年战略方向(内部)</strong></p><p>聚焦核心产品线升级与海外市场拓展,具体指标见附件。</p>";
  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: "<p>征求意见稿:上班时间 9:00……</p>",
        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: "<p>本期简报梳理工业互联网与 AI 质检应用案例……</p>",
    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 */
  }
}
/** 按阅读范围解析目标受众 */
@@ -351,17 +168,6 @@
  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) {