From abb1971cd5eeab76736dcf402f804098929ac7bc Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 17 三月 2026 16:59:06 +0800
Subject: [PATCH] 军泰伟业app 1.添加营销管理模块和采购管理模块并联调 2.添加协同办公模块

---
 src/pages/cooperativeOffice/noticeManagement/index.vue |  809 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 809 insertions(+), 0 deletions(-)

diff --git a/src/pages/cooperativeOffice/noticeManagement/index.vue b/src/pages/cooperativeOffice/noticeManagement/index.vue
new file mode 100644
index 0000000..d63a9b4
--- /dev/null
+++ b/src/pages/cooperativeOffice/noticeManagement/index.vue
@@ -0,0 +1,809 @@
+<template>
+  <view class="notice-page">
+    <PageHeader title="閫氱煡鍏憡"
+                @back="goBack" />
+    <!-- 鎼滅储琛ㄥ崟 -->
+    <!-- <view class="search_form">
+      <up-button type="primary" size="small" @click="openForm('add')">鏂板鍏憡</up-button>
+        <up-button type="error" size="small" plain @click="handleDeleteBatch" :disabled="!selectedIds.length">
+          鍒犻櫎
+        </up-button>
+    </view> -->
+    <!-- 閫氱煡鍏憡鏉� -->
+    <view class="notice-board">
+      <!-- 缁熶竴閫氱煡鍖哄煙 -->
+      <view class="notice-section"
+            v-if="totalNoticeCount > 0">
+        <view class="section-header">
+          <h3>锟� 閫氱煡鍏憡</h3>
+          <text class="section-count">{{ totalNoticeCount }}鏉�</text>
+        </view>
+        <view class="notice-cards">
+          <!-- 鏀惧亣閫氱煡 -->
+          <view v-for="notice in holidayNotices"
+                :key="'holiday-' + notice.id"
+                class="notice-card holiday-card"
+                :class="{ 'urgent': notice.priority === '3' }">
+            <view class="card-header">
+              <view class="card-title">
+                <view class="holiday-icon">
+                  <up-icon name="calendar"
+                           size="18"
+                           color="#67c23a" />
+                </view>
+                <text>{{ notice.title }}</text>
+              </view>
+              <!--              <view class="card-actions">-->
+              <!--                <up-button-->
+              <!--                  text-->
+              <!--                  type="primary"-->
+              <!--                  size="mini"-->
+              <!--                  @click="handleEdit(notice)"-->
+              <!--                  :disabled="isNoticeExpired(notice)"-->
+              <!--                >-->
+              <!--                  缂栬緫-->
+              <!--                </up-button>-->
+              <!--                <up-button-->
+              <!--                  text-->
+              <!--                  type="error"-->
+              <!--                  size="mini"-->
+              <!--                  @click="handleDelete(notice.id)"-->
+              <!--                >-->
+              <!--                  鍒犻櫎-->
+              <!--                </up-button>-->
+              <!--              </view>-->
+            </view>
+            <view class="card-content">
+              <text>{{ notice.content }}</text>
+            </view>
+            <view class="card-footer">
+              <view class="card-meta">
+                <text class="type"
+                      :class="'type-' + notice.type">
+                  {{ notice.type }}
+                </text>
+                <text class="priority"
+                      :class="'priority-' + notice.priority">
+                  {{ getPriorityText(notice.priority) }}
+                </text>
+                <text class="status"
+                      :class="'status-' + getNoticeStatus(notice)">
+                  {{ getStatusText(getNoticeStatus(notice)) }}
+                </text>
+              </view>
+              <view class="card-info">
+                <text class="creator">{{ notice.createUserName }}</text>
+                <text class="expiration"
+                      v-if="notice.expirationDate">鎴鏃ユ湡锛歿{ notice.expirationDate }}</text>
+              </view>
+            </view>
+            <view class="card-remark"
+                  v-if="notice.remark">
+              <up-icon name="info-circle"
+                       size="16"
+                       color="#409eff" />
+              <text>{{ notice.remark }}</text>
+            </view>
+          </view>
+          <!-- 璁惧缁翠慨閫氱煡 -->
+          <view v-for="notice in maintenanceNotices"
+                :key="'maintenance-' + notice.id"
+                class="notice-card maintenance-card"
+                :class="{ 'urgent': notice.priority === '3' }">
+            <view class="card-header">
+              <view class="card-title">
+                <view class="maintenance-icon">
+                  <up-icon name="wrench"
+                           size="18"
+                           color="#e6a23c" />
+                </view>
+                <text>{{ notice.title }}</text>
+              </view>
+              <view class="card-actions">
+                <!-- <up-button text
+                           type="primary"
+                           size="mini"
+                           @click="handleEdit(notice)"
+                           :disabled="isNoticeExpired(notice)">
+                  缂栬緫
+                </up-button>
+                <up-button text
+                           type="error"
+                           size="mini"
+                           @click="handleDelete(notice.id)">
+                  鍒犻櫎
+                </up-button> -->
+              </view>
+            </view>
+            <view class="card-content">
+              <text>{{ notice.content }}</text>
+            </view>
+            <view class="card-footer">
+              <view class="card-meta">
+                <text class="priority"
+                      :class="'priority-' + notice.priority">
+                  {{ getPriorityText(notice.priority) }}
+                </text>
+                <text class="status"
+                      :class="'status-' + getNoticeStatus(notice)">
+                  {{ getStatusText(getNoticeStatus(notice)) }}
+                </text>
+              </view>
+              <view class="card-info">
+                <text class="creator">{{ notice.createUserName }}</text>
+                <text class="expiration"
+                      v-if="notice.expirationDate">鎴鏃ユ湡锛歿{ notice.expirationDate }}</text>
+              </view>
+            </view>
+            <view class="card-remark"
+                  v-if="notice.remark">
+              <up-icon name="info-circle"
+                       size="16"
+                       color="#409eff" />
+              <text>{{ notice.remark }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+      <!-- 绌虹姸鎬� -->
+      <view class="empty-state"
+            v-if="holidayNotices.length === 0 && maintenanceNotices.length === 0">
+        <text>鏆傛棤閫氱煡鍏憡</text>
+      </view>
+    </view>
+    <!-- 鏂板/缂栬緫寮圭獥 -->
+    <up-popup v-model:show="dialogVisible"
+              mode="bottom"
+              :round="18"
+              :safeAreaInsetBottom="true"
+              @close="resetForm">
+      <view class="dialog-container">
+        <view class="dialog-header">
+          <text class="dialog-title">{{ dialogTitle }}</text>
+        </view>
+        <view class="dialog-body">
+          <up-form ref="formRef"
+                   :model="form"
+                   :rules="rules"
+                   labelWidth="80">
+            <up-form-item label="鍏憡鏍囬"
+                          prop="title">
+              <up-input v-model="form.title"
+                        placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" />
+            </up-form-item>
+            <up-form-item label="鍏憡绫诲瀷"
+                          prop="type">
+              <up-input v-model="form.type"
+                        placeholder="璇疯緭鍏ュ叕鍛婄被鍨�" />
+            </up-form-item>
+            <up-form-item label="鐘舵��">
+              <up-radio-group v-model="form.status">
+                <up-radio :name="0">鑽夌</up-radio>
+                <up-radio :name="1">姝e紡鍙戝竷</up-radio>
+              </up-radio-group>
+            </up-form-item>
+            <up-form-item label="浼樺厛绾�"
+                          prop="priority">
+              <up-select v-model="form.priority"
+                         :options="priorityOptions"
+                         placeholder="璇烽�夋嫨浼樺厛绾�" />
+            </up-form-item>
+            <up-form-item label="杩囨湡鏃堕棿"
+                          prop="expirationDate">
+              <up-datetime-picker v-model="form.expirationDate"
+                                  mode="date"
+                                  @confirm="onExpireConfirm">
+                <up-input :value="form.expirationDate"
+                          placeholder="璇烽�夋嫨鏃ユ湡"
+                          readonly />
+              </up-datetime-picker>
+            </up-form-item>
+            <up-form-item label="鍏憡鍐呭"
+                          prop="content">
+              <up-textarea v-model="form.content"
+                           placeholder="璇疯緭鍏ュ叕鍛婂唴瀹�"
+                           :maxlength="500"
+                           count />
+            </up-form-item>
+            <up-form-item label="澶囨敞">
+              <up-textarea v-model="form.remark"
+                           placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
+                           :maxlength="200"
+                           count />
+            </up-form-item>
+          </up-form>
+        </view>
+        <view class="dialog-footer">
+          <up-button text="鍙栨秷"
+                     type="info"
+                     plain
+                     @click="dialogVisible = false"
+                     :customStyle="{ marginRight: '10px', flex: 1 }" />
+          <up-button text="纭畾"
+                     type="primary"
+                     @click="submitForm"
+                     :customStyle="{ flex: 1 }" />
+        </view>
+      </view>
+    </up-popup>
+  </view>
+</template>
+
+<script setup>
+  import { onMounted, ref, reactive, toRefs } from "vue";
+  import { onReachBottom } from "@dcloudio/uni-app";
+  import PageHeader from "@/components/PageHeader.vue";
+  import useUserStore from "@/store/modules/user";
+  import {
+    addNotice,
+    delNotice,
+    getCount,
+    listNotice,
+    updateNotice,
+  } from "@/api/collaborativeApproval/noticeManagement.js";
+
+  const userStore = useUserStore();
+
+  // 鍝嶅簲寮忔暟鎹�
+  const data = reactive({
+    searchForm: {
+      title: "",
+      type: undefined,
+      status: undefined,
+    },
+    form: {
+      id: undefined,
+      title: "",
+      type: null,
+      content: "",
+      status: 0,
+      priority: 1,
+      remark: "",
+      expirationDate: "",
+    },
+    rules: {
+      title: [{ required: true, message: "鍏憡鏍囬涓嶈兘涓虹┖", trigger: "blur" }],
+      type: [{ required: true, message: "璇烽�夋嫨鍏憡绫诲瀷", trigger: "change" }],
+      content: [{ required: true, message: "鍏憡鍐呭涓嶈兘涓虹┖", trigger: "blur" }],
+      expirationDate: [
+        { required: true, message: "璇烽�夋嫨鏃ユ湡", trigger: "change" },
+      ],
+    },
+  });
+
+  const { searchForm, form, rules } = toRefs(data);
+
+  // 椤甸潰鐘舵��
+  const dialogVisible = ref(false);
+  const dialogTitle = ref("");
+  const selectedIds = ref([]);
+  const formRef = ref();
+
+  const priorityOptions = [
+    { label: "鏅��", value: 1 },
+    { label: "閲嶈", value: 2 },
+    { label: "绱ф��", value: 3 },
+  ];
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  const onExpireConfirm = e => {
+    if (!e) return;
+    // uview-plus datetime-picker confirm 浜嬩欢杩斿洖鐨� value
+    const value = e.value || e;
+    form.value.expirationDate = value;
+  };
+
+  const getPriorityText = priority => {
+    const priorityMap = { 1: "鏅��", 2: "閲嶈", 3: "绱ф��" };
+    return priorityMap[priority] || "鏅��";
+  };
+
+  const getStatusText = status => {
+    const statusMap = { 0: "鑽夌", 1: "宸插彂甯�", 2: "宸茶繃鏈�" };
+    return statusMap[status] || "鏈煡";
+  };
+
+  const isNoticeExpired = notice => {
+    if (!notice || !notice.expirationDate) {
+      return false;
+    }
+
+    const expiration = new Date(notice.expirationDate);
+
+    if (Number.isNaN(expiration.getTime())) {
+      return false;
+    }
+
+    expiration.setHours(23, 59, 59, 999);
+
+    return new Date() > expiration;
+  };
+
+  const getNoticeStatus = notice => {
+    const normalizedStatus =
+      notice && notice.status !== undefined && notice.status !== null
+        ? String(notice.status)
+        : "0";
+
+    return isNoticeExpired(notice) ? "2" : normalizedStatus;
+  };
+
+  const openForm = type => {
+    if (type === "add") {
+      dialogTitle.value = "鏂板鍏憡";
+      form.value = {
+        id: undefined,
+        title: "",
+        type: undefined,
+        content: "",
+        status: 0,
+        priority: 1,
+        remark: "",
+        expirationDate: "",
+      };
+    }
+    dialogVisible.value = true;
+  };
+
+  const handleEdit = row => {
+    if (isNoticeExpired(row)) {
+      uni.showToast({
+        title: "宸茶繃鏈熺殑鍏憡涓嶅彲缂栬緫",
+        icon: "none",
+      });
+      return;
+    }
+    dialogTitle.value = "缂栬緫鍏憡";
+    form.value = { ...row };
+    dialogVisible.value = true;
+  };
+
+  const handleDelete = id => {
+    if (!id) return;
+    uni.showModal({
+      title: "鎻愮ず",
+      content: "纭鍒犻櫎杩欐潯鍏憡鍚楋紵",
+      success: res => {
+        if (res.confirm) {
+          delNotice(id).then(() => {
+            uni.showToast({
+              title: "鍒犻櫎鎴愬姛",
+              icon: "success",
+            });
+            resetTable();
+          });
+        }
+      },
+    });
+  };
+
+  // 棰勭暀鎵归噺鍒犻櫎锛堢洰鍓嶆湭瀹炵幇閫変腑閫昏緫锛屼粎鍗犱綅锛�
+  const handleDeleteBatch = () => {
+    if (!selectedIds.value.length) return;
+    uni.showModal({
+      title: "鎻愮ず",
+      content: "纭鍒犻櫎閫変腑鐨勫叕鍛婂悧锛�",
+      success: res => {
+        if (res.confirm) {
+          // 鏍规嵁selectedIds鎵ц鎵归噺鍒犻櫎閫昏緫锛堝彲鎸夐渶鎵╁睍锛�
+        }
+      },
+    });
+  };
+
+  const submitForm = () => {
+    formRef.value.validate(valid => {
+      if (valid) {
+        if (form.value.id) {
+          // 缂栬緫妯″紡
+          updateNotice(form.value).then(res => {
+            uni.showToast({
+              title: "淇敼鎴愬姛",
+              icon: "success",
+            });
+            resetTable();
+          });
+        } else {
+          // 鏂板妯″紡
+          addNotice(form.value).then(res => {
+            uni.showToast({
+              title: "鏂板鎴愬姛",
+              icon: "success",
+            });
+            resetTable();
+          });
+        }
+        dialogVisible.value = false;
+      }
+    });
+  };
+
+  const totalNoticeCount = ref(0);
+  const fetchCount = () => {
+    getCount().then(res => {
+      totalNoticeCount.value = res.data.reduce(
+        (total, item) => total + item.count,
+        0
+      );
+    });
+  };
+
+  const holidayNotices = ref([]);
+  const maintenanceNotices = ref([]);
+  const holidayNoticePage = ref({
+    total: 0,
+    current: 1,
+    size: 9,
+  });
+
+  const maintenanceNoticePage = ref({
+    total: 0,
+    current: 1,
+    size: 9,
+  });
+
+  const isLoadingMore = ref(false);
+
+  const fetchHolidayNotices = (append = false) => {
+    listNotice({ ...holidayNoticePage.value }).then(res => {
+      const records = res?.data?.records || [];
+      holidayNoticePage.value.total = res?.data?.total || 0;
+      if (append && holidayNotices.value.length) {
+        holidayNotices.value = [...holidayNotices.value, ...records];
+      } else {
+        holidayNotices.value = records;
+      }
+    });
+  };
+
+  const fetchMaintenanceNotices = (append = false) => {
+    listNotice({ ...holidayNoticePage.value, type: 2 }).then(res => {
+      const records = res?.data?.records || [];
+      maintenanceNoticePage.value.total = res?.data?.total || 0;
+      if (append && maintenanceNotices.value.length) {
+        maintenanceNotices.value = [...maintenanceNotices.value, ...records];
+      } else {
+        maintenanceNotices.value = records;
+      }
+    });
+  };
+
+  const handleCurrentChange = val => {
+    holidayNoticePage.value.size = val.limit;
+    holidayNoticePage.value.current = val.page;
+    maintenanceNoticePage.value.size = val.limit;
+    maintenanceNoticePage.value.current = val.page;
+    fetchHolidayNotices();
+    fetchMaintenanceNotices();
+  };
+
+  const resetTable = () => {
+    holidayNoticePage.value.current = 1;
+    holidayNoticePage.value.size = 9;
+    maintenanceNoticePage.value.current = 1;
+    maintenanceNoticePage.value.size = 9;
+    fetchHolidayNotices();
+    fetchMaintenanceNotices();
+    fetchCount();
+  };
+
+  const resetForm = () => {
+    formRef.value?.resetFields();
+  };
+
+  // 鐢熷懡鍛ㄦ湡
+  onMounted(() => {
+    fetchCount();
+    fetchHolidayNotices();
+    fetchMaintenanceNotices();
+  });
+
+  // 涓婂垝鍔犺浇鏇村
+  onReachBottom(() => {
+    if (isLoadingMore.value) return;
+    isLoadingMore.value = true;
+
+    holidayNoticePage.value.current += 1;
+    maintenanceNoticePage.value.current += 1;
+
+    Promise.all([
+      new Promise(resolve => {
+        fetchHolidayNotices(true);
+        resolve();
+      }),
+      new Promise(resolve => {
+        fetchMaintenanceNotices(true);
+        resolve();
+      }),
+    ]).finally(() => {
+      isLoadingMore.value = false;
+    });
+  });
+</script>
+
+<style scoped>
+  .notice-page {
+    min-height: 100vh;
+    background: #f5f7fa;
+    padding-bottom: 16px;
+    display: flex;
+    flex-direction: column;
+  }
+
+  .search_form {
+    background: #ffffff;
+    padding: 12px 16px;
+    margin: 8px 12px 12px;
+    border-radius: 10px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+  }
+
+  .search_title {
+    font-weight: 500;
+    color: #333;
+    margin-right: 8px;
+  }
+
+  .ml10 {
+    margin-left: 10px;
+  }
+
+  .notice-board {
+    padding: 0 12px 16px;
+  }
+
+  .notice-section {
+    margin-bottom: 16px;
+  }
+
+  .section-header {
+    display: flex;
+    align-items: center;
+    margin: 4px 4px 12px;
+  }
+
+  .section-header h3 {
+    margin: 0;
+    color: #303133;
+    font-size: 16px;
+    font-weight: 600;
+  }
+
+  .section-count {
+    margin-left: 10px;
+    background: #409eff;
+    color: white;
+    padding: 2px 8px;
+    border-radius: 12px;
+    font-size: 12px;
+  }
+
+  .notice-cards {
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+  }
+
+  .notice-card {
+    background: white;
+    border-radius: 12px;
+    padding: 14px 14px 10px;
+    box-shadow: 0 4px 10px rgba(15, 23, 42, 0.06);
+    transition: all 0.3s ease;
+    border-left: 4px solid transparent;
+  }
+
+  .notice-card:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
+  }
+
+  .holiday-card {
+    border-left-color: #67c23a;
+  }
+
+  .maintenance-card {
+    border-left-color: #e6a23c;
+  }
+
+  .urgent {
+    border-left-color: #f56c6c;
+    background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%);
+  }
+
+  .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    margin-bottom: 10px;
+  }
+
+  .card-title {
+    display: flex;
+    align-items: center;
+    font-size: 15px;
+    font-weight: 600;
+    color: #303133;
+    flex: 1;
+  }
+
+  .holiday-icon {
+    color: #67c23a;
+    margin-right: 8px;
+    font-size: 18px;
+  }
+
+  .maintenance-icon {
+    color: #e6a23c;
+    margin-right: 8px;
+    font-size: 18px;
+  }
+
+  .card-actions {
+    display: flex;
+    gap: 8px;
+  }
+
+  .card-content {
+    margin-bottom: 10px;
+  }
+
+  .card-content text {
+    color: #606266;
+    line-height: 1.6;
+    font-size: 13px;
+  }
+
+  .card-footer {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 10px;
+  }
+
+  .card-meta {
+    display: flex;
+    gap: 8px;
+  }
+
+  .type,
+  .priority,
+  .status {
+    padding: 2px 8px;
+    border-radius: 12px;
+    font-size: 12px;
+    font-weight: 500;
+  }
+
+  .type-1 {
+    background: #f0f9ff;
+    color: #0369a1;
+  }
+
+  .type-2 {
+    background: #fef3c7;
+    color: #d97706;
+  }
+
+  .priority-1 {
+    background: #f0f9ff;
+    color: #0369a1;
+  }
+
+  .priority-2 {
+    background: #fef3c7;
+    color: #d97706;
+  }
+
+  .priority-3 {
+    background: #fef2f2;
+    color: #dc2626;
+  }
+
+  .status-0 {
+    background: #f3f4f6;
+    color: #6b7280;
+  }
+
+  .status-1 {
+    background: #d1fae5;
+    color: #059669;
+  }
+
+  .status-2 {
+    background: #fef3c7;
+    color: #d97706;
+  }
+
+  .card-info {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-end;
+    font-size: 12px;
+    color: #909399;
+  }
+
+  .creator {
+    font-weight: 500;
+    margin-bottom: 2px;
+  }
+
+  .expiration {
+    margin-top: 2px;
+  }
+
+  .card-remark {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    padding: 8px 12px;
+    background: #f8f9fa;
+    border-radius: 6px;
+    font-size: 12px;
+    color: #606266;
+    border-left: 3px solid #409eff;
+  }
+
+  .empty-state {
+    text-align: center;
+    padding: 48px 16px;
+    color: #999;
+    font-size: 13px;
+  }
+
+  .dialog-footer {
+    text-align: right;
+  }
+
+  /* 绉诲姩绔脊绐楁牱寮� */
+  .dialog-container {
+    background: #ffffff;
+    border-radius: 18px 18px 0 0;
+    max-height: 80vh;
+    display: flex;
+    flex-direction: column;
+  }
+
+  .dialog-header {
+    padding: 16px 20px 8px 20px;
+  }
+
+  .dialog-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+
+  .dialog-body {
+    flex: 1;
+    padding: 0 16px 12px 16px;
+    overflow-y: auto;
+  }
+
+  .dialog-footer {
+    display: flex;
+    padding: 12px 16px 16px 16px;
+    border-top: 1px solid #f0f0f0;
+  }
+
+  /* 鍝嶅簲寮忚璁� */
+  @media (max-width: 768px) {
+    .search_form {
+      flex-direction: column;
+      gap: 15px;
+      align-items: flex-start;
+    }
+
+    .search_form > div {
+      width: 100%;
+      display: flex;
+      gap: 10px;
+    }
+  }
+</style>

--
Gitblit v1.9.3