소스 검색

admin-2025-04-17 02:06:38

genlitex 1 주 전
부모
커밋
034aaa0f64
6개의 변경된 파일1069개의 추가작업 그리고 133개의 파일을 삭제
  1. 4 0
      dist/assets/index-BDQA6D8e.js
  2. 0 0
      dist/assets/index-DezQ0Fgr.css
  3. 14 0
      dist/index.html
  4. 544 130
      package-lock.json
  5. 3 3
      src/router/index.js
  6. 504 0
      src/views/SalaryManagementView.vue

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 4 - 0
dist/assets/index-BDQA6D8e.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
dist/assets/index-DezQ0Fgr.css


+ 14 - 0
dist/index.html

@@ -0,0 +1,14 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Vite + Vue</title>
+    <script type="module" crossorigin src="/ide/proxy/6002/assets/index-BDQA6D8e.js"></script>
+    <link rel="stylesheet" crossorigin href="/ide/proxy/6002/assets/index-DezQ0Fgr.css">
+  </head>
+  <body>
+    <div id="app"></div>
+  </body>
+</html>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 544 - 130
package-lock.json


+ 3 - 3
src/router/index.js

@@ -1,11 +1,11 @@
 import { createRouter, createWebHashHistory } from 'vue-router'
-import HomeView from '../views/HomeView.vue'
+import SalaryManagementView from '../views/SalaryManagementView.vue'
 
 const routes = [
   {
     path: '/',
-    name: 'home',
-    component: HomeView
+    name: 'salary',
+    component: SalaryManagementView
   }
 ]
 

+ 504 - 0
src/views/SalaryManagementView.vue

@@ -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>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.