zhangwencui
2026-06-04 fbba46986e98abe68cd2460d1ed2e296cc4ad269
src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
@@ -1,18 +1,17 @@
<template>
  <div>
    <!-- 申请类型选择 -->
    <el-card class="type-card">
      <div class="type-selector">
        <div
            v-for="type in applicationTypes"
            :key="type.value"
            class="type-item"
            :class="{ active: currentType === type.value }"
            @click="changeType(type.value)"
        >
        <div v-for="type in applicationTypes"
             :key="type.value"
             class="type-item"
             :class="{ active: currentType === type.value }"
             @click="changeType(type.value)">
          <div class="type-icon">
            <el-icon :size="24"><component :is="type.icon"/></el-icon>
            <el-icon :size="24">
              <component :is="type.icon" />
            </el-icon>
          </div>
          <div class="type-info">
            <div class="type-name">{{ type.name }}</div>
@@ -21,491 +20,512 @@
        </div>
      </div>
    </el-card>
    <!-- 会议申请表单 -->
    <el-card>
      <div class="form-header">
        <h3>{{ getCurrentTypeName() }}申请</h3>
      </div>
      <el-form
          ref="meetingFormRef"
          :model="meetingForm"
          :rules="rules"
          label-width="100px"
      >
      <el-form ref="meetingFormRef"
               :model="meetingForm"
               :rules="rules"
               label-width="100px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="会议主题" prop="title">
              <el-input v-model="meetingForm.title" placeholder="请输入会议主题"/>
            <el-form-item label="会议主题"
                          prop="title">
              <el-input v-model="meetingForm.title"
                        placeholder="请输入会议主题" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="会议室" prop="roomId">
              <el-select v-model="meetingForm.roomId" placeholder="请选择会议室" style="width: 100%">
                <el-option
                    v-for="room in meetingRooms"
                    :key="room.id"
                    :label="`${room.name} (${room.location})`"
                    :value="room.id"
                />
            <el-form-item label="会议室"
                          prop="roomId">
              <el-select v-model="meetingForm.roomId"
                         placeholder="请选择会议室"
                         style="width: 100%">
                <el-option v-for="room in meetingRooms"
                           :key="room.id"
                           :label="`${room.name} (${room.location})`"
                           :value="room.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="主持人" prop="host">
              <el-input v-model="meetingForm.host" placeholder="请输入主持人姓名"/>
            <el-form-item label="主持人"
                          prop="host">
              <el-input v-model="meetingForm.host"
                        placeholder="请输入主持人姓名" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="会议日期" prop="meetingDate">
              <el-date-picker
                  v-model="meetingForm.meetingDate"
                  type="date"
                  placeholder="请选择会议日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  :disabled-date="disabledDate"
                  style="width: 100%"
              />
            <el-form-item label="会议日期"
                          prop="meetingDate">
              <el-date-picker v-model="meetingForm.meetingDate"
                              type="date"
                              placeholder="请选择会议日期"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              :disabled-date="disabledDate"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <!-- 空列,保持布局 -->
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="开始时间" prop="startTime">
              <el-select
                  v-model="meetingForm.startTime"
                  placeholder="请选择开始时间"
                  style="width: 100%"
              >
                <el-option
                    v-for="time in startTimeOptions"
                    :key="time.value"
                    :label="time.label"
                    :value="time.value"
                />
            <el-form-item label="开始时间"
                          prop="startTime">
              <el-select v-model="meetingForm.startTime"
                         placeholder="请选择开始时间"
                         style="width: 100%">
                <el-option v-for="time in startTimeOptions"
                           :key="time.value"
                           :label="time.label"
                           :value="time.value" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="结束时间" prop="endTime">
              <el-select
                  v-model="meetingForm.endTime"
                  placeholder="请选择结束时间"
                  style="width: 100%"
              >
                <el-option
                    v-for="time in endTimeOptions"
                    :key="time.value"
                    :label="time.label"
                    :value="time.value"
                />
            <el-form-item label="结束时间"
                          prop="endTime">
              <el-select v-model="meetingForm.endTime"
                         placeholder="请选择结束时间"
                         style="width: 100%">
                <el-option v-for="time in endTimeOptions"
                           :key="time.value"
                           :label="time.label"
                           :value="time.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="参会人员" prop="participants">
          <el-select
              v-model="meetingForm.participants"
              multiple
              filterable
              placeholder="请选择参会人员"
              style="width: 100%"
          >
            <el-option
                v-for="person in employees"
                :key="person.id"
                :label="`${person.staffName}${person.postName ? ` (${person.postName})` : ''}`"
                :value="person.id"
            />
        <el-form-item label="参会人员"
                      prop="participants">
          <el-select v-model="meetingForm.participants"
                     multiple
                     filterable
                     placeholder="请选择参会人员"
                     style="width: 100%">
            <el-option v-for="user in users"
                       :key="user.userId"
                       :label="user.deptNames ? `${user.nickName} (${user.deptNames})` : user.nickName"
                       :value="user.userId" />
          </el-select>
        </el-form-item>
        <el-form-item label="会议说明" prop="description">
          <el-input
              v-model="meetingForm.description"
              type="textarea"
              :rows="4"
              placeholder="请输入会议说明"
          />
        <el-form-item label="会议说明"
                      prop="description">
          <el-input v-model="meetingForm.description"
                    type="textarea"
                    :rows="4"
                    placeholder="请输入会议说明" />
        </el-form-item>
      </el-form>
      <div class="form-footer">
        <el-button @click="resetForm">重置</el-button>
        <el-button type="primary" @click="submitForm">提交</el-button>
        <el-button type="primary"
                   @click="submitForm">提交</el-button>
      </div>
    </el-card>
  </div>
</template>
<script setup>
import {ref, reactive, onMounted, computed, watch} from 'vue'
import {ElMessage} from 'element-plus'
import {Plus, Document, Promotion, Bell} from '@element-plus/icons-vue'
import {getRoomEnum, saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js'
import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js";
  import { ref, reactive, onMounted, computed, watch } from "vue";
  import { ElMessage } from "element-plus";
  import { Plus, Document, Promotion, Bell } from "@element-plus/icons-vue";
  import {
    getRoomEnum,
    saveMeetingApplication,
  } from "@/api/collaborativeApproval/meeting.js";
  import { userListNoPageByTenantId } from "@/api/system/user.js";
// 当前申请类型
const currentType = ref('department') // approval: 审批流程, department: 部门级, notification: 通知发布
  // 当前申请类型
  const currentType = ref("department"); // approval: 审批流程, department: 部门级, notification: 通知发布
// 申请类型选项
const applicationTypes = ref([
  {
    value: 'approval',
    name: '审批流程会议',
    desc: '需要经过多级审批的会议申请',
    icon: Document
  },
  {
    value: 'department',
    name: '部门级会议',
    desc: '部门内部会议申请流程',
    icon: Promotion
  },
  {
    value: 'notification',
    name: '会议通知',
    desc: '无需审批直接发布的会议通知',
    icon: Bell
  }
])
  // 申请类型选项
  const applicationTypes = ref([
    {
      value: "approval",
      name: "审批流程会议",
      desc: "需要经过多级审批的会议申请",
      icon: Document,
    },
    {
      value: "department",
      name: "部门级会议",
      desc: "部门内部会议申请流程",
      icon: Promotion,
    },
    {
      value: "notification",
      name: "会议通知",
      desc: "无需审批直接发布的会议通知",
      icon: Bell,
    },
  ]);
// 表单数据
const meetingForm = reactive({
  title: '',
  type: '',
  roomId: '',
  host: '',
  meetingDate: '',
  startTime: '',
  endTime: '',
  participants: [],
  description: ''
})
  // 表单数据
  const meetingForm = reactive({
    title: "",
    type: "",
    roomId: "",
    host: "",
    meetingDate: "",
    startTime: "",
    endTime: "",
    participants: [],
    description: "",
  });
// 表单引用
const meetingFormRef = ref(null)
  // 表单引用
  const meetingFormRef = ref(null);
// 会议室列表
const meetingRooms = ref([])
  // 会议室列表
  const meetingRooms = ref([]);
// 员工列表
const employees = ref([])
  // 用户列表(系统管理-用户管理)
  const users = ref([]);
// 时间选项(以半小时为间隔)
const timeOptions = ref([])
  // 时间选项(以半小时为间隔)
  const timeOptions = ref([]);
const getTimeInMinutes = (time) => {
  if (!time) return -1
  const [hour, minute] = time.split(':').map(Number)
  return hour * 60 + minute
}
  const getTimeInMinutes = time => {
    if (!time) return -1;
    const [hour, minute] = time.split(":").map(Number);
    return hour * 60 + minute;
  };
const isToday = (dateText) => {
  if (!dateText) return false
  const [year, month, day] = dateText.split('-').map(Number)
  const now = new Date()
  return year === now.getFullYear() && month === now.getMonth() + 1 && day === now.getDate()
}
  const isToday = dateText => {
    if (!dateText) return false;
    const [year, month, day] = dateText.split("-").map(Number);
    const now = new Date();
    return (
      year === now.getFullYear() &&
      month === now.getMonth() + 1 &&
      day === now.getDate()
    );
  };
const validateStartTime = (_rule, value, callback) => {
  if (!value) {
    callback()
    return
  }
  if (isToday(meetingForm.meetingDate)) {
    const now = new Date()
    const currentMinutes = now.getHours() * 60 + now.getMinutes()
    if (getTimeInMinutes(value) > currentMinutes) {
      callback(new Error('当天开始时间不能晚于当前时间'))
      return
  const validateStartTime = (_rule, value, callback) => {
    if (!value) {
      callback();
      return;
    }
  }
  callback()
}
const validateEndTime = (_rule, value, callback) => {
  if (!value || !meetingForm.startTime) {
    callback()
    return
  }
  if (getTimeInMinutes(value) <= getTimeInMinutes(meetingForm.startTime)) {
    callback(new Error('结束时间必须大于开始时间'))
    return
  }
  callback()
}
// 表单校验规则
const rules = {
  title: [{required: true, message: '请输入会议主题', trigger: 'blur'}],
  roomId: [{required: true, message: '请选择会议室', trigger: 'change'}],
  host: [{required: true, message: '请输入主持人', trigger: 'blur'}],
  meetingDate: [{required: true, message: '请选择会议日期', trigger: 'change'}],
  startTime: [
    {required: true, message: '请选择开始时间', trigger: 'change'},
    {validator: validateStartTime, trigger: 'change'}
  ],
  endTime: [
    {required: true, message: '请选择结束时间', trigger: 'change'},
    {validator: validateEndTime, trigger: 'change'}
  ],
  participants: [{required: true, message: '请选择参会人员', trigger: 'change'}]
}
const startTimeOptions = computed(() => {
  if (!isToday(meetingForm.meetingDate)) {
    return timeOptions.value
  }
  const now = new Date()
  const currentMinutes = now.getHours() * 60 + now.getMinutes()
  return timeOptions.value.filter(item => getTimeInMinutes(item.value) <= currentMinutes)
})
const endTimeOptions = computed(() => {
  if (!meetingForm.startTime) {
    return timeOptions.value
  }
  const startMinutes = getTimeInMinutes(meetingForm.startTime)
  return timeOptions.value.filter(item => getTimeInMinutes(item.value) > startMinutes)
})
// 初始化时间选项
const initTimeOptions = () => {
  const options = []
  const now = new Date()
  const currentHour = now.getHours()
  const currentMinute = now.getMinutes()
  // meetingDate 是 "yyyy-MM-dd"
  const meetingDate = new Date(meetingForm.meetingDate)
  const isSameDay =
    now.getFullYear() === meetingDate.getFullYear() &&
    now.getMonth() === meetingDate.getMonth() &&
    now.getDate() === meetingDate.getDate()
  console.log('是否同一天:', isSameDay)
  for (let hour = 8; hour <= 18; hour++) {
    // 开始时间必须晚于当前时间
    if (hour < currentHour && isSameDay) {
      continue
    if (isToday(meetingForm.meetingDate)) {
      const now = new Date();
      const currentMinutes = now.getHours() * 60 + now.getMinutes();
      if (getTimeInMinutes(value) > currentMinutes) {
        callback(new Error("当天开始时间不能晚于当前时间"));
        return;
      }
    }
    if (hour === currentHour && currentMinute > 30 && isSameDay) {
      continue
    }
    // 每个小时添加两个选项:整点和半点
    options.push({
      value: `${hour.toString().padStart(2, '0')}:00`,
      label: `${hour.toString().padStart(2, '0')}:00`
    })
    if (hour < 18) { // 18:00之后没有半点选项
    callback();
  };
  const validateEndTime = (_rule, value, callback) => {
    if (!value || !meetingForm.startTime) {
      callback();
      return;
    }
    if (getTimeInMinutes(value) <= getTimeInMinutes(meetingForm.startTime)) {
      callback(new Error("结束时间必须大于开始时间"));
      return;
    }
    callback();
  };
  // 表单校验规则
  const rules = {
    title: [{ required: true, message: "请输入会议主题", trigger: "blur" }],
    roomId: [{ required: true, message: "请选择会议室", trigger: "change" }],
    host: [{ required: true, message: "请输入主持人", trigger: "blur" }],
    meetingDate: [
      { required: true, message: "请选择会议日期", trigger: "change" },
    ],
    startTime: [
      { required: true, message: "请选择开始时间", trigger: "change" },
      { validator: validateStartTime, trigger: "change" },
    ],
    endTime: [
      { required: true, message: "请选择结束时间", trigger: "change" },
      { validator: validateEndTime, trigger: "change" },
    ],
    participants: [
      { required: true, message: "请选择参会人员", trigger: "change" },
    ],
  };
  const startTimeOptions = computed(() => {
    if (!isToday(meetingForm.meetingDate)) {
      return timeOptions.value;
    }
    const now = new Date();
    const currentMinutes = now.getHours() * 60 + now.getMinutes();
    return timeOptions.value.filter(
      item => getTimeInMinutes(item.value) <= currentMinutes
    );
  });
  const endTimeOptions = computed(() => {
    if (!meetingForm.startTime) {
      return timeOptions.value;
    }
    const startMinutes = getTimeInMinutes(meetingForm.startTime);
    return timeOptions.value.filter(
      item => getTimeInMinutes(item.value) > startMinutes
    );
  });
  // 初始化时间选项
  const initTimeOptions = () => {
    const options = [];
    const now = new Date();
    const currentHour = now.getHours();
    const currentMinute = now.getMinutes();
    // meetingDate 是 "yyyy-MM-dd"
    const meetingDate = new Date(meetingForm.meetingDate);
    const isSameDay =
      now.getFullYear() === meetingDate.getFullYear() &&
      now.getMonth() === meetingDate.getMonth() &&
      now.getDate() === meetingDate.getDate();
    console.log("是否同一天:", isSameDay);
    for (let hour = 8; hour <= 18; hour++) {
      // 开始时间必须晚于当前时间
      if (hour < currentHour && isSameDay) {
        continue;
      }
      if (hour === currentHour && currentMinute > 30 && isSameDay) {
        continue;
      }
      // 每个小时添加两个选项:整点和半点
      options.push({
        value: `${hour.toString().padStart(2, '0')}:30`,
        label: `${hour.toString().padStart(2, '0')}:30`
      })
        value: `${hour.toString().padStart(2, "0")}:00`,
        label: `${hour.toString().padStart(2, "0")}:00`,
      });
      if (hour < 18) {
        // 18:00之后没有半点选项
        options.push({
          value: `${hour.toString().padStart(2, "0")}:30`,
          label: `${hour.toString().padStart(2, "0")}:30`,
        });
      }
    }
  }
  timeOptions.value = options
}
    timeOptions.value = options;
  };
watch(() => meetingForm.meetingDate, () => {
  if (meetingForm.startTime && !startTimeOptions.value.some(item => item.value === meetingForm.startTime)) {
    meetingForm.startTime = ''
  }
  if (meetingForm.endTime && !endTimeOptions.value.some(item => item.value === meetingForm.endTime)) {
    meetingForm.endTime = ''
  }
  if (meetingForm.startTime) {
    meetingFormRef.value?.validateField('startTime')
  }
  if (meetingForm.endTime) {
    meetingFormRef.value?.validateField('endTime')
  }
  initTimeOptions()
})
watch(() => meetingForm.startTime, () => {
  if (meetingForm.endTime && getTimeInMinutes(meetingForm.endTime) <= getTimeInMinutes(meetingForm.startTime)) {
    meetingForm.endTime = ''
  }
  if (meetingForm.endTime) {
    meetingFormRef.value?.validateField('endTime')
  }
})
// 禁用日期(禁用今天之前的日期)
const disabledDate = (time) => {
  // 禁用今天之前的日期
  return time.getTime() < Date.now() - 86400000
}
// 切换申请类型
const changeType = (type) => {
  currentType.value = type
}
// 获取当前类型名称
const getCurrentTypeName = () => {
  const type = applicationTypes.value.find(t => t.value === currentType.value)
  return type ? type.name : ''
}
// 重置表单
const resetForm = () => {
  meetingFormRef.value?.resetFields()
}
// 提交表单
const submitForm = () => {
  meetingFormRef.value?.validate((valid) => {
    if (valid) {
      let formData = {...meetingForm}
      formData.applicationType = currentType.value
      formData.startTime = `${meetingForm.meetingDate} ${meetingForm.startTime}:00`
      formData.endTime = `${meetingForm.meetingDate} ${meetingForm.endTime}:00`
      formData.participants = JSON.stringify(formData.participants)
      console.log(formData)
      saveMeetingApplication(formData).then(() => {
        // 模拟提交操作
        ElMessage.success(`${getCurrentTypeName()}提交成功`)
        // 根据不同类型执行不同操作
        switch (currentType.value) {
          case 'approval':
            ElMessage.info('会议已提交审批流程')
            break
          case 'department':
            ElMessage.info('部门级会议申请已提交')
            break
          case 'notification':
            ElMessage.info('会议通知已发布')
            break
        }
        resetForm()
      })
  watch(
    () => meetingForm.meetingDate,
    () => {
      if (
        meetingForm.startTime &&
        !startTimeOptions.value.some(item => item.value === meetingForm.startTime)
      ) {
        meetingForm.startTime = "";
      }
      if (
        meetingForm.endTime &&
        !endTimeOptions.value.some(item => item.value === meetingForm.endTime)
      ) {
        meetingForm.endTime = "";
      }
      if (meetingForm.startTime) {
        meetingFormRef.value?.validateField("startTime");
      }
      if (meetingForm.endTime) {
        meetingFormRef.value?.validateField("endTime");
      }
      initTimeOptions();
    }
  })
}
  );
// 页面加载时初始化
onMounted(() => {
  initTimeOptions()
  getRoomEnum().then(res => {
    meetingRooms.value = res.data
  })
  staffOnJobListPage({
    current: -1,
    size: -1,
    staffState: 1
  }).then(res => {
    employees.value = res.data.records.sort((a, b) => (a.postName || '').localeCompare(b.postName || ''))
  })
})
  watch(
    () => meetingForm.startTime,
    () => {
      if (
        meetingForm.endTime &&
        getTimeInMinutes(meetingForm.endTime) <=
          getTimeInMinutes(meetingForm.startTime)
      ) {
        meetingForm.endTime = "";
      }
      if (meetingForm.endTime) {
        meetingFormRef.value?.validateField("endTime");
      }
    }
  );
  // 禁用日期(禁用今天之前的日期)
  const disabledDate = time => {
    // 禁用今天之前的日期
    return time.getTime() < Date.now() - 86400000;
  };
  // 切换申请类型
  const changeType = type => {
    currentType.value = type;
  };
  // 获取当前类型名称
  const getCurrentTypeName = () => {
    const type = applicationTypes.value.find(t => t.value === currentType.value);
    return type ? type.name : "";
  };
  // 重置表单
  const resetForm = () => {
    meetingFormRef.value?.resetFields();
  };
  // 提交表单
  const submitForm = () => {
    meetingFormRef.value?.validate(valid => {
      if (valid) {
        let formData = { ...meetingForm };
        formData.applicationType = currentType.value;
        formData.startTime = `${meetingForm.meetingDate} ${meetingForm.startTime}:00`;
        formData.endTime = `${meetingForm.meetingDate} ${meetingForm.endTime}:00`;
        formData.participants = JSON.stringify(formData.participants);
        console.log(formData);
        saveMeetingApplication(formData).then(() => {
          // 模拟提交操作
          ElMessage.success(`${getCurrentTypeName()}提交成功`);
          // 根据不同类型执行不同操作
          switch (currentType.value) {
            case "approval":
              ElMessage.info("会议已提交审批流程");
              break;
            case "department":
              ElMessage.info("部门级会议申请已提交");
              break;
            case "notification":
              ElMessage.info("会议通知已发布");
              break;
          }
          resetForm();
        });
      }
    });
  };
  // 页面加载时初始化
  onMounted(() => {
    initTimeOptions();
    getRoomEnum().then(res => {
      meetingRooms.value = res.data;
    });
    userListNoPageByTenantId().then(res => {
      const list = Array.isArray(res?.data) ? res.data : [];
      users.value = list
        .map(item => ({
          userId: item?.userId,
          nickName:
            item?.nickName || item?.userName || String(item?.userId ?? ""),
          deptNames: item?.deptNames || "",
        }))
        .filter(
          item =>
            item.userId !== null && item.userId !== undefined && item.nickName
        )
        .sort((a, b) => String(a.nickName).localeCompare(String(b.nickName)));
    });
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
}
  .app-container {
    padding: 20px;
  }
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}
  .page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
  }
.page-header h2 {
  margin: 0;
  color: #303133;
}
  .page-header h2 {
    margin: 0;
    color: #303133;
  }
.type-card {
  margin-bottom: 20px;
}
  .type-card {
    margin-bottom: 20px;
  }
.type-selector {
  display: flex;
  gap: 20px;
}
  .type-selector {
    display: flex;
    gap: 20px;
  }
.type-item {
  flex: 1;
  display: flex;
  align-items: center;
  padding: 20px;
  border: 1px solid #ebeef5;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.3s;
}
  .type-item {
    flex: 1;
    display: flex;
    align-items: center;
    padding: 20px;
    border: 1px solid #ebeef5;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.3s;
  }
.type-item:hover {
  border-color: #409eff;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
  .type-item:hover {
    border-color: #409eff;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  }
.type-item.active {
  border-color: #409eff;
  background-color: #ecf5ff;
}
  .type-item.active {
    border-color: #409eff;
    background-color: #ecf5ff;
  }
.type-icon {
  margin-right: 15px;
  color: #409eff;
}
  .type-icon {
    margin-right: 15px;
    color: #409eff;
  }
.type-name {
  font-size: 16px;
  font-weight: 500;
  color: #303133;
  margin-bottom: 5px;
}
  .type-name {
    font-size: 16px;
    font-weight: 500;
    color: #303133;
    margin-bottom: 5px;
  }
.type-desc {
  font-size: 14px;
  color: #909399;
}
  .type-desc {
    font-size: 14px;
    color: #909399;
  }
.form-header {
  margin-bottom: 20px;
  padding-bottom: 15px;
  border-bottom: 1px solid #ebeef5;
}
  .form-header {
    margin-bottom: 20px;
    padding-bottom: 15px;
    border-bottom: 1px solid #ebeef5;
  }
.form-header h3 {
  margin: 0;
  color: #303133;
}
  .form-header h3 {
    margin: 0;
    color: #303133;
  }
.form-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 30px;
  padding-top: 20px;
  border-top: 1px solid #ebeef5;
}
  .form-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 30px;
    padding-top: 20px;
    border-top: 1px solid #ebeef5;
  }
</style>