From d7f9fc20b91f72f52cb76e699858458d995c6c76 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期四, 21 五月 2026 10:51:55 +0800
Subject: [PATCH] 公告通知页面复用

---
 /dev/null                                                                    |  328 --------------------------------
 src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/index.vue |  255 ------------------------
 2 files changed, 7 insertions(+), 576 deletions(-)

diff --git a/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/components/NoticeDetailPanel.vue b/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/components/NoticeDetailPanel.vue
deleted file mode 100644
index 9a490fc..0000000
--- a/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/components/NoticeDetailPanel.vue
+++ /dev/null
@@ -1,77 +0,0 @@
-<!-- NoticeAnnouncement锛氬叕鍛婅鎯呭彧璇婚潰鏉� -->
-<template>
-  <el-descriptions :column="2" border>
-    <el-descriptions-item label="鍏憡缂栧彿">{{ row.noticeNo || "鈥�" }}</el-descriptions-item>
-    <el-descriptions-item label="鍙戝竷鐘舵��">
-      <el-tag :type="statusTag" size="small">{{ statusText }}</el-tag>
-    </el-descriptions-item>
-    <el-descriptions-item label="鍏憡绫诲瀷">
-      <span class="type-badge" :style="{ color: noticeTypeColor(row.noticeType) }">
-        {{ noticeTypeLabel(row.noticeType) }}
-      </span>
-    </el-descriptions-item>
-    <el-descriptions-item label="浼樺厛绾�">
-      <el-tag :type="priorityTag(row.priority)" size="small">{{ priorityLabel(row.priority) }}</el-tag>
-    </el-descriptions-item>
-    <el-descriptions-item label="鏍囬" :span="2">{{ row.title || "鈥�" }}</el-descriptions-item>
-    <el-descriptions-item label="鍙戝竷鏃ユ湡">{{ row.publishDate || "鈥�" }}</el-descriptions-item>
-    <el-descriptions-item label="杩囨湡鏃ユ湡">{{ row.expireDate || "闀挎湡鏈夋晥" }}</el-descriptions-item>
-    <el-descriptions-item label="闃呰鑼冨洿">{{ readScopeLabel(row.readScope) }}</el-descriptions-item>
-    <el-descriptions-item label="闇�闃呰纭">{{ row.requireReadConfirm ? "鏄�" : "鍚�" }}</el-descriptions-item>
-    <el-descriptions-item label="鍙戝竷浜�">{{ row.publisherName || "鈥�" }}</el-descriptions-item>
-    <el-descriptions-item label="鍙戝竷鏃堕棿">{{ row.publishTime || "鈥�" }}</el-descriptions-item>
-    <el-descriptions-item label="闃呰閲�">{{ row.readCount ?? 0 }}</el-descriptions-item>
-  </el-descriptions>
-
-  <el-divider content-position="left">鍏憡鍐呭</el-divider>
-  <div v-if="row.priority === 'urgent'" class="urgent-banner">
-    <el-alert title="绱ф�ラ�氱煡" type="error" :closable="false" show-icon />
-  </div>
-  <div v-if="row.contentHtml" class="notice-html-body" v-html="row.contentHtml" />
-  <el-empty v-else description="鏆傛棤鍐呭" :image-size="48" />
-</template>
-
-<script setup>
-import { computed } from "vue";
-import {
-  noticeTypeLabel,
-  noticeTypeColor,
-  priorityLabel,
-  priorityTag,
-  publishStatusLabel,
-  publishStatusTag,
-  readScopeLabel,
-  isExpired,
-} from "../noticeAnnouncementUtils.js";
-
-const props = defineProps({
-  row: { type: Object, default: () => ({}) },
-});
-
-const statusText = computed(() => {
-  if (isExpired(props.row) && props.row.publishStatus === "published") return "宸茶繃鏈�";
-  return publishStatusLabel(props.row.publishStatus);
-});
-
-const statusTag = computed(() => {
-  if (isExpired(props.row) && props.row.publishStatus === "published") return "";
-  return publishStatusTag(props.row.publishStatus);
-});
-</script>
-
-<style scoped>
-.type-badge {
-  font-weight: 600;
-}
-.urgent-banner {
-  margin-bottom: 12px;
-}
-.notice-html-body {
-  padding: 12px;
-  background: var(--el-fill-color-light);
-  border-radius: 6px;
-  max-height: 400px;
-  overflow-y: auto;
-  line-height: 1.7;
-}
-</style>
diff --git a/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/index.vue b/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/index.vue
index 4599ced..3f65cb7 100644
--- a/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/index.vue
+++ b/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/index.vue
@@ -1,253 +1,12 @@
-<!--OA妯″潡锛歂oticeAnnouncement 閫氱煡鍏憡-->
+<!--
+  妯″潡涓枃鍚嶏細閫氱煡鍏憡
+  鐩綍鏍囪瘑锛歂oticeAnnouncement/notice-manage
+  澶嶇敤椤甸潰锛欯/views/collaborativeApproval/noticeManagement/index.vue锛堝崗鍚屽鎵�-閫氱煡鍏憡锛�
+-->
 <template>
-  <div class="app-container notice-announcement-page">
-    <div class="search_form mb20">
-      <div class="search_fields">
-        <span class="search_title">鍏抽敭璇嶏細</span>
-        <el-input
-          v-model="searchForm.keyword"
-          style="width: 200px"
-          placeholder="鏍囬 / 缂栧彿"
-          clearable
-          :prefix-icon="Search"
-          @keyup.enter="handleQuery"
-        />
-        <span class="search_title" style="margin-left: 12px">绫诲瀷锛�</span>
-        <el-select v-model="searchForm.noticeType" placeholder="鍏ㄩ儴" clearable style="width: 130px">
-          <el-option v-for="opt in NOTICE_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
-        </el-select>
-        <span class="search_title" style="margin-left: 12px">浼樺厛绾э細</span>
-        <el-select v-model="searchForm.priority" placeholder="鍏ㄩ儴" clearable style="width: 110px">
-          <el-option v-for="opt in PRIORITY_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
-        </el-select>
-        <span class="search_title" style="margin-left: 12px">鐘舵�侊細</span>
-        <el-select v-model="searchForm.publishStatus" placeholder="鍏ㄩ儴" clearable style="width: 110px">
-          <el-option v-for="opt in PUBLISH_STATUS_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
-        </el-select>
-        <span class="search_title" style="margin-left: 12px">鍙戝竷鏃ユ湡锛�</span>
-        <el-date-picker
-          v-model="searchForm.publishDateRange"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="寮�濮�"
-          end-placeholder="缁撴潫"
-          format="YYYY-MM-DD"
-          value-format="YYYY-MM-DD"
-          style="width: 260px"
-          clearable
-        />
-        <el-button type="primary" :icon="Search" class="ml10" @click="handleQuery">鎼滅储</el-button>
-        <el-button :icon="RefreshRight" @click="resetSearch">閲嶇疆</el-button>
-      </div>
-      <div class="search_actions">
-        <el-button type="primary" :icon="Plus" @click="openFormDialog('add')">娣诲姞鍏憡</el-button>
-      </div>
-    </div>
-
-    <div class="table_list">
-      <PIMTable
-        rowKey="id"
-        :column="tableColumn"
-        :tableData="tableData"
-        :page="page"
-        :isSelection="false"
-        :tableLoading="tableLoading"
-        :total="page.total"
-        @pagination="pagination"
-      >
-        <template #noticeType="{ row }">
-          <span class="notice-type-tag" :style="{ color: noticeTypeColor(row.noticeType) }">
-            {{ noticeTypeLabel(row.noticeType) }}
-          </span>
-        </template>
-      </PIMTable>
-    </div>
-
-    <!-- 娣诲姞 / 淇敼 -->
-    <el-dialog
-      v-model="formDialog.visible"
-      :title="formDialog.title"
-      width="800px"
-      append-to-body
-      destroy-on-close
-      class="notice-form-dialog"
-      @closed="formRef?.resetFields?.()"
-    >
-      <el-form
-        ref="formRef"
-        :model="form"
-        :rules="formRules"
-        label-width="100px"
-        :disabled="formDialog.readonly"
-      >
-        <el-form-item label="鏍囬" prop="title">
-          <el-input v-model="form.title" placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" maxlength="100" show-word-limit />
-        </el-form-item>
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="鍏憡绫诲瀷" prop="noticeType">
-              <el-select v-model="form.noticeType" placeholder="璇烽�夋嫨" style="width: 100%" @change="onNoticeTypeChange">
-                <el-option v-for="opt in NOTICE_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="浼樺厛绾�">
-              <el-select v-model="form.priority" placeholder="璇烽�夋嫨" style="width: 100%">
-                <el-option v-for="opt in PRIORITY_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="鍙戝竷鏃ユ湡" prop="publishDate">
-              <el-date-picker
-                v-model="form.publishDate"
-                type="date"
-                placeholder="鍙戝竷鏃ユ湡"
-                format="YYYY-MM-DD"
-                value-format="YYYY-MM-DD"
-                style="width: 100%"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="杩囨湡鏃ユ湡">
-              <el-date-picker
-                v-model="form.expireDate"
-                type="date"
-                placeholder="鍙�夛紝鐣欑┖涓洪暱鏈熸湁鏁�"
-                format="YYYY-MM-DD"
-                value-format="YYYY-MM-DD"
-                style="width: 100%"
-                clearable
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-form-item label="闃呰鑼冨洿">
-          <el-radio-group v-model="form.readScope">
-            <el-radio v-for="opt in READ_SCOPE_OPTIONS" :key="opt.value" :value="opt.value">
-              {{ opt.label }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item v-if="form.readScope === 'department'" label="鍙閮ㄩ棬">
-          <el-select v-model="form.targetDeptIds" multiple placeholder="閫夋嫨閮ㄩ棬" style="width: 100%">
-            <el-option v-for="d in DEPT_OPTIONS" :key="d.value" :label="d.label" :value="d.value" />
-          </el-select>
-        </el-form-item>
-        <el-form-item v-if="form.noticeType === 'emergency'" label="蹇呰纭">
-          <el-switch v-model="form.requireReadConfirm" active-text="绱ф�ラ�氱煡闇�鍛樺伐纭宸茶" />
-        </el-form-item>
-        <el-form-item label="鍐呭" prop="contentHtml">
-          <Editor v-model="form.contentHtml" :min-height="280" placeholder="璇疯緭鍏ュ唴瀹�" />
-        </el-form-item>
-        <el-form-item label="鍙戝竷浜�">
-          <el-input v-model="form.publisherName" placeholder="濡傦細琛屾斂閮�" maxlength="50" />
-        </el-form-item>
-      </el-form>
-      <template v-if="!formDialog.readonly" #footer>
-        <el-button @click="formDialog.visible = false">鍙� 娑�</el-button>
-        <el-button @click="onSave(false)">瀛樿崏绋�</el-button>
-        <el-button type="primary" @click="onSave(true)">纭� 瀹�</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 璇︽儏 -->
-    <el-dialog v-model="detailDialog.visible" title="鍏憡璇︽儏" width="800px" append-to-body destroy-on-close>
-      <NoticeDetailPanel :row="detailRow" />
-      <template #footer>
-        <el-button @click="detailDialog.visible = false">鍏� 闂�</el-button>
-      </template>
-    </el-dialog>
-  </div>
+  <NoticeManagement />
 </template>
 
 <script setup>
-import { Plus, RefreshRight } from "@element-plus/icons-vue";
-import { ElMessage } from "element-plus";
-import { onMounted } from "vue";
-import Editor from "@/components/Editor/index.vue";
-import { noticeTypeColor } from "./noticeAnnouncementUtils.js";
-import NoticeDetailPanel from "./components/NoticeDetailPanel.vue";
-import { useNoticeAnnouncement } from "./useNoticeAnnouncement.js";
-
-const {
-  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,
-  handleQuery,
-  resetSearch,
-  pagination,
-  openFormDialog,
-  saveForm,
-} = useNoticeAnnouncement();
-
-function onNoticeTypeChange(type) {
-  if (type === "emergency") {
-    form.priority = "urgent";
-    form.requireReadConfirm = true;
-  }
-}
-
-function onSave(publish) {
-  const ret = saveForm(publish);
-  if (ret?.message) {
-    ElMessage.warning(ret.message);
-    return;
-  }
-  if (ret?.ok) {
-    ElMessage.success(publish ? "鍏憡宸插彂甯�" : "宸蹭繚瀛樿崏绋�");
-  }
-}
-
-onMounted(() => {
-  handleQuery();
-});
+import NoticeManagement from "@/views/collaborativeApproval/noticeManagement/index.vue";
 </script>
-
-<style scoped>
-.notice-announcement-page .search_form {
-  display: flex;
-  flex-wrap: wrap;
-  justify-content: space-between;
-  align-items: flex-start;
-  gap: 12px;
-}
-.search_fields {
-  display: flex;
-  flex-wrap: wrap;
-  align-items: center;
-  gap: 4px;
-}
-.search_actions {
-  flex-shrink: 0;
-}
-.notice-type-tag {
-  font-weight: 600;
-  font-size: 13px;
-}
-.ml10 {
-  margin-left: 10px;
-}
-.mb20 {
-  margin-bottom: 20px;
-}
-</style>
diff --git a/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/noticeAnnouncementUtils.js b/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/noticeAnnouncementUtils.js
deleted file mode 100644
index 7fe3d6d..0000000
--- a/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/noticeAnnouncementUtils.js
+++ /dev/null
@@ -1,109 +0,0 @@
-import dayjs from "dayjs";
-
-/** 鍏憡绫诲瀷 */
-export const NOTICE_TYPE_OPTIONS = [
-  { value: "emergency", label: "绱ф�ラ�氱煡", color: "#f56c6c" },
-  { value: "employee", label: "鍛樺伐鍏憡", color: "#409eff" },
-  { value: "company", label: "浼佷笟鍏憡", color: "#e6a23c" },
-];
-
-/** 浼樺厛绾� */
-export const PRIORITY_OPTIONS = [
-  { value: "urgent", label: "绱ф��", tag: "danger" },
-  { value: "high", label: "閲嶈", tag: "warning" },
-  { value: "normal", label: "鏅��", tag: "info" },
-];
-
-/** 鍙戝竷鐘舵�� */
-export const PUBLISH_STATUS_OPTIONS = [
-  { value: "draft", label: "鑽夌", tag: "info" },
-  { value: "published", label: "宸插彂甯�", tag: "success" },
-  { value: "withdrawn", label: "宸叉挙鍥�", tag: "warning" },
-  { value: "expired", label: "宸茶繃鏈�", tag: "" },
-];
-
-/** 闃呰鑼冨洿 */
-export const READ_SCOPE_OPTIONS = [
-  { value: "all", label: "鍏ㄥ憳鍙" },
-  { value: "department", label: "鎸囧畾閮ㄩ棬" },
-  { value: "management", label: "绠$悊灞�" },
-];
-
-export const DEPT_OPTIONS = [
-  { value: "101", label: "鐮斿彂閮�" },
-  { value: "102", label: "閿�鍞儴" },
-  { value: "103", label: "琛屾斂閮�" },
-  { value: "104", label: "璐㈠姟閮�" },
-  { value: "105", label: "鎬荤粡鍔�" },
-  { value: "106", label: "浜哄姏璧勬簮閮�" },
-];
-
-export function noticeTypeLabel(v) {
-  return NOTICE_TYPE_OPTIONS.find((x) => x.value === v)?.label || v || "鈥�";
-}
-
-export function noticeTypeColor(v) {
-  return NOTICE_TYPE_OPTIONS.find((x) => x.value === v)?.color || "#909399";
-}
-
-export function priorityLabel(v) {
-  return PRIORITY_OPTIONS.find((x) => x.value === v)?.label || v || "鈥�";
-}
-
-export function priorityTag(v) {
-  return PRIORITY_OPTIONS.find((x) => x.value === v)?.tag || "info";
-}
-
-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 readScopeLabel(v) {
-  return READ_SCOPE_OPTIONS.find((x) => x.value === v)?.label || v || "鈥�";
-}
-
-export function createEmptyForm() {
-  return {
-    id: "",
-    noticeNo: "",
-    title: "",
-    noticeType: "employee",
-    priority: "normal",
-    contentHtml: "",
-    publishDate: dayjs().format("YYYY-MM-DD"),
-    expireDate: "",
-    readScope: "all",
-    targetDeptIds: [],
-    requireReadConfirm: false,
-    publishStatus: "draft",
-    publisherName: "",
-    publishTime: "",
-    readCount: 0,
-    createTime: "",
-    updateTime: "",
-  };
-}
-
-export function nextNoticeNo() {
-  return `NA${dayjs().format("YYYYMMDD")}${String(Math.floor(Math.random() * 9000) + 1000)}`;
-}
-
-export function validateNoticeForm(form) {
-  const title = (form.title || "").trim();
-  if (!title) return { ok: false, message: "璇疯緭鍏ュ叕鍛婃爣棰�" };
-  if (!form.publishDate) return { ok: false, message: "璇烽�夋嫨鍙戝竷鏃ユ湡" };
-  if (!form.noticeType) return { ok: false, message: "璇烽�夋嫨鍏憡绫诲瀷" };
-  if (form.readScope === "department" && !(form.targetDeptIds || []).length) {
-    return { ok: false, message: "璇烽�夋嫨鍙閮ㄩ棬" };
-  }
-  return { ok: true, title };
-}
-
-export function isExpired(row) {
-  if (!row.expireDate) return false;
-  return dayjs(row.expireDate).endOf("day").isBefore(dayjs());
-}
diff --git a/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/useNoticeAnnouncement.js b/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/useNoticeAnnouncement.js
deleted file mode 100644
index 67281c4..0000000
--- a/src/views/officeProcessAutomation/NoticeAnnouncement/notice-manage/useNoticeAnnouncement.js
+++ /dev/null
@@ -1,328 +0,0 @@
-import { Search } from "@element-plus/icons-vue";
-import dayjs from "dayjs";
-import { ElMessageBox } from "element-plus";
-import { computed, onMounted, reactive, ref, watch } from "vue";
-import {
-  NOTICE_TYPE_OPTIONS,
-  PRIORITY_OPTIONS,
-  PUBLISH_STATUS_OPTIONS,
-  READ_SCOPE_OPTIONS,
-  DEPT_OPTIONS,
-  createEmptyForm,
-  nextNoticeNo,
-  validateNoticeForm,
-  noticeTypeLabel,
-  priorityLabel,
-  publishStatusLabel,
-  isExpired,
-} from "./noticeAnnouncementUtils.js";
-
-export function useNoticeAnnouncement() {
-  const allRows = ref([]);
-
-  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) },
-      ],
-    },
-  ]);
-
-  onMounted(() => {
-    try {
-      localStorage.removeItem("oa_notice_announcement_v1");
-    } catch {
-      /* 娓呴櫎鍘嗗彶鏈湴婕旂ず缂撳瓨 */
-    }
-  });
-
-  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 };
-    }
-    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";
-      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");
-      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);
-      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,
-  };
-}

--
Gitblit v1.9.3