zouyu
8 天以前 1c0863efe062af3ebcdecb8c10568d779f5c8295
src/views/personnelManagement/selfService/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,800 @@
<template>
  <div class="app-container self-service-container">
    <!-- åŠŸèƒ½å¯¼èˆªå¡ç‰‡ -->
    <el-row :gutter="20" class="nav-cards">
      <el-col :span="6" v-for="(item, index) in navItems" :key="index">
        <el-card class="nav-card" @click="handleNavClick(item.type)">
          <div class="nav-content">
            <el-icon :size="40" class="nav-icon">
              <component :is="item.icon" />
            </el-icon>
            <h3>{{ item.title }}</h3>
            <p>{{ item.desc }}</p>
          </div>
        </el-card>
      </el-col>
    </el-row>
    <!-- ä¸»è¦å†…容区域 -->
    <div class="main-content">
      <!-- è€ƒå‹¤è®°å½• -->
      <el-card v-if="currentView === 'attendance'" class="content-card">
        <template #header>
          <div class="card-header">
            <span>个人考勤记录</span>
            <el-button type="primary" @click="addAttendanceRecord">新增记录</el-button>
          </div>
        </template>
        <el-table :data="attendanceData" style="width: 100%" :loading="tableLoading">
          <el-table-column prop="date" label="日期"  />
          <el-table-column prop="checkIn" label="签到时间"  />
          <el-table-column prop="checkOut" label="签退时间"  />
          <el-table-column prop="workHours" label="工作时长" width="100" />
          <el-table-column prop="status" label="状态" width="100">
            <template #default="scope">
              <el-tag :type="scope.row.status === '正常' ? 'success' : 'danger'">
                {{ scope.row.status }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="150">
            <template #default="scope">
              <el-button size="small" @click="editAttendanceRecord(scope.row)">编辑</el-button>
              <el-button size="small" type="danger" @click="deleteAttendanceRecord(scope.row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-card>
      <!-- è–ªèµ„单 -->
      <el-card v-if="currentView === 'salary'" class="content-card">
        <template #header>
          <div class="card-header">
            <span>薪资单查询</span>
            <el-date-picker v-model="payDateStr" type="month" placeholder="选择月份" value-format="YYYY-MM" format="YYYY-MM" @change="changMonth"/>
          </div>
        </template>
        <el-table :data="salaryData" style="width: 100%">
          <el-table-column prop="payDate" label="月份"  />
          <el-table-column prop="basicSalary" label="基本工资"  />
          <el-table-column prop="bonus" label="奖金"  />
          <el-table-column prop="deduction" label="扣款"  />
          <el-table-column prop="actualWages" label="实发工资"  />
          <el-table-column prop="status" label="状态" >
            <template #default="scope">
              <el-tag :type="scope.row.status === '已发放' ? 'success' : 'warning'">
                {{ scope.row.status }}
              </el-tag>
            </template>
          </el-table-column>
        </el-table>
      </el-card>
      <!-- å‡æœŸç”³è¯· -->
      <el-card v-if="currentView === 'leave'" class="content-card">
        <template #header>
          <div class="card-header">
            <span>假期申请管理</span>
            <el-button type="primary" @click="openLeaveForm">申请假期</el-button>
          </div>
        </template>
        <el-table :data="leaveData" style="width: 100%">
          <el-table-column prop="type" label="假期类型"  />
          <el-table-column prop="startDate" label="开始日期"  />
          <el-table-column prop="endDate" label="结束日期"  />
          <el-table-column prop="days" label="天数" width="80" />
          <el-table-column prop="reason" label="申请原因" />
          <el-table-column prop="status" label="审批状态" width="100">
            <template #default="scope">
              <el-tag :type="getStatusType(scope.row.status)">
                {{ scope.row.status }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="150">
            <template #default="scope">
              <el-button size="small" @click="editLeaveRecord(scope.row)">编辑</el-button>
              <el-button size="small" type="danger" @click="deleteLeaveRecord(scope.row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-card>
      <!-- ä¸ªäººä¿¡æ¯ -->
      <el-card v-if="currentView === 'profile'" class="content-card">
        <template #header>
          <div class="card-header">
            <span>个人信息维护</span>
            <el-button type="primary" @click="editProfileForm">编辑信息</el-button>
          </div>
        </template>
        <el-descriptions :column="2" border>
          <el-descriptions-item label="姓名">{{ profile.name }}</el-descriptions-item>
          <el-descriptions-item label="工号">{{ profile.employeeId }}</el-descriptions-item>
          <el-descriptions-item label="部门">{{ profile.department }}</el-descriptions-item>
          <el-descriptions-item label="职位">{{ profile.position }}</el-descriptions-item>
          <el-descriptions-item label="入职日期">{{ profile.hireDate }}</el-descriptions-item>
          <el-descriptions-item label="联系电话">{{ profile.phone }}</el-descriptions-item>
          <el-descriptions-item label="邮箱">{{ profile.email }}</el-descriptions-item>
          <el-descriptions-item label="地址">{{ profile.adress }}</el-descriptions-item>
        </el-descriptions>
      </el-card>
    </div>
    <!-- å‡æœŸç”³è¯·å¼¹çª— -->
    <el-dialog v-model="showLeaveDialog" :title="leaveOperationType === 'add' ? '申请假期' : '编辑假期'" width="500px">
      <el-form :model="leaveForm" label-width="100px">
        <el-form-item label="假期类型">
          <el-select v-model="leaveForm.type" placeholder="请选择假期类型">
            <el-option label="年假" value="年假" />
            <el-option label="病假" value="病假" />
            <el-option label="调休" value="调休" />
            <el-option label="事假" value="事假" />
          </el-select>
        </el-form-item>
        <el-form-item label="开始日期">
          <el-date-picker v-model="leaveForm.startDate" type="date" placeholder="选择开始日期" />
        </el-form-item>
        <el-form-item label="结束日期">
          <el-date-picker v-model="leaveForm.endDate" type="date" placeholder="选择结束日期" />
        </el-form-item>
        <el-form-item label="申请原因">
          <el-input v-model="leaveForm.reason" type="textarea" rows="3" />
        </el-form-item>
        <!-- <el-form-item label="审批状态">
          <el-select v-model="leaveForm.status" placeholder="请选择审批状态">
            <el-option label="审批中" value="审批中" />
            <el-option label="已通过" value="已通过" />
            <el-option label="已拒绝" value="已拒绝" />
          </el-select>
        </el-form-item> -->
      </el-form>
      <template #footer>
        <el-button @click="showLeaveDialog = false">取消</el-button>
        <el-button type="primary" @click="submitLeaveApplication">提交申请</el-button>
      </template>
    </el-dialog>
    <!-- æ–°å¢ž-编辑考勤记录弹窗 -->
    <el-dialog v-model="showAttendanceDialog" :title="operationType === 'add' ? '新增考勤记录' : '编辑考勤记录'" width="500px">
      <el-form :model="attendanceForm" :rules="attendanceRules" ref="attendanceFormRef" label-width="100px">
        <el-form-item label="日期" prop="date">
          <el-date-picker v-model="attendanceForm.date" type="date" value-format="YYYY-MM-DD" format="YYYY-MM-DD" placeholder="选择日期" />
        </el-form-item>
        <el-form-item label="签到时间" prop="checkIn">
          <el-time-picker v-model="attendanceForm.checkIn" placeholder="选择签到时间" format="HH:mm" value-format="HH:mm" />
        </el-form-item>
        <el-form-item label="签退时间" prop="checkOut">
          <el-time-picker v-model="attendanceForm.checkOut" placeholder="选择签退时间" format="HH:mm" value-format="HH:mm" />
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-select v-model="attendanceForm.status" placeholder="请选择状态">
            <el-option label="正常" value="正常" />
            <el-option label="迟到" value="迟到" />
            <el-option label="早退" value="早退" />
            <el-option label="缺勤" value="缺勤" />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showAttendanceDialog = false">取消</el-button>
        <el-button type="primary" @click="submitAttendance">提交</el-button>
      </template>
    </el-dialog>
    <!-- ä¸ªäººä¿¡æ¯ç¼–辑弹窗 -->
    <el-dialog v-model="editProfile" title="编辑个人信息" width="500px">
      <el-form :model="profileForm" label-width="100px">
        <el-form-item label="姓名">
          <el-input v-model="profileForm.name" />
        </el-form-item>
        <el-form-item label="联系电话">
          <el-input v-model="profileForm.phone" />
        </el-form-item>
        <el-form-item label="邮箱">
          <el-input v-model="profileForm.email" />
        </el-form-item>
        <el-form-item label="地址">
          <el-input v-model="profileForm.adress" type="textarea" rows="2" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="editProfile = false">取消</el-button>
        <el-button type="primary" @click="saveProfile">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, watch, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
  Calendar,
  Money,
  Clock,
  User
} from '@element-plus/icons-vue'
import { personalAttendanceRecordsListPage, personalAttendanceRecordsAdd, personalAttendanceRecordsUpdate, personalAttendanceRecordsDelete, holidayApplicationListPage, holidayApplicationAdd, holidayApplicationUpdate, holidayApplicationDelete } from '@/api/personnelManagement/selfService'
import { compensationListPage, compensationAdd, compensationUpdate, compensationDelete } from '@/api/personnelManagement/payrollManagement'
const { proxy } = getCurrentInstance()
import { getUserProfile } from '@/api/system/user.js'
import {staffOnJobListPage, updateStaffOnJob} from "@/api/personnelManagement/staffOnJob.js";
const tableLoading = ref(false)
// åˆ†é¡µå‚æ•°
const page = reactive({
  current: 1,
  size: 10,
  total: 0
})
// å½“前视图
const currentView = ref('attendance')
// å¯¼èˆªé¡¹
const navItems = [
  { type: 'attendance', title: '考勤记录', desc: '查询个人考勤信息', icon: 'Calendar' },
  { type: 'salary', title: '薪资单', desc: '查看薪资发放记录', icon: 'Money' },
  { type: 'leave', title: '假期申请', desc: '在线申请各类假期', icon: 'Clock' },
  { type: 'profile', title: '个人信息', desc: '维护个人基本信息', icon: 'User' }
]
// è€ƒå‹¤æ•°æ®
const attendanceData = ref([])
// è–ªèµ„数据
const salaryData = ref([])
// å‡æœŸæ•°æ®
const leaveData = ref([])
const currentUser = ref()
const user= ref()
// ä¸ªäººä¿¡æ¯
const profile = ref({
  id: '',
  name: '',
  employeeId: '',
  department: '',
  position: '',
  hireDate: '',
  phone: '',
  email: '',
  adress: ''
  })
// å¼¹çª—控制
const showLeaveDialog = ref(false)
const editProfile = ref(false)
const payDateStr = ref('')
// è¡¨å•数据
const leaveForm = reactive({
  id: '',
  type: '',
  startDate: '',
  endDate: '',
  days: 0,
  reason: '',
  status: ''
})
const profileForm = reactive({
  name: "",
  email: "",
  adress: "",
  phone: "",
})
const joinForm = reactive({
  id: "",
  staffNo: "",
  staffName: "",
  email: "",
  adress: "",
  sex: "",
  nativePlace: "",
  postJob: "",
  firstStudy: "",
  profession: "",
  age: 0,
  phone: "",
  emergencyContact: "",
  emergencyContactPhone: "",
  contractTerm: 0,
  contractStartTime: "",
  contractEndTime: "",
  staffState: 1,
})
// æ–°å¢žè€ƒå‹¤è®°å½•:弹窗与表单
const operationType = ref('add')
const leaveOperationType = ref('add')
const showAttendanceDialog = ref(false)
const attendanceFormRef = ref(null)
const attendanceForm = reactive({
  id: '',
  date: '',
  checkIn: '',
  checkOut: '',
  workHours: '',
  status: '正常'
})
const attendanceRules = {
  date: [{ required: true, message: '请选择日期', trigger: 'change' }],
  checkIn: [{ required: true, message: '请选择签到时间', trigger: 'change' }],
  checkOut: [{ required: true, message: '请选择签退时间', trigger: 'change' }],
  status: [{ required: true, message: '请选择状态', trigger: 'change' }]
}
// å¤„理导航点击
const handleNavClick = (type) => {
  currentView.value = type
}
// èŽ·å–çŠ¶æ€ç±»åž‹
const getStatusType = (status) => {
  const types = {
    '已通过': 'success',
    '审批中': 'warning',
    '已拒绝': 'danger'
  }
  return types[status] || 'info'
}
// æ–°å¢žè€ƒå‹¤è®°å½•(打开弹窗并预填默认值)
const addAttendanceRecord = () => {
  operationType.value = 'add'
  attendanceForm.date = new Date().toISOString().split('T')[0]
  attendanceForm.checkIn = '09:00'
  attendanceForm.checkOut = '18:00'
  attendanceForm.status = '正常'
  showAttendanceDialog.value = true
}
// è®¡ç®—工时
const computeWorkHours = (inStr, outStr) => {
  const [inH, inM] = inStr.split(':').map(n => parseInt(n, 10))
  const [outH, outM] = outStr.split(':').map(n => parseInt(n, 10))
  const inMin = inH * 60 + inM
  const outMin = outH * 60 + outM
  const diff = Math.max(0, outMin - inMin)
  const h = Math.floor(diff / 60)
  const m = diff % 60
  return m === 0 ? `${h}小时` : `${h}小时${m}分`
}
// ç¼–辑考勤记录
const editAttendanceRecord = (row) => {
  operationType.value = 'edit'
  Object.assign(attendanceForm, row)
  showAttendanceDialog.value = true
}
// æäº¤æ–°å¢ž-编辑考勤记录
const submitAttendance = () => {
  // if (!attendanceFormRef.value) return
    const workHours = computeWorkHours(attendanceForm.checkIn, attendanceForm.checkOut)
    const newRecord = {
      date: attendanceForm.date,
      checkIn: attendanceForm.checkIn,
      checkOut: attendanceForm.checkOut,
      workHours,
      status: attendanceForm.status
    }
  if (operationType.value === 'add') {
    personalAttendanceRecordsAdd(newRecord)
    .then(res => {
      if (res.code === 200) {
        ElMessage.success('考勤记录添加成功')
        getPersonalAttendanceRecordsList()
        showAttendanceDialog.value = false
        // é‡ç½®è¡¨å•
        attendanceForm.date = ''
        attendanceForm.checkIn = ''
        attendanceForm.checkOut = ''
        attendanceForm.status = '正常'
      }
    }).catch(err => {
      ElMessage.error('考勤记录添加失败')
    })
  }else{
    attendanceForm.workHours = computeWorkHours(attendanceForm.checkIn, attendanceForm.checkOut)
    personalAttendanceRecordsUpdate(attendanceForm)
    .then(res => {
      if (res.code === 200) {
        ElMessage.success('考勤记录更新成功')
        getPersonalAttendanceRecordsList()
        showAttendanceDialog.value = false
        // é‡ç½®è¡¨å•
        attendanceForm.date = ''
        attendanceForm.checkIn = ''
        attendanceForm.checkOut = ''
        attendanceForm.status = '正常'
      }
    }).catch(err => {
      ElMessage.error('考勤记录更新失败')
    })
  }
  // attendanceFormRef.value.validate((valid) => {
  //   if (!valid) return
  // })
}
// åˆ é™¤è€ƒå‹¤è®°å½•
const deleteAttendanceRecord = (row) => {
  ElMessageBox.confirm('确定删除该考勤记录吗?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    personalAttendanceRecordsDelete(row.id)
    .then(res => {
      if (res.code === 200) {
        ElMessage.success('考勤记录删除成功')
        getPersonalAttendanceRecordsList()
      }
    }).catch(err => {
      ElMessage.error('考勤记录删除失败')
    })
  }).catch(() => {
    ElMessage({
      type: 'info',
      message: '已取消删除'
    })
  })
}
// ç”³è¯·å‡æœŸ
const openLeaveForm = () => {
  leaveOperationType.value = 'add'
  showLeaveDialog.value = true
  // leaveForm.type = ''
  // leaveForm.startDate = ''
  // leaveForm.endDate = ''
  // leaveForm.days = 0
  // leaveForm.reason = ''
  // leaveForm.status = 'warning'
}
// ç¼–辑假期记录
const editLeaveRecord = (row) => {
  leaveOperationType.value = 'edit'
  showLeaveDialog.value = true
  Object.assign(leaveForm, row)
  // ElMessage.info('编辑功能开发中...')
}
// åˆ é™¤å‡æœŸè®°å½•
const deleteLeaveRecord = (row) => {
  ElMessageBox.confirm('确定删除该假期记录吗?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    holidayApplicationDelete(row.id)
    .then(res => {
      if (res.code === 200) {
        ElMessage.success('假期记录删除成功')
        getHolidayApplicationList()
      }
    }).catch(err => {
      ElMessage.error('假期记录删除失败')
    })
  }).catch(() => {
    ElMessage({
      type: 'info',
      message: '已取消删除'
    })
  })
}
//计算假期天数
const calculateDays = () => {
  try {
    if (leaveForm.startDate && leaveForm.endDate) {
      const start = new Date(leaveForm.startDate)
      const end = new Date(leaveForm.endDate)
      leaveForm.startDate = start.toISOString().split('T')[0]
      leaveForm.endDate = end.toISOString().split('T')[0]
      if (isNaN(start.getTime()) || isNaN(end.getTime())) {
        console.warn('无效的日期格式')
        return
      }
      const diffTime = Math.abs(end - start)
      const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1
      leaveForm.days = diffDays
    }
  } catch (error) {
    console.error('计算天数失败:', error)
  }
}
// æäº¤å‡æœŸç”³è¯·
const submitLeaveApplication = () => {
  if (leaveOperationType.value === 'add') {
    if (!leaveForm.type || !leaveForm.startDate || !leaveForm.endDate || !leaveForm.reason) {
      ElMessage.warning('请填写完整信息')
      return
    }
    calculateDays()
    const newLeave = {
    type: leaveForm.type,
    startDate: leaveForm.startDate,
    endDate: leaveForm.endDate,
    days: leaveForm.days, // ç®€å•计算
    reason: leaveForm.reason,
    status: '审批中'
    }
    holidayApplicationAdd(newLeave)
    .then(res => {
      if (res.code === 200) {
        ElMessage.success('假期申请提交成功')
        getHolidayApplicationList()
        showLeaveDialog.value = false
        // é‡ç½®è¡¨å•
        Object.keys(leaveForm).forEach(key => {
          leaveForm[key] = ''
        })
      }
    }).catch(err => {
      ElMessage.error('假期申请提交失败')
    })
  }else{
    calculateDays()
    holidayApplicationUpdate(leaveForm)
    .then(res => {
      if (res.code === 200) {
        ElMessage.success('假期申请更新成功')
        getHolidayApplicationList()
        showLeaveDialog.value = false
        // é‡ç½®è¡¨å•
        Object.keys(leaveForm).forEach(key => {
          leaveForm[key] = ''
        })
      }
    }).catch(err => {
      ElMessage.error('假期申请更新失败')
    })
  }
}
// èŽ·å–ä¸ªäººä¿¡æ¯
const getProfile = () => {
  tableLoading.value = true;
  getUserProfile().then(res => {
    if (res.code === 200) {
      currentUser.value = res.data
      // console.log("----",currentUser.value)
        //得到人员列表
      staffOnJobListPage({staffState: 1}).then(res => {
          //筛选出和currentUser同名的人员
          // let tableData = res.data.records
          user.value = res.data.records.find(item => item.staffName === currentUser.value.userName)
          // console.log("++++",user.value)
          if(user.value){
            profile.value.id=user.value.id
            profile.value.name=user.value.staffName
            profile.value.employeeId=user.value.staffNo
            profile.value.phone=user.value.phone
            profile.value.email=currentUser.value.email
            profile.value.adress=user.value.adress
            profile.value.position=user.value.postJob
            profile.value.hireDate=user.value.createTime
            profile.value.department=currentUser.value.deptNames
          }
          // console.log(profile.value)
          // tableLoading.value = false;
        }).catch(err => {})
    }
  }).catch(err => {
    tableLoading.value = false;
    ElMessage.error('获取个人信息失败')
  })
}
// ä¿å­˜ä¸ªäººä¿¡æ¯
const saveProfile = async () => {
  tableLoading.value = true;
  try {
    const userRes = await getUserProfile();
    if (userRes.code === 200) {
      currentUser.value = userRes.data;
      const staffListRes = await staffOnJobListPage({ staffState: 1 });
      user.value = staffListRes.data.records.find(item => item.staffName === currentUser.value.userName);
      Object.assign(joinForm, user.value);
      joinForm.staffName = profileForm.name;
      joinForm.phone = profileForm.phone;
      joinForm.email = profileForm.email;
      joinForm.adress = profileForm.adress;
      // è°ƒç”¨æ›´æ–°ä¸ªäººä¿¡æ¯çš„æŽ¥å£
      updateStaffOnJob(user.value.id, joinForm).then(res => {
        if (res.code === 200) {
          ElMessage.success('个人信息保存成功');
          getProfile();
          editProfile.value = false;
        }
      }).catch(err => {
        ElMessage.error('个人信息保存失败');
      })
    }
  } catch (err) {
    ElMessage.error('获取个人信息失败');
  } finally {
    tableLoading.value = false;
  }
};
// ç¼–辑个人信息
const editProfileForm = () => {
  editProfile.value = true;
  Object.assign(profileForm, {
    name: profile.value.name,
    phone: profile.value.phone,
    email: profile.value.email,
    adress: profile.value.adress,
  });
};
//月份改变
const changMonth = () => {
  getCompensationList()
}
//获取考勤记录列表
const getPersonalAttendanceRecordsList = async () => {
  tableLoading.value = true
  personalAttendanceRecordsListPage(page)
  .then(res => {
    attendanceData.value = res.data.records
    page.value.total = res.data.total;
    tableLoading.value = false;
  }).catch(err => {
    tableLoading.value = false;
  })
}
//薪资单查询
const getCompensationList = async () => {
  tableLoading.value = true
  compensationListPage({...page,payDateStr:payDateStr.value})
  .then(res => {
    salaryData.value = res.data.records
    //过滤出当前月份的扣款合计
    salaryData.value.forEach(item => {
      item.deduction =0 + item.deductionAbsenteeism+item.sickLeaveDeductions+item.deductionPersonalLeave+item.forgetClockDeduct,
      item.bonus=0,
      item.status='已发放'
    })
    page.value.total = res.data.total;
    tableLoading.value = false;
  }).catch(err => {
    tableLoading.value = false;
  })
}
//获取假期申请列表
const getHolidayApplicationList = async () => {
  tableLoading.value = true
  holidayApplicationListPage(page)
  .then(res => {
    leaveData.value = res.data.records
    page.value.total = res.data.total;
    tableLoading.value = false;
  }).catch(err => {
    tableLoading.value = false;
  })
}
onMounted(() => {
  // åˆå§‹åŒ–
  getPersonalAttendanceRecordsList()
  getCompensationList()
  getHolidayApplicationList()
  getProfile()
})
</script>
<style scoped>
.self-service-container {
  padding: 20px;
  background-color: #f5f7fa;
  min-height: 100vh;
}
.page-header {
  text-align: center;
  margin-bottom: 30px;
  padding: 20px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 12px;
  color: white;
}
.page-header h2 {
  color: white;
  margin-bottom: 10px;
  font-size: 28px;
  font-weight: 600;
}
.page-header p {
  color: rgba(255, 255, 255, 0.9);
  font-size: 14px;
  margin: 0;
}
.nav-cards {
  margin-bottom: 30px;
}
.nav-card {
  cursor: pointer;
  transition: all 0.3s ease;
  border-radius: 12px;
  border: none;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.nav-card:hover {
  transform: translateY(-5px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.nav-content {
  text-align: center;
  padding: 20px;
}
.nav-icon {
  color: #409EFF;
  margin-bottom: 15px;
}
.nav-content h3 {
  margin: 0 0 10px 0;
  color: #303133;
  font-size: 18px;
}
.nav-content p {
  margin: 0;
  color: #909399;
  font-size: 14px;
}
.main-content {
  margin-bottom: 30px;
}
.content-card {
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  border: none;
}
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 600;
  color: #303133;
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .self-service-container {
    padding: 10px;
  }
  .nav-cards .el-col {
    margin-bottom: 15px;
  }
  .page-header h2 {
    font-size: 24px;
  }
}
</style>