|
@@ -0,0 +1,504 @@
|
|
|
+<template>
|
|
|
+ <div class="min-h-screen bg-gray-50 dark:bg-gray-900 p-6">
|
|
|
+ <!-- 顶部导航栏 -->
|
|
|
+ <div class="flex justify-between items-center mb-6">
|
|
|
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-white">薪资管理</h1>
|
|
|
+ <div class="flex space-x-4">
|
|
|
+ <button
|
|
|
+ @click="showSalaryAdjustment = true"
|
|
|
+ class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
|
|
+ >
|
|
|
+ <PlusIcon class="h-5 w-5 inline-block mr-2" />
|
|
|
+ 薪资调整
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ @click="handleSalaryPayment"
|
|
|
+ class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
|
|
+ >
|
|
|
+ <DownloadIcon class="h-5 w-5 inline-block mr-2" />
|
|
|
+ 发放薪资
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 薪资统计卡片 -->
|
|
|
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
|
|
|
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
|
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">本月总薪资</h3>
|
|
|
+ <p class="mt-2 text-3xl font-bold text-indigo-600">¥{{ totalSalary }}</p>
|
|
|
+ <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">本月</p>
|
|
|
+ </div>
|
|
|
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
|
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">平均薪资</h3>
|
|
|
+ <p class="mt-2 text-3xl font-bold text-green-600">¥{{ averageSalary }}</p>
|
|
|
+ <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">本月</p>
|
|
|
+ </div>
|
|
|
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
|
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">最高薪资</h3>
|
|
|
+ <p class="mt-2 text-3xl font-bold text-yellow-600">¥{{ maxSalary }}</p>
|
|
|
+ <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">本月</p>
|
|
|
+ </div>
|
|
|
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
|
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">最低薪资</h3>
|
|
|
+ <p class="mt-2 text-3xl font-bold text-red-600">¥{{ minSalary }}</p>
|
|
|
+ <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">本月</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 搜索和筛选 -->
|
|
|
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
|
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">月份</label>
|
|
|
+ <input
|
|
|
+ type="month"
|
|
|
+ v-model="filters.month"
|
|
|
+ class="mt-1 block w-full border border-gray-300 dark:border-gray-600 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:bg-gray-700 dark:text-white"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">部门</label>
|
|
|
+ <select
|
|
|
+ v-model="filters.department"
|
|
|
+ class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:bg-gray-700 dark:text-white"
|
|
|
+ >
|
|
|
+ <option value="">所有部门</option>
|
|
|
+ <option v-for="dept in departments" :key="dept.id" :value="dept.id">
|
|
|
+ {{ dept.name }}
|
|
|
+ </option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">薪资范围</label>
|
|
|
+ <div class="flex space-x-2">
|
|
|
+ <input
|
|
|
+ type="number"
|
|
|
+ v-model="filters.minSalary"
|
|
|
+ placeholder="最低"
|
|
|
+ class="mt-1 block w-full border border-gray-300 dark:border-gray-600 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:bg-gray-700 dark:text-white"
|
|
|
+ />
|
|
|
+ <input
|
|
|
+ type="number"
|
|
|
+ v-model="filters.maxSalary"
|
|
|
+ placeholder="最高"
|
|
|
+ class="mt-1 block w-full border border-gray-300 dark:border-gray-600 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:bg-gray-700 dark:text-white"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">搜索</label>
|
|
|
+ <div class="mt-1 relative rounded-md shadow-sm">
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ v-model="searchText"
|
|
|
+ placeholder="搜索员工姓名..."
|
|
|
+ class="block w-full pr-10 border-gray-300 dark:border-gray-600 rounded-md focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:bg-gray-700 dark:text-white"
|
|
|
+ />
|
|
|
+ <div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
|
|
+ <SearchIcon class="h-5 w-5 text-gray-400" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 薪资列表 -->
|
|
|
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
|
|
|
+ <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
|
+ <thead class="bg-gray-50 dark:bg-gray-700">
|
|
|
+ <tr>
|
|
|
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">员工信息</th>
|
|
|
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">基本工资</th>
|
|
|
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">绩效奖金</th>
|
|
|
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">加班工资</th>
|
|
|
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">补贴</th>
|
|
|
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">扣款</th>
|
|
|
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">实发工资</th>
|
|
|
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">状态</th>
|
|
|
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">操作</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
|
|
+ <tr v-for="salary in filteredSalaries" :key="salary.id">
|
|
|
+ <td class="px-6 py-4 whitespace-nowrap">
|
|
|
+ <div class="flex items-center">
|
|
|
+ <div class="flex-shrink-0 h-10 w-10">
|
|
|
+ <div class="h-10 w-10 rounded-full bg-gray-200 flex items-center justify-center">
|
|
|
+ <UserIcon class="h-6 w-6 text-gray-500" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="ml-4">
|
|
|
+ <div class="text-sm font-medium text-gray-900 dark:text-white">{{ salary.employee.name }}</div>
|
|
|
+ <div class="text-sm text-gray-500 dark:text-gray-400">{{ getDepartmentName(salary.employee.departmentId) }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </td>
|
|
|
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
|
+ ¥{{ salary.baseSalary }}
|
|
|
+ </td>
|
|
|
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
|
+ ¥{{ salary.bonus }}
|
|
|
+ </td>
|
|
|
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
|
+ ¥{{ salary.overtimePay }}
|
|
|
+ </td>
|
|
|
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
|
+ ¥{{ salary.allowance }}
|
|
|
+ </td>
|
|
|
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
|
+ ¥{{ salary.deduction }}
|
|
|
+ </td>
|
|
|
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
|
|
|
+ ¥{{ salary.netSalary }}
|
|
|
+ </td>
|
|
|
+ <td class="px-6 py-4 whitespace-nowrap">
|
|
|
+ <span :class="getStatusClass(salary.status)" class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full">
|
|
|
+ {{ getStatusText(salary.status) }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
|
+ <button
|
|
|
+ @click="handleView(salary)"
|
|
|
+ class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300 mr-3"
|
|
|
+ >
|
|
|
+ 查看
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ @click="handleAdjust(salary)"
|
|
|
+ class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300"
|
|
|
+ >
|
|
|
+ 调整
|
|
|
+ </button>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 薪资调整弹窗 -->
|
|
|
+ <div v-if="showSalaryAdjustment" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center z-50">
|
|
|
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-2xl w-full">
|
|
|
+ <div class="p-6">
|
|
|
+ <div class="flex justify-between items-center mb-6">
|
|
|
+ <h3 class="text-lg font-medium text-gray-900 dark:text-white">薪资调整</h3>
|
|
|
+ <button
|
|
|
+ @click="showSalaryAdjustment = false"
|
|
|
+ class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300"
|
|
|
+ >
|
|
|
+ <XIcon class="h-6 w-6" />
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <form @submit.prevent="handleSubmitAdjustment" class="space-y-6">
|
|
|
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">员工</label>
|
|
|
+ <select
|
|
|
+ v-model="adjustmentForm.employeeId"
|
|
|
+ class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:bg-gray-700 dark:text-white"
|
|
|
+ >
|
|
|
+ <option v-for="emp in employees" :key="emp.id" :value="emp.id">
|
|
|
+ {{ emp.name }}
|
|
|
+ </option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">调整类型</label>
|
|
|
+ <select
|
|
|
+ v-model="adjustmentForm.type"
|
|
|
+ class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:bg-gray-700 dark:text-white"
|
|
|
+ >
|
|
|
+ <option value="base">基本工资</option>
|
|
|
+ <option value="bonus">绩效奖金</option>
|
|
|
+ <option value="allowance">补贴</option>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">调整金额</label>
|
|
|
+ <input
|
|
|
+ type="number"
|
|
|
+ v-model="adjustmentForm.amount"
|
|
|
+ class="mt-1 block w-full border border-gray-300 dark:border-gray-600 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:bg-gray-700 dark:text-white"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">生效日期</label>
|
|
|
+ <input
|
|
|
+ type="date"
|
|
|
+ v-model="adjustmentForm.effectiveDate"
|
|
|
+ class="mt-1 block w-full border border-gray-300 dark:border-gray-600 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:bg-gray-700 dark:text-white"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="md:col-span-2">
|
|
|
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">调整原因</label>
|
|
|
+ <textarea
|
|
|
+ v-model="adjustmentForm.reason"
|
|
|
+ rows="3"
|
|
|
+ class="mt-1 block w-full border border-gray-300 dark:border-gray-600 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm dark:bg-gray-700 dark:text-white"
|
|
|
+ ></textarea>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex justify-end space-x-3">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ @click="showSalaryAdjustment = false"
|
|
|
+ class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
|
|
+ >
|
|
|
+ 取消
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ type="submit"
|
|
|
+ class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
|
|
+ >
|
|
|
+ 提交
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, computed } from 'vue'
|
|
|
+import {
|
|
|
+ SearchIcon,
|
|
|
+ PlusIcon,
|
|
|
+ DownloadIcon,
|
|
|
+ XIcon,
|
|
|
+ UserIcon
|
|
|
+} from 'lucide-vue-next'
|
|
|
+
|
|
|
+// 薪资数据
|
|
|
+const salaries = ref([
|
|
|
+ {
|
|
|
+ id: 'S202401010001',
|
|
|
+ employee: {
|
|
|
+ id: 'E001',
|
|
|
+ name: '张三',
|
|
|
+ departmentId: 'D001'
|
|
|
+ },
|
|
|
+ baseSalary: 10000,
|
|
|
+ bonus: 2000,
|
|
|
+ overtimePay: 1000,
|
|
|
+ allowance: 500,
|
|
|
+ deduction: 300,
|
|
|
+ netSalary: 13200,
|
|
|
+ status: 'paid',
|
|
|
+ month: '2024-01'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'S202401010002',
|
|
|
+ employee: {
|
|
|
+ id: 'E002',
|
|
|
+ name: '李四',
|
|
|
+ departmentId: 'D002'
|
|
|
+ },
|
|
|
+ baseSalary: 8000,
|
|
|
+ bonus: 1500,
|
|
|
+ overtimePay: 800,
|
|
|
+ allowance: 400,
|
|
|
+ deduction: 200,
|
|
|
+ netSalary: 10500,
|
|
|
+ status: 'pending',
|
|
|
+ month: '2024-01'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'S202401010003',
|
|
|
+ employee: {
|
|
|
+ id: 'E003',
|
|
|
+ name: '王五',
|
|
|
+ departmentId: 'D003'
|
|
|
+ },
|
|
|
+ baseSalary: 12000,
|
|
|
+ bonus: 3000,
|
|
|
+ overtimePay: 1500,
|
|
|
+ allowance: 600,
|
|
|
+ deduction: 400,
|
|
|
+ netSalary: 16700,
|
|
|
+ status: 'paid',
|
|
|
+ month: '2024-01'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'S202401010004',
|
|
|
+ employee: {
|
|
|
+ id: 'E004',
|
|
|
+ name: '赵六',
|
|
|
+ departmentId: 'D001'
|
|
|
+ },
|
|
|
+ baseSalary: 9000,
|
|
|
+ bonus: 1800,
|
|
|
+ overtimePay: 900,
|
|
|
+ allowance: 450,
|
|
|
+ deduction: 250,
|
|
|
+ netSalary: 11900,
|
|
|
+ status: 'cancelled',
|
|
|
+ month: '2024-01'
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+// 员工数据
|
|
|
+const employees = ref([
|
|
|
+ {
|
|
|
+ id: 'E001',
|
|
|
+ name: '张三',
|
|
|
+ departmentId: 'D001',
|
|
|
+ position: '高级工程师',
|
|
|
+ baseSalary: 10000
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'E002',
|
|
|
+ name: '李四',
|
|
|
+ departmentId: 'D002',
|
|
|
+ position: '市场经理',
|
|
|
+ baseSalary: 8000
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'E003',
|
|
|
+ name: '王五',
|
|
|
+ departmentId: 'D003',
|
|
|
+ position: '人事总监',
|
|
|
+ baseSalary: 12000
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'E004',
|
|
|
+ name: '赵六',
|
|
|
+ departmentId: 'D001',
|
|
|
+ position: '前端工程师',
|
|
|
+ baseSalary: 9000
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+// 部门数据
|
|
|
+const departments = ref([
|
|
|
+ { id: 'D001', name: '技术部' },
|
|
|
+ { id: 'D002', name: '市场部' },
|
|
|
+ { id: 'D003', name: '人事部' },
|
|
|
+ { id: 'D004', name: '财务部' }
|
|
|
+])
|
|
|
+
|
|
|
+// 搜索和筛选
|
|
|
+const searchText = ref('')
|
|
|
+const filters = ref({
|
|
|
+ month: '2024-01',
|
|
|
+ department: '',
|
|
|
+ minSalary: '',
|
|
|
+ maxSalary: ''
|
|
|
+})
|
|
|
+
|
|
|
+// 薪资调整表单
|
|
|
+const showSalaryAdjustment = ref(false)
|
|
|
+const adjustmentForm = ref({
|
|
|
+ employeeId: '',
|
|
|
+ type: 'base',
|
|
|
+ amount: 0,
|
|
|
+ effectiveDate: '',
|
|
|
+ reason: ''
|
|
|
+})
|
|
|
+
|
|
|
+// 计算属性
|
|
|
+const filteredSalaries = computed(() => {
|
|
|
+ return salaries.value.filter(salary => {
|
|
|
+ const matchesSearch = !searchText.value ||
|
|
|
+ salary.employee.name.toLowerCase().includes(searchText.value.toLowerCase())
|
|
|
+ const matchesMonth = !filters.value.month || salary.month === filters.value.month
|
|
|
+ const matchesDepartment = !filters.value.department || salary.employee.departmentId === filters.value.department
|
|
|
+ const matchesSalary = (!filters.value.minSalary || salary.netSalary >= Number(filters.value.minSalary)) &&
|
|
|
+ (!filters.value.maxSalary || salary.netSalary <= Number(filters.value.maxSalary))
|
|
|
+ return matchesSearch && matchesMonth && matchesDepartment && matchesSalary
|
|
|
+ })
|
|
|
+})
|
|
|
+
|
|
|
+const totalSalary = computed(() => {
|
|
|
+ return filteredSalaries.value.reduce((sum, salary) => sum + salary.netSalary, 0)
|
|
|
+})
|
|
|
+
|
|
|
+const averageSalary = computed(() => {
|
|
|
+ return filteredSalaries.value.length > 0
|
|
|
+ ? Math.round(totalSalary.value / filteredSalaries.value.length)
|
|
|
+ : 0
|
|
|
+})
|
|
|
+
|
|
|
+const maxSalary = computed(() => {
|
|
|
+ return filteredSalaries.value.length > 0
|
|
|
+ ? Math.max(...filteredSalaries.value.map(salary => salary.netSalary))
|
|
|
+ : 0
|
|
|
+})
|
|
|
+
|
|
|
+const minSalary = computed(() => {
|
|
|
+ return filteredSalaries.value.length > 0
|
|
|
+ ? Math.min(...filteredSalaries.value.map(salary => salary.netSalary))
|
|
|
+ : 0
|
|
|
+})
|
|
|
+
|
|
|
+// 方法
|
|
|
+const getDepartmentName = (id) => {
|
|
|
+ const dept = departments.value.find(d => d.id === id)
|
|
|
+ return dept ? dept.name : '未知部门'
|
|
|
+}
|
|
|
+
|
|
|
+const getStatusClass = (status) => {
|
|
|
+ const classes = {
|
|
|
+ paid: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
|
|
|
+ pending: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
|
|
|
+ cancelled: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'
|
|
|
+ }
|
|
|
+ return classes[status] || 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200'
|
|
|
+}
|
|
|
+
|
|
|
+const getStatusText = (status) => {
|
|
|
+ const texts = {
|
|
|
+ paid: '已发放',
|
|
|
+ pending: '待发放',
|
|
|
+ cancelled: '已取消'
|
|
|
+ }
|
|
|
+ return texts[status] || '未知'
|
|
|
+}
|
|
|
+
|
|
|
+const handleView = (salary) => {
|
|
|
+ // 实现查看薪资详情的逻辑
|
|
|
+ console.log('查看薪资:', salary)
|
|
|
+ // 这里可以实现查看薪资详情的逻辑,例如打开一个详情弹窗
|
|
|
+}
|
|
|
+
|
|
|
+const handleAdjust = (salary) => {
|
|
|
+ // 设置调整表单的初始值
|
|
|
+ adjustmentForm.value.employeeId = salary.employee.id
|
|
|
+ // 打开薪资调整弹窗
|
|
|
+ showSalaryAdjustment.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const handleSubmitAdjustment = () => {
|
|
|
+ // 实现提交薪资调整的逻辑
|
|
|
+ console.log('提交薪资调整:', adjustmentForm.value)
|
|
|
+ // 这里可以实现提交薪资调整的逻辑,例如发送请求到后端API
|
|
|
+
|
|
|
+ // 关闭弹窗
|
|
|
+ showSalaryAdjustment.value = false
|
|
|
+
|
|
|
+ // 重置表单
|
|
|
+ adjustmentForm.value = {
|
|
|
+ employeeId: '',
|
|
|
+ type: 'base',
|
|
|
+ amount: 0,
|
|
|
+ effectiveDate: '',
|
|
|
+ reason: ''
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleSalaryPayment = () => {
|
|
|
+ // 实现发放薪资的逻辑
|
|
|
+ console.log('发放薪资')
|
|
|
+ // 这里可以实现发放薪资的逻辑,例如将待发放的薪资状态更新为已发放
|
|
|
+
|
|
|
+ // 模拟更新薪资状态
|
|
|
+ salaries.value = salaries.value.map(salary => {
|
|
|
+ if (salary.status === 'pending') {
|
|
|
+ return { ...salary, status: 'paid' }
|
|
|
+ }
|
|
|
+ return salary
|
|
|
+ })
|
|
|
+}
|
|
|
+</script>
|