From a1df9699594b0a0e46d26a0394eafb1eb030c68b Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期三, 20 五月 2026 17:42:09 +0800
Subject: [PATCH] 企业新闻

---
 src/views/officeProcessAutomation/HrManage/regular-apply/index.vue                                |    9 
 src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue                               |    6 
 src/api/officeProcessAutomation/enterpriseNews.js                                                 |   38 ++
 src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsUtils.js               |   43 ++
 src/views/officeProcessAutomation/EnterpriseNews/news-manage/components/NewsDetailPanel.vue       |    8 
 src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js |   41 ++
 src/views/officeProcessAutomation/EnterpriseNews/news-manage/index.vue                            |  298 +++++++++++-------
 src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js              |  122 ++++++
 src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsApprovalBridge.js      |  109 ------
 src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsMappers.js             |  224 ++++++++++++++
 src/views/officeProcessAutomation/EnterpriseNews/news-manage/useEnterpriseNewsList.js             |   55 +++
 11 files changed, 698 insertions(+), 255 deletions(-)

diff --git a/src/api/officeProcessAutomation/enterpriseNews.js b/src/api/officeProcessAutomation/enterpriseNews.js
new file mode 100644
index 0000000..52f345d
--- /dev/null
+++ b/src/api/officeProcessAutomation/enterpriseNews.js
@@ -0,0 +1,38 @@
+import request from "@/utils/request";
+
+/** 鍒嗛〉鏌ヨ浼佷笟鏂伴椈 */
+export function listEnterpriseNewsPage(params) {
+  return request({
+    url: "/enterpriseNews/listPage",
+    method: "get",
+    params,
+  });
+}
+
+/** 鏂板浼佷笟鏂伴椈 */
+export function saveEnterpriseNews(enterpriseNewsDto) {
+  return request({
+    url: "/enterpriseNews/save",
+    method: "post",
+    data: enterpriseNewsDto,
+  });
+}
+
+/** 淇敼浼佷笟鏂伴椈 */
+export function updateEnterpriseNews(enterpriseNewsDto) {
+  return request({
+    url: "/enterpriseNews/update",
+    method: "put",
+    data: enterpriseNewsDto,
+  });
+}
+
+/** 鍒犻櫎浼佷笟鏂伴椈锛坆ody 涓� ID 鏁扮粍锛� */
+export function deleteEnterpriseNews(ids) {
+  const idList = (Array.isArray(ids) ? ids : [ids]).filter((id) => id != null && id !== "");
+  return request({
+    url: "/enterpriseNews/delete",
+    method: "delete",
+    data: idList,
+  });
+}
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
index c16dcd8..4aa3c61 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
@@ -33,6 +33,7 @@
 
 /** 鍒楄〃鏌ヨ锛氬鎵圭姸鎬侊紙涓庡悗绔� status 鏋氫妇涓�鑷达級 */
 export const APPROVAL_STATUS_SEARCH_OPTIONS = [
+  { value: "DRAFT", label: "鑽夌" },
   { value: "PENDING", label: "寰呭鎵�" },
   { value: "APPROVED", label: "宸查�氳繃" },
   { value: "REJECTED", label: "宸查┏鍥�" },
@@ -40,34 +41,105 @@
 
 /**
  * 瀹℃壒鐘舵�佸睍绀猴紙涓庡悗绔� status 鏋氫妇涓�鑷达級
- * PENDING 鈫� 寰呭鎵�/杩涜涓�  APPROVED 鈫� 宸查�氳繃/宸插畬鎴�  REJECTED 鈫� 宸查┏鍥�
+ * DRAFT鈫掕崏绋� PENDING鈫掑緟瀹℃壒/杩涜涓� APPROVED鈫掑凡閫氳繃/宸插畬鎴� REJECTED鈫掑凡椹冲洖
  */
 export const APPROVAL_STATUS_OPTIONS = [
+  { value: "draft", api: "DRAFT", label: "鑽夌" },
   { value: "pending", api: "PENDING", label: "寰呭鎵�" },
   { value: "approved", api: "APPROVED", label: "宸查�氳繃" },
   { value: "rejected", api: "REJECTED", label: "宸查┏鍥�" },
   { value: "cancelled", api: "CANCELLED", label: "宸叉挙閿�" },
 ];
 
+/** 鏁板瓧鐘舵�佺爜锛堥儴鍒嗗悗绔敤 0/1/2锛� */
+const STATUS_NUMERIC_MAP = {
+  0: "pending",
+  1: "approved",
+  2: "rejected",
+  3: "cancelled",
+  4: "cancelled",
+};
+
 /** 鍚庣 status / 椤甸潰 approvalStatus 鈫� 缁熶竴椤甸潰 key锛坧ending | approved | rejected | cancelled锛� */
 export function normalizeApprovalStatusKey(v) {
-  const s = String(v ?? "").trim();
+  if (v == null || v === "") return "pending";
+  if (typeof v === "number" || (typeof v === "string" && /^\d+$/.test(v.trim()))) {
+    const numKey = STATUS_NUMERIC_MAP[Number(v)];
+    if (numKey) return numKey;
+  }
+  const s = String(v).trim();
   if (!s) return "pending";
   const upper = s.toUpperCase();
-  if (upper === "APPROVED" || upper === "APPROVE" || upper === "PASS") return "approved";
-  if (upper === "REJECTED" || upper === "REJECT" || upper === "REFUSE") return "rejected";
-  if (upper === "CANCELLED" || upper === "CANCEL") return "cancelled";
+  if (upper === "DRAFT") return "draft";
+  if (upper === "PUBLISHED") return "approved";
+  if (upper === "OFFLINE") return "cancelled";
+  if (upper === "APPROVED" || upper === "APPROVE" || upper === "PASS" || upper === "AGREE") {
+    return "approved";
+  }
+  if (
+    upper === "REJECTED" ||
+    upper === "REJECT" ||
+    upper === "REFUSE" ||
+    upper === "REFUSED" ||
+    upper === "DENIED"
+  ) {
+    return "rejected";
+  }
+  if (upper === "CANCELLED" || upper === "CANCEL" || upper === "REVOKED") return "cancelled";
   if (
     upper === "PENDING" ||
     upper === "IN_PROGRESS" ||
     upper === "PROCESSING" ||
-    upper === "RUNNING"
+    upper === "RUNNING" ||
+    upper === "WAIT" ||
+    upper === "WAITING"
   ) {
     return "pending";
   }
+  if (s.includes("鑽夌")) return "draft";
+  if (s.includes("椹冲洖") || s.includes("鎷掔粷")) return "rejected";
+  if (s.includes("涓嬬嚎")) return "cancelled";
+  if (s.includes("鎾ら攢")) return "cancelled";
+  if (s.includes("鍙戝竷") || s.includes("閫氳繃") || s.includes("瀹屾垚")) return "approved";
+  if (s.includes("寰呭") || s.includes("杩涜涓�") || s.includes("瀹℃壒涓�")) return "pending";
   const lower = s.toLowerCase();
-  if (["pending", "approved", "rejected", "cancelled"].includes(lower)) return lower;
+  if (["draft", "pending", "approved", "rejected", "cancelled"].includes(lower)) return lower;
   return "pending";
+}
+
+/** 浠庡垪琛�/璇︽儏琛岃В鏋愬悗绔師濮嬬姸鎬侊紙鍏煎澶氬瓧娈靛懡鍚嶏級 */
+export function resolveInstanceStatusRaw(row) {
+  if (!row || typeof row !== "object") return "";
+  const candidates = [
+    row.status,
+    row.statusRaw,
+    row.approvalStatus,
+    row.statusName,
+    row.statusLabel,
+    row.approvalStatusName,
+    row.statusDesc,
+    row.instanceStatus,
+    row.approvalInstanceStatus,
+    row.approveStatus,
+    row.auditStatus,
+    row.approvalInstance?.status,
+    row.approvalInstanceVo?.status,
+  ];
+  for (const c of candidates) {
+    if (c != null && c !== "") return c;
+  }
+  const tasks = row.tasks;
+  if (Array.isArray(tasks) && tasks.length) {
+    const rejected = tasks.some((t) =>
+      normalizeApprovalStatusKey(t?.status ?? t?.taskStatus) === "rejected"
+    );
+    if (rejected) return "REJECTED";
+    const allApproved = tasks.every((t) =>
+      normalizeApprovalStatusKey(t?.status ?? t?.taskStatus) === "approved"
+    );
+    if (allApproved) return "APPROVED";
+  }
+  return "";
 }
 
 /** 鎻愪氦寮圭獥锛氭ā鏉垮崱鐗囷紙鏉ヨ嚜鍚庣鍒楄〃锛� */
@@ -322,12 +394,15 @@
     dto.id = existingRow?.id ?? submitForm?.instanceId;
     dto.instanceNo = existingRow?.instanceNo ?? submitForm?.instanceNo ?? "";
     dto.status =
-      existingRow?.statusRaw || mapInstanceStatusToApi(existingRow?.approvalStatus) || "PENDING";
+      submitForm?.saveStatusApi ||
+      existingRow?.statusRaw ||
+      mapInstanceStatusToApi(existingRow?.approvalStatus) ||
+      "PENDING";
     dto.currentLevel = existingRow?.currentLevel ?? submitForm?.currentLevel ?? 1;
     dto.applicantId = existingRow?.applicantId ?? existingRow?.applicantNo;
     dto.applicantName = existingRow?.applicantName || "";
   } else {
-    dto.status = "PENDING";
+    dto.status = submitForm?.saveStatusApi || "PENDING";
     dto.currentLevel = 1;
     dto.applicantId = userStore?.id;
     dto.applicantName = userStore?.nickName || userStore?.name || "";
@@ -352,6 +427,12 @@
   return normalizeApprovalStatusKey(status);
 }
 
+/** 鍒楄〃/璇︽儏琛� 鈫� 椤甸潰 approvalStatus key */
+export function mapInstanceApprovalStatusFromRow(row) {
+  const raw = resolveInstanceStatusRaw(row);
+  return normalizeApprovalStatusKey(raw);
+}
+
 /** 椤甸潰 approvalStatus 鈫� 鍚庣 status */
 export function mapInstanceStatusToApi(approvalStatus) {
   const key = normalizeApprovalStatusKey(approvalStatus);
@@ -370,7 +451,8 @@
 /** 鍒嗛〉鍒楄〃椤� 鈫� 琛ㄦ牸琛� */
 export function mapInstanceFromApi(row) {
   if (!row) return {};
-  const approvalStatus = mapInstanceStatusFromApi(row.status);
+  const statusRaw = resolveInstanceStatusRaw(row);
+  const approvalStatus = normalizeApprovalStatusKey(statusRaw);
   const createTime = formatDisplayTime(row.createTime ?? row.applyTime ?? "");
   const applyTime = formatDisplayTime(row.applyTime ?? "");
   const finishTime = formatDisplayTime(row.finishTime ?? "");
@@ -397,7 +479,7 @@
     unread: Boolean(row.isApprove) && approvalStatus === "pending",
     isApprove: Boolean(row.isApprove),
     approvalStatus,
-    statusRaw: row.status,
+    statusRaw: statusRaw || row.status,
     createTime,
     applyTime: applyTime === "鈥�" ? "" : applyTime,
     finishTime: finishTime === "鈥�" ? "" : finishTime,
@@ -439,10 +521,16 @@
 }
 
 export function buildApprovalInstanceListParams({ page, searchForm, businessType, extraParams }) {
+  const extra = { ...(extraParams && typeof extraParams === "object" ? extraParams : {}) };
+  if (extra.createTime != null && extra.createTimeStart == null) {
+    extra.createTimeStart = extra.createTime;
+  }
+  delete extra.createTime;
+
   const params = {
     current: page.current,
     size: page.size,
-    ...(extraParams && typeof extraParams === "object" ? extraParams : {}),
+    ...extra,
   };
   const bizType = businessType ?? searchForm?.businessType;
   if (bizType != null && bizType !== "") {
@@ -451,9 +539,12 @@
   if (searchForm?.status) {
     params.status = searchForm.status;
   }
-  const range = searchForm?.createTimeRange;
+  const range =
+    searchForm?.createTimeRange ??
+    searchForm?.applyDateRange ??
+    searchForm?.transferDateRange;
   if (Array.isArray(range) && range[0]) {
-    params.createTime = range[0];
+    params.createTimeStart = range[0];
   }
   if (Array.isArray(range) && range[1]) {
     params.createTimeEnd = range[1];
@@ -483,6 +574,7 @@
 /** 涓氬姟鐢宠椤电姸鎬佹枃妗堬細PENDING鈫掕繘琛屼腑 APPROVED鈫掑凡瀹屾垚 REJECTED鈫掑凡椹冲洖 */
 export function businessApprovalStatusLabel(v) {
   const key = normalizeApprovalStatusKey(v);
+  if (key === "draft") return "鑽夌";
   if (key === "pending") return "杩涜涓�";
   if (key === "approved") return "宸插畬鎴�";
   if (key === "rejected") return "宸查┏鍥�";
@@ -503,6 +595,7 @@
 
 export function businessApprovalStatusTagType(v) {
   const key = normalizeApprovalStatusKey(v);
+  if (key === "draft") return "info";
   if (key === "approved") return "success";
   if (key === "rejected") return "danger";
   if (key === "cancelled") return "info";
@@ -511,6 +604,7 @@
 
 export function approvalStatusTagType(v) {
   const key = normalizeApprovalStatusKey(v);
+  if (key === "draft") return "info";
   if (key === "approved") return "success";
   if (key === "rejected") return "danger";
   if (key === "cancelled") return "info";
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
index 895ff0d..fef178f 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
@@ -6,8 +6,41 @@
   resolveInstanceFormFields,
 } from "../approve-list/approveListConstants.js";
 
-/** 鍒楄〃/璇︽儏涓嶅洖鏄句负鐙珛鍒楃殑濉姤椤� key */
-const DEFAULT_EXCLUDE_KEYS = new Set(["summary"]);
+/** 鍒楄〃/璇︽儏涓嶅洖鏄句负鐙珛鍒楃殑濉姤椤� key锛堥伩鍏嶈鐩栧疄渚嬬郴缁熷瓧娈碉級 */
+const DEFAULT_EXCLUDE_KEYS = new Set([
+  "summary",
+  "status",
+  "approvalStatus",
+  "approvalstatus",
+  "instanceStatus",
+  "publishStatus",
+  "newsStatus",
+]);
+
+/** enrich 鍚庡繀椤讳繚鐣欑殑瀹炰緥瀛楁锛堜笉琚� formConfig 閾哄钩瑕嗙洊锛� */
+const PRESERVE_INSTANCE_FIELDS = [
+  "id",
+  "approvalStatus",
+  "statusRaw",
+  "status",
+  "instanceNo",
+  "templateId",
+  "templateName",
+  "businessType",
+  "businessId",
+  "businessName",
+  "applicantId",
+  "applicantNo",
+  "applicantName",
+  "createTime",
+  "applyTime",
+  "finishTime",
+  "title",
+  "isApprove",
+  "unread",
+  "currentLevel",
+  "newsStatus",
+];
 
 /**
  * 浠庤鏁版嵁 formConfig 瑙f瀽瀛楁瀹氫箟涓庡~鎶ュ�硷紝骞堕摵骞冲埌琛屼笂渚涗富琛� prop 缁戝畾锛堝睍绀虹敤鏍煎紡鍖栧�硷級
@@ -41,6 +74,10 @@
     displayRow[f.key] = text;
   }
 
+  for (const key of PRESERVE_INSTANCE_FIELDS) {
+    if (row[key] !== undefined) displayRow[key] = row[key];
+  }
+
   return displayRow;
 }
 
diff --git a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/components/NewsDetailPanel.vue b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/components/NewsDetailPanel.vue
index bb25ba0..1124472 100644
--- a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/components/NewsDetailPanel.vue
+++ b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/components/NewsDetailPanel.vue
@@ -2,9 +2,9 @@
 <template>
   <el-descriptions :column="2" border>
     <el-descriptions-item label="鏂伴椈缂栧彿">{{ row.newsNo || "鈥�" }}</el-descriptions-item>
-    <el-descriptions-item label="鍙戝竷鐘舵��">
-      <el-tag :type="publishStatusTag(row.publishStatus)" size="small">
-        {{ publishStatusLabel(row.publishStatus) }}
+    <el-descriptions-item label="鐘舵��">
+      <el-tag :type="publishStatusTag(row.newsStatus ?? row.publishStatus)" size="small">
+        {{ publishStatusLabel(row.newsStatus ?? row.publishStatus) }}
       </el-tag>
     </el-descriptions-item>
     <el-descriptions-item label="鏂伴椈鍒嗙被">
@@ -57,7 +57,7 @@
   </template>
   <el-empty v-else description="鏆傛棤闄勪欢" :image-size="48" />
 
-  <template v-if="row.newsType === 'culture' && row.publishStatus === 'published'">
+  <template v-if="row.newsType === 'culture' && (row.publishStatus === 'PUBLISHED' || row.publishStatus === 'published')">
     <el-divider content-position="left">浜掑姩锛堢偣璧� {{ likeCount }} 路 璇勮 {{ commentCount }}锛�</el-divider>
     <div class="interaction-bar">
       <el-button type="primary" plain size="small" @click="$emit('like')">
diff --git a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsApprovalBridge.js b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsApprovalBridge.js
index f61d9a9..b870ab7 100644
--- a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsApprovalBridge.js
+++ b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsApprovalBridge.js
@@ -1,98 +1,11 @@
-import {
-  createEmptyForm,
-  publishStatusLabel,
-  PUBLISH_STATUS_OPTIONS,
-} from "./enterpriseNewsUtils.js";
-import { normalizeApprovalStatusKey } from "../../ApproveManage/approve-list/approveListConstants.js";
-
-/** formPayload 涓瓨鏀惧畬鏁翠紒涓氭柊闂讳笟鍔℃暟鎹殑閿� */
-export const ENTERPRISE_NEWS_PAYLOAD_KEY = "enterpriseNews";
-
-export function extractEnterpriseNewsFromRow(row) {
-  const payload = row?.formPayload || {};
-  const raw = payload[ENTERPRISE_NEWS_PAYLOAD_KEY];
-  if (raw && typeof raw === "object") {
-    return { ...createEmptyForm(), ...raw };
-  }
-  return {
-    ...createEmptyForm(),
-    title: payload.title || row?.title || "",
-    summary: payload.summary || "",
-    newsType: payload.newsType || "announcement",
-    contentHtml: payload.contentHtml || "",
-  };
-}
-
-/** 鍒楄〃琛屽寮猴細涓昏〃灞曠ず鏂伴椈瀛楁 */
-export function enrichEnterpriseNewsListRow(row) {
-  const news = extractEnterpriseNewsFromRow(row);
-  const publishStatus =
-    news.publishStatus || mapApprovalStatusToPublishStatus(row?.approvalStatus);
-  return {
-    ...row,
-    newsNo: news.newsNo || row.instanceNo || "鈥�",
-    title: news.title || row.title || "鈥�",
-    summary: news.summary,
-    newsType: news.newsType,
-    publisherName: news.publisherName || row.applicantName || "鈥�",
-    publishTime: news.publishTime || row.createTime || "",
-    updateTime: news.updateTime || row.createTime || "",
-    publishStatus,
-    _news: news,
-  };
-}
-
-function mapApprovalStatusToPublishStatus(approvalStatus) {
-  const key = normalizeApprovalStatusKey(approvalStatus);
-  if (key === "approved") return "published";
-  if (key === "pending") return "pending_review";
-  if (key === "rejected") return "draft";
-  return "draft";
-}
-
-/** 浼佷笟鏂伴椈琛ㄥ崟 鈫� 瀹℃壒瀹炰緥 formPayload */
-export function syncNewsFormToSubmitPayload(newsForm, submitForm) {
-  const snapshot = JSON.parse(JSON.stringify(newsForm));
-  submitForm.formPayload = {
-    ...(submitForm.formPayload || {}),
-    [ENTERPRISE_NEWS_PAYLOAD_KEY]: snapshot,
-    title: snapshot.title,
-    summary: snapshot.summary,
-  };
-}
-
-export function buildEnterpriseNewsTableColumns(buildTableActions) {
-  return [
-    { label: "缂栧彿", prop: "newsNo", width: 150 },
-    { label: "鏍囬", prop: "title", minWidth: 180, showOverflowTooltip: true },
-    {
-      label: "鍒嗙被",
-      prop: "newsType",
-      width: 100,
-      dataType: "slot",
-      slot: "newsType",
-    },
-    {
-      label: "鐘舵��",
-      prop: "publishStatus",
-      width: 90,
-      dataType: "tag",
-      formatData: (v) => publishStatusLabel(v),
-      formatType: (v) => {
-        const hit = PUBLISH_STATUS_OPTIONS.find((x) => x.value === v);
-        return hit?.tag || "info";
-      },
-    },
-    { label: "鍙戝竷浜�", prop: "publisherName", width: 110 },
-    { label: "鍙戝竷鏃堕棿", prop: "publishTime", width: 170 },
-    { label: "鏇存柊鏃堕棿", prop: "updateTime", width: 170 },
-    {
-      dataType: "action",
-      label: "鎿嶄綔",
-      align: "center",
-      fixed: "right",
-      width: 220,
-      operation: buildTableActions(),
-    },
-  ];
-}
+/** @deprecated 璇蜂娇鐢� enterpriseNewsMappers.js */
+export {
+  ENTERPRISE_NEWS_PAYLOAD_KEY,
+  buildEnterpriseNewsSaveDto,
+  buildEnterpriseNewsTableColumns,
+  canEditEnterpriseNewsRow,
+  extractEnterpriseNewsFromRow,
+  mapApiRowToNewsForm,
+  mapEnterpriseNewsFromApi,
+  syncNewsFormToSubmitPayload,
+} from "./enterpriseNewsMappers.js";
diff --git a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsMappers.js b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsMappers.js
new file mode 100644
index 0000000..5ef1a19
--- /dev/null
+++ b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsMappers.js
@@ -0,0 +1,224 @@
+import {
+  createEmptyForm,
+  normalizeEnterpriseNewsStatus,
+  publishStatusLabel,
+  PUBLISH_STATUS_OPTIONS,
+} from "./enterpriseNewsUtils.js";
+
+/** formPayload 涓瓨鏀惧畬鏁翠紒涓氭柊闂讳笟鍔℃暟鎹殑閿紙瀹℃壒瀹炰緥淇濆瓨鐢級 */
+export const ENTERPRISE_NEWS_PAYLOAD_KEY = "enterpriseNews";
+
+const READ_SCOPE_FROM_API = {
+  all: "all",
+  dept: "department",
+  department: "department",
+  custom: "custom",
+  management: "management",
+};
+
+const READ_SCOPE_TO_API = {
+  all: "all",
+  department: "dept",
+  dept: "dept",
+  custom: "custom",
+  management: "all",
+};
+
+export function mapReadScopeFromApi(scope) {
+  const key = String(scope ?? "").trim().toLowerCase();
+  return READ_SCOPE_FROM_API[key] || key || "all";
+}
+
+export function mapReadScopeToApi(scope) {
+  return READ_SCOPE_TO_API[scope] || scope || "all";
+}
+
+export function unwrapEnterpriseNewsPage(res) {
+  const data = res?.data ?? res;
+  if (!data || typeof data !== "object") {
+    return { records: [], total: 0 };
+  }
+  if (Array.isArray(data.records)) {
+    return { records: data.records, total: Number(data.total ?? 0) };
+  }
+  const nested = data.data;
+  if (nested && typeof nested === "object" && Array.isArray(nested.records)) {
+    return { records: nested.records, total: Number(nested.total ?? 0) };
+  }
+  return { records: [], total: 0 };
+}
+
+/** 缁勮 listPage 鏌ヨ鍙傛暟 */
+export function buildEnterpriseNewsListParams({ page, searchForm }) {
+  const params = {
+    current: page.current,
+    size: page.size,
+  };
+  const kw = (searchForm?.keyword || "").trim();
+  if (kw) params.title = kw;
+  if (searchForm?.newsType) params.category = searchForm.newsType;
+  if (searchForm?.status) params.status = searchForm.status;
+  const range = searchForm?.createTimeRange;
+  if (Array.isArray(range) && range[0]) {
+    params.createTimeStart = range[0];
+  }
+  if (Array.isArray(range) && range[1]) {
+    params.createTimeEnd = range[1];
+  }
+  return params;
+}
+
+/** 鎺ュ彛 EnterpriseNewsVo 鈫� 鍒楄〃琛� */
+export function mapEnterpriseNewsFromApi(row) {
+  if (!row) return {};
+  const newsStatus = normalizeEnterpriseNewsStatus(row.status);
+  return {
+    ...row,
+    newsNo: row.id != null ? String(row.id) : "鈥�",
+    newsType: row.category || "",
+    contentHtml: row.content || "",
+    publisherName: row.createUserName || "鈥�",
+    publishTime: row.createTime || "",
+    updateTime: row.updateTime || "",
+    newsStatus,
+    requireReadConfirm: row.isRequired === "1" || row.isRequired === 1,
+    readScope: mapReadScopeFromApi(row.readScope),
+    readCount: row.readCount ?? 0,
+    requiredReadCount: row.requiredReadCount ?? 0,
+  };
+}
+
+/** 鏄惁鍏佽淇敼锛堣崏绋裤�侀┏鍥炲彲鏀癸級 */
+export function canEditEnterpriseNewsRow(row) {
+  const status = normalizeEnterpriseNewsStatus(row?.newsStatus ?? row?.status);
+  return status === "DRAFT" || status === "REJECTED";
+}
+
+/** 鎺ュ彛琛� / 璇︽儏 鈫� 琛ㄥ崟 */
+export function mapApiRowToNewsForm(row) {
+  if (!row) return createEmptyForm();
+  return {
+    ...createEmptyForm(),
+    id: row.id != null ? String(row.id) : "",
+    newsNo: row.id != null ? String(row.id) : "",
+    title: row.title || "",
+    summary: row.summary || "",
+    contentHtml: row.content || row.contentHtml || "",
+    newsType: row.newsType || row.category || "announcement",
+    readScope: mapReadScopeFromApi(row.readScope),
+    requireReadConfirm: Boolean(row.requireReadConfirm ?? row.isRequired === "1"),
+    publisherName: row.createUserName || row.publisherName || "",
+    publishStatus: normalizeEnterpriseNewsStatus(row.newsStatus ?? row.status),
+    templateId: row.templateId,
+    templateName: row.templateName || "",
+    targetDeptIds: [...(row.deptIds || row.targetDeptIds || [])],
+    targetUserIds: [...(row.userIds || row.targetUserIds || [])],
+  };
+}
+
+/** 瀹℃壒瀹炰緥琛� formPayload 鈫� 琛ㄥ崟锛堝吋瀹规棫鏁版嵁锛� */
+export function extractEnterpriseNewsFromRow(row) {
+  if (!row?.formPayload && !row?.formFieldDefs && !row?.instanceNo) {
+    return mapApiRowToNewsForm(row);
+  }
+  const payload = row?.formPayload || {};
+  const raw = payload[ENTERPRISE_NEWS_PAYLOAD_KEY];
+  if (raw && typeof raw === "object") {
+    return { ...createEmptyForm(), ...raw };
+  }
+  return {
+    ...createEmptyForm(),
+    title: payload.title || row?.title || "",
+    summary: payload.summary || "",
+    newsType: payload.newsType || row?.category || "announcement",
+    contentHtml: payload.contentHtml || row?.content || "",
+  };
+}
+
+export function syncNewsFormToSubmitPayload(newsForm, submitForm) {
+  const snapshot = JSON.parse(JSON.stringify(newsForm));
+  submitForm.formPayload = {
+    ...(submitForm.formPayload || {}),
+    [ENTERPRISE_NEWS_PAYLOAD_KEY]: snapshot,
+    title: snapshot.title,
+    summary: snapshot.summary,
+  };
+}
+
+function toIdList(ids) {
+  if (!Array.isArray(ids) || !ids.length) return undefined;
+  const list = ids
+    .map((id) => (typeof id === "number" ? id : Number(id)))
+    .filter((n) => !Number.isNaN(n));
+  return list.length ? list : undefined;
+}
+
+/** 琛ㄥ崟 鈫� POST /enterpriseNews/save 璇锋眰浣� */
+export function buildEnterpriseNewsSaveDto(newsForm, { status } = {}) {
+  const dto = {
+    title: (newsForm.title || "").trim(),
+    summary: newsForm.summary || "",
+    content: newsForm.contentHtml || "",
+    category: newsForm.newsType || "",
+    readScope: mapReadScopeToApi(newsForm.readScope),
+    isRequired: newsForm.requireReadConfirm ? "1" : "0",
+    status: normalizeEnterpriseNewsStatus(status ?? newsForm.publishStatus),
+  };
+
+  const rawId = newsForm.id;
+  if (rawId != null && rawId !== "") {
+    const id = Number(rawId);
+    if (!Number.isNaN(id)) dto.id = id;
+  }
+
+  const deptIds = toIdList(newsForm.targetDeptIds);
+  if (deptIds) dto.deptIds = deptIds;
+
+  const userIds = toIdList(newsForm.targetUserIds);
+  if (userIds) dto.userIds = userIds;
+
+  const templateId = newsForm.templateId;
+  if (templateId != null && templateId !== "") {
+    const tid = Number(templateId);
+    if (!Number.isNaN(tid)) dto.templateId = tid;
+  }
+  if (newsForm.templateName) dto.templateName = newsForm.templateName;
+
+  return dto;
+}
+
+export function buildEnterpriseNewsTableColumns(buildTableActions) {
+  return [
+    { label: "缂栧彿", prop: "newsNo", width: 120 },
+    { label: "鏍囬", prop: "title", minWidth: 180, showOverflowTooltip: true },
+    {
+      label: "鍒嗙被",
+      prop: "newsType",
+      width: 100,
+      dataType: "slot",
+      slot: "newsType",
+    },
+    {
+      label: "鐘舵��",
+      prop: "newsStatus",
+      width: 100,
+      dataType: "tag",
+      formatData: (v) => publishStatusLabel(v),
+      formatType: (v) => {
+        const hit = PUBLISH_STATUS_OPTIONS.find((x) => x.value === v);
+        return hit?.tag || "info";
+      },
+    },
+    { label: "鍒涘缓浜�", prop: "publisherName", width: 110 },
+    { label: "鍒涘缓鏃堕棿", prop: "createTime", width: 170 },
+    { label: "鏇存柊鏃堕棿", prop: "updateTime", width: 170 },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 220,
+      operation: buildTableActions(),
+    },
+  ];
+}
diff --git a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsUtils.js b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsUtils.js
index 4ba3085..71ceaba 100644
--- a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/enterpriseNewsUtils.js
+++ b/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 = [
@@ -63,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) {
@@ -99,7 +124,7 @@
     readScope: "all",
     targetDeptIds: [],
     targetUserIds: [],
-    publishStatus: "draft",
+    publishStatus: "DRAFT",
     publisherName: "",
     publishTime: "",
     readRecords: [],
@@ -109,6 +134,8 @@
     versions: [],
     versionNo: 1,
     requireReadConfirm: false,
+    templateId: null,
+    templateName: "",
   };
 }
 
diff --git a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/index.vue b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/index.vue
index f444153..13decff 100644
--- a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/index.vue
+++ b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/index.vue
@@ -1,4 +1,4 @@
-<!--OA妯″潡锛欵nterpriseNews 浼佷笟鏂伴椈锛堝垪琛ㄨ蛋瀹℃壒瀹炰緥锛屾柊澧�/淇敼淇濈暀鍘熻〃鍗� + 妯℃澘瀹℃壒娴佺▼锛�-->
+<!--OA妯″潡锛欵nterpriseNews 浼佷笟鏂伴椈锛坙istPage|save|update|delete锛屾柊寤轰繚鐣欏鎵规ā鏉匡級-->
 <template>
   <div class="app-container enterprise-news-page">
     <div class="search_form mb20">
@@ -7,7 +7,7 @@
         <el-input
           v-model="searchForm.keyword"
           style="width: 200px"
-          placeholder="鏍囬 / 缂栧彿 / 鎽樿"
+          placeholder="鏍囬"
           clearable
           :prefix-icon="Search"
           @keyup.enter="onSearch"
@@ -16,16 +16,16 @@
         <el-select v-model="searchForm.newsType" placeholder="鍏ㄩ儴" clearable style="width: 140px">
           <el-option v-for="opt in NEWS_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
         </el-select>
-        <span class="search_title" style="margin-left: 12px">瀹℃壒鐘舵�侊細</span>
+        <span class="search_title" style="margin-left: 12px">鐘舵�侊細</span>
         <el-select v-model="searchForm.status" placeholder="鍏ㄩ儴" clearable style="width: 120px">
           <el-option
-            v-for="opt in APPROVAL_STATUS_SEARCH_OPTIONS"
+            v-for="opt in ENTERPRISE_NEWS_STATUS_SEARCH_OPTIONS"
             :key="opt.value"
             :label="opt.label"
             :value="opt.value"
           />
         </el-select>
-        <span class="search_title" style="margin-left: 12px">鐢宠鏃ユ湡锛�</span>
+        <span class="search_title" style="margin-left: 12px">鍒涘缓鏃堕棿锛�</span>
         <el-date-picker
           v-model="searchForm.createTimeRange"
           type="daterange"
@@ -72,7 +72,6 @@
       @closed="onTemplateBindClosed"
     />
 
-    <!-- 鏂板缓 / 缂栬緫锛氬師浼佷笟鏂伴椈琛ㄥ崟 + 妯℃澘瀹℃壒娴佺▼ -->
     <el-dialog
       v-model="newsFormDialog.visible"
       :title="newsFormDialog.title"
@@ -171,45 +170,41 @@
         <el-form-item label="鏀跨瓥绫诲繀璇�">
           <el-switch v-model="newsForm.requireReadConfirm" active-text="闇�闃呰纭锛堜究浜庣粺璁℃湭璇伙級" />
         </el-form-item>
-        <el-form-item label="鍙戝竷浜�">
-          <el-input v-model="newsForm.publisherName" placeholder="濡傦細浜哄姏璧勬簮閮�" maxlength="50" />
-        </el-form-item>
 
-        <template v-if="activeTemplate">
+        <template v-if="hasApprovalTemplate">
           <el-divider content-position="left">瀹℃壒娴佺▼</el-divider>
           <el-form-item label="瀹℃壒妯℃澘">
-            <span class="template-name">{{ activeTemplate.label || submitForm.templateName }}</span>
+            <span class="template-name">{{ approvalTemplateLabel }}</span>
           </el-form-item>
-          <el-form-item label="瀹℃壒娴佺▼" required>
+          <el-form-item v-if="activeTemplate" label="瀹℃壒娴佺▼" required>
             <TemplateFlowEditor v-model="submitForm.flowNodes" :user-options="flowUserOptions" />
             <p class="section-tip">娴佺▼涓庡鎵逛汉鐢辨ā鏉块缃紝鍙寜闇�寰皟鑺傜偣瀹℃壒浜恒��</p>
           </el-form-item>
         </template>
-        <el-alert v-else type="warning" show-icon :closable="false" title="璇峰厛閫氳繃銆屾柊寤烘柊闂汇�嶉�夋嫨瀹℃壒妯℃澘" />
+        <el-alert
+          v-else-if="!isNewsEdit"
+          type="warning"
+          show-icon
+          :closable="false"
+          title="璇峰厛閫氳繃銆屾柊寤烘柊闂汇�嶉�夋嫨瀹℃壒妯℃澘"
+        />
       </el-form>
       <template v-if="!newsFormDialog.readonly" #footer>
         <el-button @click="newsFormDialog.visible = false">鍙� 娑�</el-button>
-        <el-button :loading="submitSaving" @click="onNewsSave('draft')">瀛樿崏绋�</el-button>
-        <el-button type="warning" :loading="submitSaving" @click="onNewsSave('submit_review')">
+        <el-button :loading="newsSaving" @click="onNewsSave('draft')">瀛樿崏绋�</el-button>
+        <el-button type="warning" :loading="newsSaving" @click="onNewsSave('submit_review')">
           鎻愪氦瀹℃牳
         </el-button>
-        <el-button type="primary" :loading="submitSaving" @click="onNewsSave('submit_review')">
+        <el-button type="primary" :loading="newsSaving" @click="onNewsSave('submit_review')">
           淇� 瀛�
         </el-button>
       </template>
     </el-dialog>
 
-    <!-- 璇︽儏 -->
     <el-dialog v-model="detailDialog.visible" title="鏂伴椈璇︽儏" width="880px" append-to-body destroy-on-close>
       <NewsDetailPanel :row="detailNewsRow" />
-      <el-divider content-position="left">瀹℃壒淇℃伅</el-divider>
-      <ApproveDetailPanel :row="detailRow" />
       <template #footer>
-        <el-button
-          v-if="canEditBusinessInstanceRow(detailRow)"
-          type="primary"
-          @click="openNewsEditFromDetail"
-        >
+        <el-button v-if="canEditEnterpriseNewsRow(detailRow)" type="primary" @click="openNewsEditFromDetail">
           淇敼
         </el-button>
         <el-button @click="detailDialog.visible = false">鍏� 闂�</el-button>
@@ -220,22 +215,27 @@
 
 <script setup>
 import { Plus, RefreshRight, Search } from "@element-plus/icons-vue";
-import { ElMessage } from "element-plus";
+import { ElMessage, ElMessageBox } from "element-plus";
+import {
+  deleteEnterpriseNews,
+  saveEnterpriseNews,
+  updateEnterpriseNews,
+} from "@/api/officeProcessAutomation/enterpriseNews.js";
 import { computed, onMounted, reactive, ref } from "vue";
-import useUserStore from "@/store/modules/user";
 import Editor from "@/components/Editor/index.vue";
 import FileUpload from "@/components/AttachmentUpload/file/index.vue";
-import { APPROVAL_STATUS_SEARCH_OPTIONS } from "../../ApproveManage/approve-list/approveListConstants.js";
-import ApproveDetailPanel from "../../ApproveManage/approve-list/components/ApproveDetailPanel.vue";
-import { buildEditFormFromInstanceRow } from "../../ApproveManage/approve-list/approveListConstants.js";
 import ApprovalTemplateBindDialog from "../../ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue";
 import TemplateFlowEditor from "../../ApproveManage/approve-template/components/TemplateFlowEditor.vue";
 import {
   applyBindingToForm,
   validateTemplateBinding,
 } from "../../ApproveManage/approve-shared/approvalTemplateBindingUtils.js";
+import { createEmptySubmitForm } from "../../ApproveManage/approve-list/approveListConstants.js";
 import { APPROVAL_MODULE_KEYS } from "../../ApproveManage/approve-shared/approvalModuleRegistry.js";
-import { useApprovalInstanceModule } from "../../ApproveManage/approve-shared/useApprovalInstanceModule.js";
+import {
+  applyBindingToForm,
+  validateTemplateBinding,
+} from "../../ApproveManage/approve-shared/approvalTemplateBindingUtils.js";
 import { useFlowUserOptions } from "../../ApproveManage/approve-shared/useFlowUserOptions.js";
 import NewsDetailPanel from "./components/NewsDetailPanel.vue";
 import {
@@ -245,18 +245,18 @@
   PUBLISH_ROLE_OPTIONS,
   DEPT_OPTIONS,
   createEmptyForm,
+  ENTERPRISE_NEWS_STATUS_SEARCH_OPTIONS,
   newsTypeColor,
   newsTypeLabel,
   validateNewsForm,
 } from "./enterpriseNewsUtils.js";
 import {
-  enrichEnterpriseNewsListRow,
-  extractEnterpriseNewsFromRow,
-  syncNewsFormToSubmitPayload,
+  buildEnterpriseNewsSaveDto,
   buildEnterpriseNewsTableColumns,
-} from "./enterpriseNewsApprovalBridge.js";
-
-const userStore = useUserStore();
+  canEditEnterpriseNewsRow,
+  mapApiRowToNewsForm,
+} from "./enterpriseNewsMappers.js";
+import { useEnterpriseNewsList } from "./useEnterpriseNewsList.js";
 
 const searchForm = reactive({
   keyword: "",
@@ -266,6 +266,8 @@
 });
 
 const newsFormDialog = reactive({ visible: false, title: "", mode: "add", readonly: false });
+const detailDialog = reactive({ visible: false });
+const detailRow = ref({});
 const newsForm = reactive(createEmptyForm());
 const newsFormRef = ref();
 const galleryInput = ref("");
@@ -276,66 +278,44 @@
   readScope: [{ required: true, message: "璇烽�夋嫨闃呰鑼冨洿", trigger: "change" }],
 };
 
-const mod = useApprovalInstanceModule({
-  moduleKey: APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS,
-  enrichListRow: enrichEnterpriseNewsListRow,
-  buildExtraListParams(sf) {
-    const extra = {};
-    const kw = (sf?.keyword || "").trim();
-    if (kw) extra.title = kw;
-    if (sf?.newsType) extra.newsType = sf.newsType;
-    return extra;
-  },
-  async beforeSave(submitForm) {
-    const v = validateNewsForm(newsForm);
-    if (!v.ok) {
-      ElMessage.warning(v.message);
-      throw new Error(v.message);
-    }
-    if (!activeTemplate.value) {
-      ElMessage.warning("璇峰厛閫夋嫨瀹℃壒妯℃澘");
-      throw new Error("no template");
-    }
-    const bindingCheck = validateTemplateBinding({ flowNodes: submitForm.flowNodes });
-    if (!bindingCheck.ok) {
-      ElMessage.warning(bindingCheck.message);
-      throw new Error(bindingCheck.message);
-    }
-    syncNewsFormToSubmitPayload(newsForm, submitForm);
-  },
-});
+const newsList = useEnterpriseNewsList();
+const { tableData, tableLoading, page, handleQuery: fetchNewsList, pagination: paginateNewsList } =
+  newsList;
 
-const {
-  tableData,
-  tableLoading,
-  page,
-  detailDialog,
-  detailRow,
-  submitDialog,
-  submitForm,
-  submitSaving,
-  isSubmitEdit,
-  activeTemplate,
-  templateBindVisible,
-  pendingTemplateBinding,
-  submitEditRow,
-  handleQuery,
-  initModuleList,
-  pagination,
-  openAddWithTemplate,
-  onTemplateBound,
-  resetSubmitForm,
-  submitInstanceForm,
-  removeInstance,
-  canEditBusinessInstanceRow,
-} = mod;
+const submitForm = reactive(createEmptySubmitForm(""));
+const templateBindVisible = ref(false);
+const pendingTemplateBinding = ref(null);
+const newsSaving = ref(false);
+
+const isNewsEdit = computed(() => newsFormDialog.mode === "edit");
+const activeTemplate = computed(() => submitForm.templateSnapshot || null);
+const hasApprovalTemplate = computed(
+  () => Boolean(activeTemplate.value || newsForm.templateId)
+);
+const approvalTemplateLabel = computed(
+  () =>
+    activeTemplate.value?.label ||
+    newsForm.templateName ||
+    submitForm.templateName ||
+    "鈥�"
+);
 
 const { flowUserOptions, loadFlowUsers } = useFlowUserOptions();
 
-const detailNewsRow = computed(() => {
-  if (!detailRow.value?.id) return {};
-  return extractEnterpriseNewsFromRow(detailRow.value);
-});
+function openAddWithTemplate() {
+  pendingTemplateBinding.value = null;
+  templateBindVisible.value = true;
+}
+
+function onTemplateBound(binding) {
+  pendingTemplateBinding.value = binding;
+}
+
+function resetSubmitForm() {
+  Object.assign(submitForm, createEmptySubmitForm(""));
+}
+
+const detailNewsRow = computed(() => mapApiRowToNewsForm(detailRow.value));
 
 const tableColumn = ref(
   buildEnterpriseNewsTableColumns(() => [
@@ -343,13 +323,14 @@
     {
       name: "淇敼",
       type: "text",
-      disabled: (row) => !canEditBusinessInstanceRow(row),
+      disabled: (row) => !canEditEnterpriseNewsRow(row),
       clickFun: (row) => openNewsEdit(row),
     },
     {
       name: "鍒犻櫎",
       type: "danger",
-      clickFun: (row) => removeInstance(row),
+      disabled: (row) => !canEditEnterpriseNewsRow(row),
+      clickFun: (row) => handleNewsDelete(row),
     },
   ])
 );
@@ -364,11 +345,9 @@
   newsFormDialog.title =
     mode === "add" ? "鏂板缓浼佷笟鏂伴椈" : mode === "edit" ? "缂栬緫浼佷笟鏂伴椈" : "鏌ョ湅浼佷笟鏂伴椈";
   if (mode === "add") {
-    resetNewsForm({
-      publisherName: userStore?.nickName || userStore?.name || "褰撳墠鐢ㄦ埛",
-    });
+    resetNewsForm();
   } else if (row) {
-    resetNewsForm(extractEnterpriseNewsFromRow(row));
+    resetNewsForm(mapApiRowToNewsForm(row));
   }
   newsFormDialog.visible = true;
 }
@@ -379,19 +358,23 @@
   pendingTemplateBinding.value = null;
   resetSubmitForm();
   applyBindingToForm(submitForm, binding);
-  submitDialog.mode = "add";
-  submitEditRow.value = null;
+  if (binding.templateId) {
+    newsForm.templateId = binding.templateId;
+    newsForm.templateName = binding.templateName || "";
+  }
   openNewsFormDialog("add");
 }
 
 function openNewsEdit(row) {
-  if (!canEditBusinessInstanceRow(row)) {
-    ElMessage.warning("杩涜涓垨宸插畬鎴愮殑瀹℃壒涓嶅彲淇敼");
+  if (!canEditEnterpriseNewsRow(row)) {
+    ElMessage.warning("褰撳墠鐘舵�佷笉鍙慨鏀�");
     return;
   }
-  submitDialog.mode = "edit";
-  submitEditRow.value = { ...row };
-  Object.assign(submitForm, buildEditFormFromInstanceRow(row));
+  resetSubmitForm();
+  if (row?.templateId != null) {
+    submitForm.templateId = row.templateId;
+    submitForm.templateName = row.templateName || "";
+  }
   openNewsFormDialog("edit", row);
 }
 
@@ -404,6 +387,40 @@
   const row = detailRow.value;
   detailDialog.visible = false;
   openNewsEdit(row);
+}
+
+async function handleNewsDelete(row) {
+  if (!canEditEnterpriseNewsRow(row)) {
+    ElMessage.warning("褰撳墠鐘舵�佷笉鍙垹闄�");
+    return;
+  }
+  if (row?.id == null || row.id === "") {
+    ElMessage.warning("鏃犳硶鍒犻櫎锛氱己灏戞柊闂� ID");
+    return;
+  }
+  const title = (row.title || "").trim() || "璇ユ潯鏂伴椈";
+  try {
+    await ElMessageBox.confirm(
+      `纭畾瑕佸垹闄ゃ��${title}銆嶅悧锛熷垹闄ゅ悗涓嶅彲鎭㈠銆俙,
+      "鍒犻櫎纭",
+      {
+        type: "warning",
+        confirmButtonText: "纭畾鍒犻櫎",
+        cancelButtonText: "鍙栨秷",
+        distinguishCancelAndClose: true,
+        autofocus: false,
+      }
+    );
+  } catch {
+    return;
+  }
+  try {
+    await deleteEnterpriseNews([row.id]);
+    ElMessage.success("鍒犻櫎鎴愬姛");
+    await fetchNewsList(searchForm);
+  } catch {
+    /* 閿欒鐢辫姹傛嫤鎴櫒鎻愮ず */
+  }
 }
 
 function onNewsFormClosed() {
@@ -425,19 +442,67 @@
     ElMessage.warning("璇峰畬鍠勮〃鍗曞繀濉」鍚庡啀淇濆瓨");
     return;
   }
-  if (action === "draft") newsForm.publishStatus = "draft";
-  else newsForm.publishStatus = "pending_review";
-  const ok = await submitInstanceForm({ skipValidate: true });
-  if (ok) {
+  const v = validateNewsForm(newsForm);
+  if (!v.ok) {
+    ElMessage.warning(v.message);
+    return;
+  }
+  const status = action === "draft" ? "DRAFT" : "PENDING";
+  newsForm.publishStatus = status;
+
+  if (!isNewsEdit.value) {
+    const templateId = newsForm.templateId || submitForm.templateId;
+    if (!templateId) {
+      ElMessage.warning("璇峰厛閫夋嫨瀹℃壒妯℃澘");
+      return;
+    }
+    if (!newsForm.templateId) newsForm.templateId = templateId;
+    if (!newsForm.templateName && submitForm.templateName) {
+      newsForm.templateName = submitForm.templateName;
+    }
+    if (action !== "draft") {
+      const bindingCheck = validateTemplateBinding({ flowNodes: submitForm.flowNodes });
+      if (!bindingCheck.ok) {
+        ElMessage.warning(bindingCheck.message);
+        return;
+      }
+    }
+  } else if (!newsForm.templateId && submitForm.templateId) {
+    newsForm.templateId = submitForm.templateId;
+    newsForm.templateName = submitForm.templateName || newsForm.templateName;
+  }
+
+  const dto = buildEnterpriseNewsSaveDto(newsForm, { status });
+  if (isNewsEdit.value) {
+    if (dto.id == null) {
+      ElMessage.warning("鏃犳硶淇敼锛氱己灏戞柊闂� ID");
+      return;
+    }
+  }
+
+  if (newsSaving.value) return;
+  newsSaving.value = true;
+  try {
+    if (isNewsEdit.value) {
+      await updateEnterpriseNews(dto);
+    } else {
+      await saveEnterpriseNews(dto);
+    }
     newsFormDialog.visible = false;
     const msg =
-      action === "draft" ? "宸蹭繚瀛樿崏绋�" : isSubmitEdit.value ? "淇敼鎴愬姛" : "宸叉彁浜ゅ鏍�";
+      action === "draft" ? "宸蹭繚瀛樿崏绋�" : isNewsEdit.value ? "淇敼鎴愬姛" : "宸叉彁浜ゅ鏍�";
     ElMessage.success(msg);
+    if (!isNewsEdit.value) page.current = 1;
+    await fetchNewsList(searchForm);
+  } catch {
+    /* 閿欒鐢辫姹傛嫤鎴櫒鎻愮ず */
+  } finally {
+    newsSaving.value = false;
   }
 }
 
 function onSearch() {
-  handleQuery(searchForm);
+  fetchNewsList(searchForm);
 }
 
 function resetSearch() {
@@ -449,17 +514,12 @@
 }
 
 function onPagination(obj) {
-  pagination(obj, searchForm);
+  paginateNewsList(obj, searchForm);
 }
 
-onMounted(async () => {
-  try {
-    localStorage.removeItem("oa_enterprise_news_v1");
-  } catch {
-    /* 娓呴櫎鍘嗗彶鏈湴婕旂ず缂撳瓨 */
-  }
+onMounted(() => {
   loadFlowUsers();
-  await initModuleList(searchForm);
+  fetchNewsList(searchForm);
 });
 </script>
 
diff --git a/src/views/officeProcessAutomation/EnterpriseNews/news-manage/useEnterpriseNewsList.js b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/useEnterpriseNewsList.js
new file mode 100644
index 0000000..66aef1e
--- /dev/null
+++ b/src/views/officeProcessAutomation/EnterpriseNews/news-manage/useEnterpriseNewsList.js
@@ -0,0 +1,55 @@
+import { listEnterpriseNewsPage } from "@/api/officeProcessAutomation/enterpriseNews.js";
+import { ElMessage } from "element-plus";
+import { reactive, ref } from "vue";
+import {
+  buildEnterpriseNewsListParams,
+  mapEnterpriseNewsFromApi,
+  unwrapEnterpriseNewsPage,
+} from "./enterpriseNewsMappers.js";
+
+/** 浼佷笟鏂伴椈鍒楄〃锛氬垎椤垫煡璇� /enterpriseNews/listPage */
+export function useEnterpriseNewsList() {
+  const tableData = ref([]);
+  const tableLoading = ref(false);
+  const page = reactive({ current: 1, size: 10, total: 0 });
+  let lastSearchForm = null;
+
+  async function fetchList(searchForm = {}) {
+    tableLoading.value = true;
+    try {
+      const res = await listEnterpriseNewsPage(
+        buildEnterpriseNewsListParams({ page, searchForm })
+      );
+      const { records, total } = unwrapEnterpriseNewsPage(res);
+      tableData.value = records.map(mapEnterpriseNewsFromApi);
+      page.total = total;
+    } catch {
+      tableData.value = [];
+      page.total = 0;
+      ElMessage.error("浼佷笟鏂伴椈鍒楄〃鍔犺浇澶辫触");
+    } finally {
+      tableLoading.value = false;
+    }
+  }
+
+  function handleQuery(searchForm) {
+    lastSearchForm = searchForm;
+    page.current = 1;
+    return fetchList(searchForm);
+  }
+
+  function pagination({ page: p, limit }, searchForm) {
+    page.current = p;
+    page.size = limit;
+    return fetchList(searchForm ?? lastSearchForm ?? {});
+  }
+
+  return {
+    tableData,
+    tableLoading,
+    page,
+    fetchList,
+    handleQuery,
+    pagination,
+  };
+}
diff --git a/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue b/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
index 83bfefb..ef4a1e8 100644
--- a/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
+++ b/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
@@ -95,11 +95,10 @@
 const mod = useApprovalInstanceModule({
   moduleKey: APPROVAL_MODULE_KEYS.REGULAR,
   buildExtraListParams(sf) {
-    const range = sf?.applyDateRange;
-    if (Array.isArray(range) && range[0]) {
-      return { createTime: range[0], createTimeEnd: range[1] };
-    }
-    return {};
+    const extra = {};
+    const name = (sf?.applicantName || "").trim();
+    if (name) extra.applicantName = name;
+    return extra;
   },
 });
 
diff --git a/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue b/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
index 5be9b2b..4121157 100644
--- a/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
+++ b/src/views/officeProcessAutomation/HrManage/transfer-apply/index.vue
@@ -137,11 +137,7 @@
 
 const mod = useApprovalInstanceModule({
   moduleKey: APPROVAL_MODULE_KEYS.TRANSFER,
-  buildExtraListParams(sf) {
-    const range = sf?.transferDateRange;
-    if (Array.isArray(range) && range[0]) {
-      return { createTime: range[0], createTimeEnd: range[1] };
-    }
+  buildExtraListParams() {
     return {};
   },
 });

--
Gitblit v1.9.3