zhangwencui
2 天以前 ac135a715d43b83f685365dbb68a8205ca019338
src/pages/cooperativeOffice/noticeManagement/index.vue
@@ -1,6 +1,7 @@
<template>
  <view class="notice-page">
    <PageHeader title="通知公告" @back="goBack" />
    <PageHeader title="通知公告"
                @back="goBack" />
    <!-- 搜索表单 -->
    <!-- <view class="search_form">
      <up-button type="primary" size="small" @click="openForm('add')">新增公告</up-button>
@@ -8,106 +9,108 @@
          删除
        </up-button>
    </view> -->
    <!-- 通知公告板 -->
    <view class="notice-board">
      <!-- 统一通知区域 -->
      <view class="notice-section" v-if="totalNoticeCount > 0">
      <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 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" />
                  <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 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">
                <text class="type"
                      :class="'type-' + notice.type">
                  {{ notice.type }}
                </text>
                <text class="priority" :class="'priority-' + notice.priority">
                <text class="priority"
                      :class="'priority-' + notice.priority">
                  {{ getPriorityText(notice.priority) }}
                </text>
                <text class="status" :class="'status-' + getNoticeStatus(notice)">
                <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>
                <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" />
            <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 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" />
                  <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 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 text
                           type="error"
                           size="mini"
                           @click="handleDelete(notice.id)">
                  删除
                </up-button>
              </view>
@@ -117,56 +120,61 @@
            </view>
            <view class="card-footer">
              <view class="card-meta">
                <text class="priority" :class="'priority-' + notice.priority">
                <text class="priority"
                      :class="'priority-' + notice.priority">
                  {{ getPriorityText(notice.priority) }}
                </text>
                <text class="status" :class="'status-' + getNoticeStatus(notice)">
                <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>
                <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" />
            <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">
      <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"
    >
    <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 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 label="公告类型"
                          prop="type">
              <up-input v-model="form.type"
                        placeholder="请输入公告类型" />
            </up-form-item>
            <up-form-item label="状态">
              <up-radio-group v-model="form.status">
@@ -174,58 +182,47 @@
                <up-radio :name="1">正式发布</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 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-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 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-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 }"
          />
          <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>
@@ -233,581 +230,580 @@
</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";
  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 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,
  // 响应式数据
  const data = reactive({
    searchForm: {
      title: "",
      type: undefined,
      status: undefined,
    },
    form: {
      id: undefined,
      title: "",
      type: null,
      content: "",
      status: 0,
      priority: 1,
      remark: "",
      expirationDate: "",
    };
  }
  dialogVisible.value = true;
};
    },
    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 handleEdit = (row) => {
  if (isNoticeExpired(row)) {
    uni.showToast({
      title: "已过期的公告不可编辑",
      icon: "none"
  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();
          });
        }
      },
    });
    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"
  // 预留批量删除(目前未实现选中逻辑,仅占位)
  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();
          });
          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"
        } else {
          // 新增模式
          addNotice(form.value).then(res => {
            uni.showToast({
              title: "新增成功",
              icon: "success",
            });
            resetTable();
          });
          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 {
        // 新增模式
        addNotice(form.value).then(res => {
          uni.showToast({
            title: "新增成功",
            icon: "success"
          });
          resetTable();
        })
        holidayNotices.value = records;
      }
      dialogVisible.value = false;
    }
    });
  };
  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();
  });
};
const totalNoticeCount = ref(0)
const fetchCount = () => {
  getCount().then(res => {
    totalNoticeCount.value = res.data.reduce((total, item) => total + item.count, 0);
  // 上划加载更多
  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;
    });
  });
}
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%;
  .notice-page {
    min-height: 100vh;
    background: #f5f7fa;
    padding-bottom: 16px;
    display: flex;
    gap: 10px;
    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>