From fdd6bb96d8aad1ab796ce10038d170b47a19f661 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 13 十一月 2025 13:45:41 +0800
Subject: [PATCH] 1.排班管理-班次、加一个午休时间(h)、人员多选、人员从用户管理获取,加一条列表也只展示一条 2.薪资管理-只要,人员、薪资、月份三个字段

---
 src/views/index.vue                                                    |   54 +++
 src/api/personnelManagement/payrollManagement.js                       |   16 +
 src/api/system/user.js                                                 |    7 
 src/views/personnelManagement/payrollManagement/components/formDia.vue |  168 ------------
 src/views/personnelManagement/payrollManagement/index.vue              |  219 +++++++++-------
 src/views/personnelManagement/scheduling/index.vue                     |  301 +++++++++++++---------
 6 files changed, 388 insertions(+), 377 deletions(-)

diff --git a/src/api/personnelManagement/payrollManagement.js b/src/api/personnelManagement/payrollManagement.js
index c29a6b1..c7284d4 100644
--- a/src/api/personnelManagement/payrollManagement.js
+++ b/src/api/personnelManagement/payrollManagement.js
@@ -32,4 +32,20 @@
     method: "delete",
     data: query,
   });
+}
+// 瀵煎叆
+export function importData(query) {
+  return request({
+    url: "/compensationPerformance/importData",
+    method: "post",
+    data: query,
+  });
+}
+// 涓嬭浇妯$増
+export function exportTemplate(query) {
+  return request({
+    url: "/compensationPerformance/exportTemplate",
+    method: "post",
+    data: query,
+  });
 }
\ No newline at end of file
diff --git a/src/api/system/user.js b/src/api/system/user.js
index 431f6b0..1a455c7 100644
--- a/src/api/system/user.js
+++ b/src/api/system/user.js
@@ -8,6 +8,13 @@
     method: 'get',
     params: query
   })
+}// 鏌ヨ鐢ㄦ埛鍒楄〃
+export function listAll(query) {
+  return request({
+    url: '/system/user//listAll',
+    method: 'get',
+    params: query
+  })
 }
 
 // 鏌ヨ鐢ㄦ埛璇︾粏
diff --git a/src/views/index.vue b/src/views/index.vue
index 2888b16..e616dc9 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -13,9 +13,13 @@
 							<div class="company-meta">{{userStore.roleName}}</div>
 						</div>
 						<div style="display: flex;align-items: center;gap: 8px">
-							<el-icon color="#5053B5" size="22"><Clock /></el-icon>
-							<span>鐧婚檰鏃ユ湡锛歿{userStore.currentLoginTime}}</span>
-						</div>
+						<el-icon color="#5053B5" size="22"><Clock /></el-icon>
+						<span>鐧婚檰鏃ユ湡锛歿{userStore.currentLoginTime}}</span>
+					</div>
+					<div style="display: flex;align-items: center;gap: 8px">
+						<el-icon color="#5053B5" size="22"><Calendar /></el-icon>
+						<span>鎺掔彮鏃堕棿锛歿{scheduleTime}}</span>
+					</div>
 					</div>
 				</div>
 				<div class="data-cards">
@@ -169,6 +173,7 @@
 import Echarts from "@/components/Echarts/echarts.vue";
 import * as echarts from 'echarts';
 import useUserStore from "@/store/modules/user.js";
+import { Clock, Calendar } from '@element-plus/icons-vue'
 import {
 	analysisCustomerContractAmounts, getAmountHalfYear,
 	getBusiness,
@@ -176,6 +181,8 @@
 	qualityStatistics,
 	statisticsReceivablePayable
 } from "@/api/viewIndex.js";
+import { listPage } from "@/api/personnelManagement/scheduling.js";
+import dayjs from "dayjs";
 
 const userStore = useUserStore()
 
@@ -341,6 +348,10 @@
 const todoList = ref([])
 const radio1 = ref(1)
 
+// 鎺掔彮鏃堕棿
+const scheduleTime = ref('')
+const scheduleInfo = ref({})
+
 // 鍥捐〃寮曠敤
 const barChart = ref(null)
 const lineChart = ref(null)
@@ -358,6 +369,7 @@
 	statisticsReceivable()
 	qualityStatisticsInfo()
 	getAmountHalfYearNum()
+	getCurrentUserSchedule()
 })
 // 鏁版嵁缁熻
 const getBusinessData = () => {
@@ -409,6 +421,42 @@
 		qualityStatisticsObject.value.factoryNum = res.data.factoryNum
 	})
 }
+// 鑾峰彇褰撳墠鐢ㄦ埛鎺掔彮淇℃伅
+const getCurrentUserSchedule = async () => {
+  try {
+    const today = dayjs().format('YYYY-MM-DD')
+    const res = await listPage({
+      staffName: userStore.name,
+      startDate: today,
+      endDate: today,
+      current: 1,
+      size: 10
+    })
+    
+    if (res.data && res.data.records && res.data.records.length > 0) {
+      const currentSchedule = res.data.records[0]
+      scheduleInfo.value = currentSchedule
+      
+      // 鏍煎紡鍖栨帓鐝椂闂存樉绀�
+      if (currentSchedule.startTime && currentSchedule.endTime) {
+        scheduleTime.value = `${currentSchedule.startTime} - ${currentSchedule.endTime}`
+      } else if (currentSchedule.workStartTime && currentSchedule.workEndTime) {
+        const startTime = dayjs(currentSchedule.workStartTime).format('HH:mm')
+        const endTime = dayjs(currentSchedule.workEndTime).format('HH:mm')
+        scheduleTime.value = `${startTime} - ${endTime}`
+      } else {
+        scheduleTime.value = '浠婃棩鏃犳帓鐝�'
+      }
+    } else {
+      scheduleTime.value = '浠婃棩鏃犳帓鐝�'
+      scheduleInfo.value = {}
+    }
+  } catch (error) {
+    console.error('鑾峰彇鎺掔彮淇℃伅澶辫触:', error)
+    scheduleTime.value = '鑾峰彇鎺掔彮淇℃伅澶辫触'
+  }
+}
+
 const getAmountHalfYearNum = async () => {
 	const res = await getAmountHalfYear()
 	console.log(res)
diff --git a/src/views/personnelManagement/payrollManagement/components/formDia.vue b/src/views/personnelManagement/payrollManagement/components/formDia.vue
index e4cf0b3..8da9fb3 100644
--- a/src/views/personnelManagement/payrollManagement/components/formDia.vue
+++ b/src/views/personnelManagement/payrollManagement/components/formDia.vue
@@ -27,128 +27,15 @@
 							<el-select v-model="form.staffId" placeholder="璇烽�夋嫨浜哄憳" style="width: 100%" @change="handleSelect" :disabled="operationType === 'edit'">
 								<el-option
 									v-for="item in personList"
-									:key="item.id"
-									:label="item.staffName"
-									:value="item.id"
+									:key="item.userId"
+									:label="item.nickName"
+									:value="item.userId"
 								/>
 							</el-select>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="搴斿嚭鍕ゅぉ鏁帮細" prop="shouldAttendedNum">
-							<el-input v-model="form.shouldAttendedNum" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="瀹為檯鍑哄嫟澶╂暟锛�" prop="actualAttendedNum">
-              <el-input v-model="form.actualAttendedNum" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鍩烘湰宸ヨ祫锛�" prop="basicSalary">
-              <el-input v-model="form.basicSalary" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="宀椾綅宸ヨ祫锛�" prop="postSalary">
-              <el-input v-model="form.postSalary" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鍏ョ鑱岀己鍕ゆ墸娆撅細" prop="deductionAbsenteeism">
-              <el-input v-model="form.deductionAbsenteeism" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鐥呭亣鎵f锛�" prop="sickLeaveDeductions">
-              <el-input v-model="form.sickLeaveDeductions" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="浜嬪亣鎵f锛�" prop="deductionPersonalLeave">
-              <el-input v-model="form.deductionPersonalLeave" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="蹇樿鎵撳崱鎵f锛�" prop="forgetClockDeduct">
-              <el-input v-model="form.forgetClockDeduct" style="width: 100%" type="number"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="缁╂晥寰楀垎锛�" prop="performanceScore">
-              <el-input v-model="form.performanceScore" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="缁╂晥宸ヨ祫锛�" prop="performancePay">
-              <el-input v-model="form.performancePay" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="搴斿彂鍚堣锛�" prop="payableWages">
-              <el-input v-model="form.payableWages" placeholder="璇疯緭鍏�" clearable type="number"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="绀句繚涓汉锛�" prop="socialSecurityIndividuals">
-              <el-input v-model="form.socialSecurityIndividuals" :precision="0" :step="1" style="width: 100%" type="number"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="绀句繚鍏徃锛�" prop="socialSecurityCompanies">
-							<el-input v-model="form.socialSecurityCompanies" :precision="0" :step="1" style="width: 100%" type="number"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="绀句繚鍚堣锛�" prop="socialSecurityTotal">
-							<el-input v-model="form.socialSecurityTotal" :precision="0" :step="1" style="width: 100%" type="number"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鍏Н閲戜釜浜猴細" prop="providentFundIndividuals">
-							<el-input v-model="form.providentFundIndividuals" :precision="0" :step="1" style="width: 100%" type="number"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鍏Н閲戝叕鍙革細" prop="providentFundCompany">
-							<el-input v-model="form.providentFundCompany" :precision="0" :step="1" style="width: 100%" type="number"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鍏Н閲戝悎璁★細" prop="providentFundTotal">
-							<el-input v-model="form.providentFundTotal" :precision="0" :step="1" style="width: 100%" type="number"/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="搴旂◣宸ヨ祫锛�" prop="taxableWaget">
-							<el-input v-model="form.taxableWaget" :precision="0" :step="1" style="width: 100%" type="number"/>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="涓汉鎵�寰楃◣锛�" prop="personalIncomeTax">
-							<el-input v-model="form.personalIncomeTax" :step="0.1" style="width: 100%" type="number"/>
-            </el-form-item>
-          </el-col>
           <el-col :span="12">
             <el-form-item label="瀹炲彂宸ヨ祫锛�" prop="actualWages">
 							<el-input v-model="form.actualWages" style="width: 100%" type="number"/>
@@ -170,6 +57,7 @@
 import {ref} from "vue";
 import {getStaffJoinInfo, getStaffOnJob, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
 import {compensationAdd, compensationUpdate} from "@/api/personnelManagement/payrollManagement.js";
+import {listUser} from "@/api/system/user.js";
 const { proxy } = getCurrentInstance()
 const emit = defineEmits(['close'])
 
@@ -180,50 +68,11 @@
 		payDate: "",
     staffId: "",
 		name: "",
-		shouldAttendedNum: "",
-		actualAttendedNum: "",
-		basicSalary: "",
-		postSalary: "",
-		deductionAbsenteeism: "",
-		sickLeaveDeductions: "",
-		deductionPersonalLeave: "",
-		forgetClockDeduct: "",
-		performanceScore: "",
-		performancePay: "",
-		payableWages: "",
-		socialSecurityIndividuals: "",
-		socialSecurityCompanies: "",
-		socialSecurityTotal: "",
-		providentFundIndividuals: "",
-		providentFundCompany: "",
-		providentFundTotal: "",
-		taxableWaget: "",
-		personalIncomeTax: "",
 		actualWages: "",
   },
   rules: {
 		payDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" },],
 		staffId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" },],
-    staffName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		shouldAttendedNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		actualAttendedNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		basicSalary: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		postSalary: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		deductionAbsenteeism: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		sickLeaveDeductions: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		deductionPersonalLeave: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		forgetClockDeduct: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		performanceScore: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		performancePay: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		payableWages: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		socialSecurityIndividuals: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		socialSecurityCompanies: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		socialSecurityTotal: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		providentFundIndividuals: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		providentFundCompany: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		providentFundTotal: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		taxableWaget: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		personalIncomeTax: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
 		actualWages: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
   },
 });
@@ -234,8 +83,8 @@
 const openDialog = (type, row) => {
   operationType.value = type;
   dialogFormVisible.value = true;
-	getStaffOnJob().then(res => {
-		personList.value = res.data
+	listUser().then(res => {
+		personList.value = res.rows
 	})
 	form.value = {}
   if (operationType.value === 'edit') {
@@ -246,10 +95,9 @@
   }
 }
 const handleSelect = (value) => {
-	console.log('value', value)
-	const index = personList.value.findIndex(row => row.id === value)
+	const index = personList.value.findIndex(row => row.userId === value)
 	if (index > -1) {
-		form.value.name = personList.value[index].staffName
+		form.value.name = personList.value[index].nickName
 	}
 }
 // 鎻愪氦浜у搧琛ㄥ崟
diff --git a/src/views/personnelManagement/payrollManagement/index.vue b/src/views/personnelManagement/payrollManagement/index.vue
index 24e3dd8..a116316 100644
--- a/src/views/personnelManagement/payrollManagement/index.vue
+++ b/src/views/personnelManagement/payrollManagement/index.vue
@@ -27,9 +27,10 @@
 				>
 			</div>
 			<div>
-				<el-button @click="handleExport" style="margin-right: 10px">瀵煎嚭</el-button>
+				<el-button @click="handleImport">瀵煎叆</el-button>
 				<el-button type="primary" @click="openForm('add')">鏂板钖祫</el-button>
 				<el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+				<el-button @click="handleExport">瀵煎嚭</el-button>
 			</div>
 		</div>
 		<div class="table_list">
@@ -46,17 +47,56 @@
 			></PIMTable>
 		</div>
 		<form-dia ref="formDia" @close="handleQuery"></form-dia>
+		
+		<!-- 瀵煎叆寮圭獥 -->
+		<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body @close="handleUploadClose">
+			<el-upload
+				ref="uploadRef"
+				:limit="1"
+				accept=".xlsx, .xls"
+				:headers="upload.headers"
+				:action="upload.url"
+				:disabled="upload.isUploading"
+				:on-progress="upload.onProgress"
+				:on-success="upload.onSuccess"
+				:on-error="upload.onError"
+				:on-change="upload.onChange"
+				:auto-upload="false"
+				drag
+			>
+				<el-icon class="el-icon--upload"><upload-filled /></el-icon>
+				<div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+				<template #tip>
+					<div class="el-upload__tip text-center">
+						<span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+						<el-link
+							type="primary"
+							:underline="false"
+							style="font-size: 12px; vertical-align: baseline"
+							@click="importTemplate"
+						>涓嬭浇妯℃澘</el-link>
+					</div>
+				</template>
+			</el-upload>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+					<el-button @click="handleUploadCancel">鍙� 娑�</el-button>
+				</div>
+			</template>
+		</el-dialog>
 	</div>
 </template>
 
 <script setup>
-import { Search } from "@element-plus/icons-vue";
+import { Search, UploadFilled } from "@element-plus/icons-vue";
 import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
 import FormDia from "@/views/personnelManagement/payrollManagement/components/formDia.vue";
 import {staffJoinDel} from "@/api/personnelManagement/onboarding.js";
 import {ElMessageBox} from "element-plus";
 import dayjs from "dayjs";
 import {compensationDelete, compensationListPage} from "@/api/personnelManagement/payrollManagement.js";
+import { getToken } from "@/utils/auth.js";
 
 const data = reactive({
 	searchForm: {
@@ -73,98 +113,6 @@
 	{
 		label: "濮撳悕",
 		prop: "name",
-	},
-	{
-		label: "搴斿嚭鍕ゅぉ鏁�",
-		prop: "shouldAttendedNum",
-		width:100
-	},
-	{
-		label: "瀹為檯鍑哄嫟澶╂暟",
-		prop: "actualAttendedNum",
-		width:110
-	},
-	{
-		label: "鍩烘湰宸ヨ祫",
-		prop: "basicSalary",
-	},
-	{
-		label: "宀椾綅宸ヨ祫",
-		prop: "postSalary",
-		width:100
-	},
-	{
-		label: "鍏ョ鑱岀己鍕ゆ墸娆�",
-		prop: "deductionAbsenteeism",
-		width:130
-	},
-	{
-		label: "鐥呭亣鎵f",
-		prop: "sickLeaveDeductions",
-		width:100
-	},
-	{
-		label: "浜嬪亣鎵f",
-		prop: "deductionPersonalLeave",
-		width:100
-	},
-	{
-		label: "蹇樿鎵撳崱鎵f",
-		prop: "forgetClockDeduct",
-		width:110
-	},
-	{
-		label: "缁╂晥寰楀垎",
-		prop: "performanceScore",
-		width:150
-	},
-	{
-		label: "缁╂晥宸ヨ祫",
-		prop: "performancePay",
-		width: 120
-	},
-	{
-		label: "搴斿彂鍚堣",
-		prop: "payableWages",
-		width:150
-	},
-	{
-		label: "绀句繚涓汉",
-		prop: "socialSecurityIndividuals",
-	},
-	{
-		label: "绀句繚鍏徃",
-		prop: "socialSecurityCompanies",
-		width: 120
-	},
-	{
-		label: "绀句繚鍚堣",
-		prop: "socialSecurityTotal",
-		width: 120
-	},
-	{
-		label: "鍏Н閲戜釜浜�",
-		prop: "providentFundIndividuals",
-		width: 120
-	},
-	{
-		label: "鍏Н閲戝叕鍙�",
-		prop: "providentFundCompany",
-		width: 120
-	},
-	{
-		label: "鍏Н閲戝悎璁�",
-		prop: "providentFundTotal",
-		width: 120
-	},
-	{
-		label: "搴旂◣宸ヨ祫",
-		prop: "taxableWaget",
-	},
-	{
-		label: "涓汉鎵�寰楃◣",
-		prop: "personalIncomeTax",
-		width: 120
 	},
 	{
 		label: "瀹炲彂宸ヨ祫",
@@ -197,6 +145,56 @@
 });
 const formDia = ref()
 const { proxy } = getCurrentInstance()
+
+// 瀵煎叆鍔熻兘閰嶇疆
+const upload = reactive({
+  // 鏄惁鏄剧ず寮瑰嚭灞傦紙钖祫瀵煎叆锛�
+  open: false,
+  // 寮瑰嚭灞傛爣棰橈紙钖祫瀵煎叆锛�
+  title: "",
+  // 鏄惁绂佺敤涓婁紶
+  isUploading: false,
+  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+  headers: { Authorization: "Bearer " + getToken() },
+  // 涓婁紶鐨勫湴鍧�
+  url: import.meta.env.VITE_APP_BASE_API + "/compensationPerformance/importData",
+  // 鏂囦欢涓婁紶鍓嶇殑鍥炶皟
+  beforeUpload: (file) => {
+    // 鍙互鍦ㄦ澶勫仛鏂囦欢绫诲瀷鎴栧ぇ灏忔牎楠�
+    const isValid = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
+    if (!isValid) {
+      proxy.$modal.msgError("鍙兘涓婁紶 Excel 鏂囦欢");
+    }
+    return isValid;
+  },
+  // 鏂囦欢鐘舵�佹敼鍙樻椂鐨勫洖璋�
+  onChange: (file, fileList) => {
+    console.log('鏂囦欢鐘舵�佹敼鍙�', file, fileList);
+  },
+  // 鏂囦欢涓婁紶鎴愬姛鏃剁殑鍥炶皟
+  onSuccess: (response, file, fileList) => {
+    upload.isUploading = false;
+    if(response.code === 200){
+      proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
+      handleUploadClose();
+      getList();
+    }else if(response.code === 500){
+      proxy.$modal.msgError(response.msg);
+    }else{
+      proxy.$modal.msgWarning(response.msg);
+    }
+  },
+  // 鏂囦欢涓婁紶澶辫触鏃剁殑鍥炶皟
+  onError: (error, file, fileList) => {
+    console.error('涓婁紶澶辫触', error, file, fileList);
+    upload.isUploading = false;
+    proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
+  },
+  // 鏂囦欢涓婁紶杩涘害鍥炶皟
+  onProgress: (event, file, fileList) => {
+    console.log('涓婁紶涓�...', event.percent);
+  }
+});
 
 const handleDateChange = (value,type) => {
 	searchForm.value.entryDateEnd = null
@@ -299,6 +297,39 @@
 		});
 };
 
+/** 瀵煎叆鎸夐挳鎿嶄綔 */
+function handleImport() {
+  upload.title = "钖祫瀵煎叆";
+  upload.open = true;
+}
+
+/** 鎻愪氦涓婁紶鏂囦欢 */
+function submitFileForm() {
+  upload.isUploading = true;
+  proxy.$refs["uploadRef"].submit();
+}
+
+/** 涓嬭浇妯℃澘 */
+function importTemplate() {
+  proxy.download("/compensationPerformance/exportTemplate", {}, "钖祫瀵煎叆妯℃澘.xlsx");
+}
+
+// 澶勭悊涓婁紶寮规鍙栨秷
+function handleUploadCancel() {
+  upload.open = false;
+  handleUploadClose();
+}
+
+// 澶勭悊涓婁紶寮规鍏抽棴
+function handleUploadClose() {
+  upload.open = false;
+  upload.isUploading = false;
+  // 娓呯┖涓婁紶鏂囦欢缂撳瓨
+  if (proxy.$refs.uploadRef) {
+    proxy.$refs.uploadRef.clearFiles();
+  }
+}
+
 onMounted(() => {
 	getList();
 });
diff --git a/src/views/personnelManagement/scheduling/index.vue b/src/views/personnelManagement/scheduling/index.vue
index 251dbe9..7c6324f 100644
--- a/src/views/personnelManagement/scheduling/index.vue
+++ b/src/views/personnelManagement/scheduling/index.vue
@@ -11,34 +11,14 @@
               style="width: 150px"
           />
         </el-form-item>
-        <el-form-item label="鐝绫诲瀷锛�">
-          <el-select v-model="filterForm.shiftType" placeholder="璇烽�夋嫨鐝" clearable style="width: 120px">
-            <el-option v-for="item in shift_type" :label="item.label" :value="item.value" :key="item.value"/>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="鏃ユ湡鑼冨洿锛�">
-          <el-date-picker
-              v-model="filterForm.dateRange"
-              type="daterange"
-              range-separator="鑷�"
-              start-placeholder="寮�濮嬫棩鏈�"
-              end-placeholder="缁撴潫鏃ユ湡"
-              format="YYYY-MM-DD"
-              value-format="YYYY-MM-DD"
-              style="width: 250px"
-          />
-        </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="handleFilter">
-            <el-icon><Search/></el-icon>
-            绛涢��
+            鎼滅储
           </el-button>
           <el-button @click="resetFilter">
-            <el-icon><Refresh/></el-icon>
             閲嶇疆
           </el-button>
           <el-button @click="handleExport">
-            <el-icon><Download/></el-icon>
             瀵煎嚭
           </el-button>
           <el-button type="primary" @click="openScheduleDialog('add')">
@@ -61,48 +41,53 @@
           @selection-change="handleSelectionChange"
       >
         <el-table-column type="selection" width="55"/>
-        <el-table-column prop="staffName" label="鍛樺伐濮撳悕" width="120"/>
-        <el-table-column prop="staffNo" label="鍛樺伐宸ュ彿" width="100"/>
-        <el-table-column prop="department" label="閮ㄩ棬" width="120">
+        <el-table-column prop="staffName" label="鍛樺伐濮撳悕"/>
+        <!-- <el-table-column prop="staffNo" label="鍛樺伐宸ュ彿" width="100"/> -->
+        <!-- <el-table-column prop="department" label="閮ㄩ棬" width="120">
           <template #default="scope">
               {{ (department_type.find(i => i.value === String(scope.row.department)) || {}).label }}
           </template>
-        </el-table-column>
-        <el-table-column prop="shiftType" label="鐝绫诲瀷" width="100">
+        </el-table-column> -->
+        <!-- <el-table-column prop="shiftType" label="鐝绫诲瀷" width="100">
           <template #default="scope">
             <el-tag :type="getShiftTagType(scope.row.shiftType)">
               {{ (shift_type.find(i => i.value === String(scope.row.shiftType)) || {}).label }}
             </el-tag>
           </template>
+        </el-table-column> -->
+        <!-- <el-table-column prop="workDate" label="宸ヤ綔鏃ユ湡" width="120"/> -->
+        <el-table-column prop="workStartTime" label="寮�濮嬫椂闂�"/>
+        <el-table-column prop="workEndTime" label="缁撴潫鏃堕棿"/>
+        <el-table-column prop="lunchTime" label="鍗堜紤鏃堕棿(h)">
+          <template #default="scope">
+            {{ scope.row.lunchTime }}灏忔椂
+          </template>
         </el-table-column>
-        <el-table-column prop="workDate" label="宸ヤ綔鏃ユ湡" width="120"/>
-        <el-table-column prop="startTime" label="寮�濮嬫椂闂�" width="100"/>
-        <el-table-column prop="endTime" label="缁撴潫鏃堕棿" width="100"/>
-        <el-table-column prop="workHours" label="宸ヤ綔鏃堕暱" width="100">
+        <!-- <el-table-column prop="workHours" label="宸ヤ綔鏃堕暱" width="100">
           <template #default="scope">
             {{ scope.row.workHours }}灏忔椂
           </template>
-        </el-table-column>
-        <el-table-column prop="status" label="鐘舵��" width="100">
+        </el-table-column> -->
+        <!-- <el-table-column prop="status" label="鐘舵��" width="100">
           <template #default="scope">
             <el-tag :type="getStatusTagType(scope.row.status)">
               {{ (schedule_status.find(i => i.value === String(scope.row.status)) || {}).label }}
             </el-tag>
           </template>
-        </el-table-column>
-        <el-table-column prop="remark" label="澶囨敞" min-width="150"/>
-        <el-table-column label="鎿嶄綔" width="200" fixed="right">
+        </el-table-column> -->
+        <!-- <el-table-column prop="remark" label="澶囨敞" min-width="150"/> -->
+        <el-table-column label="鎿嶄綔" width="200" fixed="right" align="center">
           <template #default="scope">
             <el-button
-                type="primary"
-                size="small"
+							link
+							type="primary"
                 @click="openScheduleDialog('edit', scope.row)"
             >
               缂栬緫
             </el-button>
             <el-button
-                type="danger"
-                size="small"
+							link
+							type="danger"
                 @click="handleDelete(scope.row)"
             >
               鍒犻櫎
@@ -144,22 +129,26 @@
           label-width="120px"
       >
         <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="鍛樺伐濮撳悕锛�" prop="staffId">
-              <el-select v-model="scheduleForm.staffId" placeholder="璇疯緭鍏ュ憳宸ュ鍚�" style="width: 100%"
+          <el-col :span="24">
+            <el-form-item label="鍛樺伐濮撳悕锛�" prop="staffIds">
+          <el-select v-model="scheduleForm.staffIds" placeholder="璇烽�夋嫨鍛樺伐濮撳悕" style="width: 100%"
+                         multiple filterable collapse-tags-tooltip
                          @change="handleSelectStaff">
-                <el-option v-for="item in personList" :label="item.staffName" :value="item.id" :key="item.id"/>
-              </el-select>
-            </el-form-item>
+            <el-option v-for="item in personList" :label="item.nickName" :value="item.userId" :key="item.userId"/>
+          </el-select>
+        </el-form-item>
           </el-col>
+        </el-row>
+
+        <!-- <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="鍛樺伐宸ュ彿锛�" prop="staffNo">
               <el-input :disabled="true" v-model="scheduleForm.staffNo" placeholder=""/>
             </el-form-item>
           </el-col>
-        </el-row>
+        </el-row> -->
 
-        <el-row :gutter="20">
+        <!-- <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="閮ㄩ棬锛�" prop="department">
               <el-select v-model="scheduleForm.department" placeholder="璇烽�夋嫨閮ㄩ棬" style="width: 100%">
@@ -174,9 +163,9 @@
               </el-select>
             </el-form-item>
           </el-col>
-        </el-row>
+        </el-row> -->
 
-        <el-row :gutter="20">
+        <!-- <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="宸ヤ綔鏃ユ湡锛�" prop="workDate">
               <el-date-picker
@@ -196,34 +185,49 @@
               </el-select>
             </el-form-item>
           </el-col>
-        </el-row>
+        </el-row> -->
 
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="寮�濮嬫椂闂达細" prop="startTime">
+            <el-form-item label="寮�濮嬫椂闂达細" prop="workStartTime">
               <el-time-picker
-                  v-model="scheduleForm.startTime"
+                  v-model="scheduleForm.workStartTime"
                   placeholder="閫夋嫨寮�濮嬫椂闂�"
                   style="width: 100%"
                   format="HH:mm"
-                  value-format="HH:mm"
+                  value-format="YYYY-MM-DD HH:mm:ss"
               />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="缁撴潫鏃堕棿锛�" prop="endTime">
+            <el-form-item label="缁撴潫鏃堕棿锛�" prop="workEndTime">
               <el-time-picker
-                  v-model="scheduleForm.endTime"
+                  v-model="scheduleForm.workEndTime"
                   placeholder="閫夋嫨缁撴潫鏃堕棿"
                   style="width: 100%"
                   format="HH:mm"
-                  value-format="HH:mm"
+                  value-format="YYYY-MM-DD HH:mm:ss"
               />
             </el-form-item>
           </el-col>
         </el-row>
 
         <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍗堜紤鏃堕棿(h)锛�" prop="lunchTime">
+              <el-input-number
+                  v-model="scheduleForm.lunchTime"
+                  :min="0"
+                  :max="8"
+                  :step="0.5"
+                  placeholder="璇疯緭鍏ュ崍浼戞椂闂�"
+                  style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- <el-row :gutter="20">
           <el-col :span="24">
             <el-form-item label="澶囨敞锛�" prop="remark">
               <el-input
@@ -234,7 +238,7 @@
               />
             </el-form-item>
           </el-col>
-        </el-row>
+        </el-row> -->
       </el-form>
 
       <template #footer>
@@ -248,7 +252,7 @@
 </template>
 
 <script setup>
-import {ref, reactive, computed, onMounted, getCurrentInstance} from 'vue'
+import {ref, reactive, computed, onMounted, getCurrentInstance, watch} from 'vue'
 import {ElMessage, ElMessageBox} from 'element-plus'
 import {useDict} from "@/utils/dict.js"
 import {Plus, Download, Search, Refresh} from '@element-plus/icons-vue'
@@ -256,6 +260,7 @@
 import {getStaffOnJob} from "@/api/personnelManagement/onboarding.js";
 import dayjs from "dayjs";
 import pagination from "@/components/PIMTable/Pagination.vue";
+import {listUser} from "@/api/system/user.js";
 
 const { proxy } = getCurrentInstance();
 
@@ -269,8 +274,6 @@
 // 绛涢�夎〃鍗�
 const filterForm = reactive({
   staffName: '',
-  shiftType: '',
-  dateRange: [],
   current:1,
   size: 10
 })
@@ -278,29 +281,31 @@
 // 鎺掔彮琛ㄥ崟
 const scheduleForm = reactive({
   id: '',
-  staffId: '',
-  staffNo: '',
-  department: '',
-  shiftType: '',
-  workDate: '',
-  startTime: '',
-  endTime: '',
+  staffIds: [],
+  // staffNo: '',
+  // department: '',
+  // shiftType: '',
+  // workDate: '',
   workStartTime: '',
   workEndTime: '',
-  workHours: 0,
-  status: '',
-  remark: ''
+  lunchTime: 3,
+  // workStartTime: '',
+  // workEndTime: '',
+  // workHours: 0,
+  // status: '',
+  // remark: ''
 })
 
 // 琛ㄥ崟楠岃瘉瑙勫垯
 const scheduleRules = reactive({
-  staffId: [{required: true, message: '璇烽�夋嫨鍛樺伐', trigger: 'change'}],
-  department: [{required: true, message: '璇烽�夋嫨閮ㄩ棬', trigger: 'change'}],
-  shiftType: [{required: true, message: '璇烽�夋嫨鐝绫诲瀷', trigger: 'change'}],
-  workDate: [{required: true, message: '璇烽�夋嫨宸ヤ綔鏃ユ湡', trigger: 'change'}],
-  startTime: [{required: true, message: '璇烽�夋嫨寮�濮嬫椂闂�', trigger: 'change'}],
-  endTime: [{required: true, message: '璇烽�夋嫨缁撴潫鏃堕棿', trigger: 'change'}],
-  status: [{required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change'}]
+  staffIds: [{required: true, message: '璇烽�夋嫨鍛樺伐', trigger: 'change'}],
+  // department: [{required: true, message: '璇烽�夋嫨閮ㄩ棬', trigger: 'change'}],
+  // shiftType: [{required: true, message: '璇烽�夋嫨鐝绫诲瀷', trigger: 'change'}],
+  // workDate: [{required: true, message: '璇烽�夋嫨宸ヤ綔鏃ユ湡', trigger: 'change'}],
+  workStartTime: [{required: true, message: '璇烽�夋嫨寮�濮嬫椂闂�', trigger: 'change'}],
+  workEndTime: [{required: true, message: '璇烽�夋嫨缁撴潫鏃堕棿', trigger: 'change'}],
+  lunchTime: [{required: true, message: '璇疯緭鍏ュ崍浼戞椂闂�', trigger: 'blur'}],
+  // status: [{required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change'}]
 })
 const tableLoading = ref(false)
 
@@ -317,9 +322,9 @@
  * 鑾峰彇褰撳墠鍦ㄨ亴浜哄憳鍒楄〃
  */
 const getPersonList = () => {
-  getStaffOnJob().then(res => {
-    personList.value = res.data
-  })
+	listUser().then(res => {
+		personList.value = res.rows
+	})
 };
 const paginationChange = (obj) => {
   filterForm.current = obj.page;
@@ -328,9 +333,9 @@
 };
 
 const handleSelectStaff = (val) => {
-  let obj = personList.value.find(item => item.id === val)
-  scheduleForm.staffNo = obj.staffNo
-
+  // 澶氶�夊憳宸ワ紝涓嶅啀鑷姩璁剧疆鍛樺伐宸ュ彿
+  // let obj = personList.value.find(item => item.id === val)
+  // scheduleForm.staffNo = obj.staffNo
 }
 
 // 鑾峰彇鐝鏍囩绫诲瀷
@@ -349,19 +354,19 @@
 const handleFilter = async () => {
   tableLoading.value = true
   let searchForm = {
-    ...filterForm,
-    ...(filterForm.dateRange.length > 0 && {
-      startDate: filterForm.dateRange[0],
-      endDate: filterForm.dateRange[1],
-    })
+    ...filterForm
   }
   let resp = await listPage(searchForm)
   tableCount.value = resp.data.total
   scheduleList.value = resp.data.records.map(it => {
     return {
       ...it,
-      'startTime': dayjs(it.workStartTime).format('HH:mm'),
-      'endTime': dayjs(it.workEndTime).format('HH:mm'),
+      // 淇濆瓨鍘熷鏃堕棿鏍煎紡鐢ㄤ簬缂栬緫
+      'originalWorkStartTime': it.workStartTime,
+      'originalWorkEndTime': it.workEndTime,
+      // 鏍煎紡鍖栨椂闂寸敤浜庤〃鏍兼樉绀�
+      'workStartTime': dayjs(it.workStartTime).format('HH:mm'),
+      'workEndTime': dayjs(it.workEndTime).format('HH:mm'),
     }
   })
   tableLoading.value = false
@@ -371,8 +376,6 @@
 // 閲嶇疆绛涢��
 const resetFilter = () => {
   filterForm.staffName = ''
-  filterForm.shiftType = ''
-  filterForm.dateRange = []
 }
 
 // 鎵撳紑鎺掔彮瀵硅瘽妗�
@@ -381,16 +384,29 @@
   scheduleDialog.value = true
   getPersonList()
   if (type === 'edit' && data) {
-    // 缂栬緫妯″紡锛屽鍒舵暟鎹�
-    Object.assign(scheduleForm, {...data})
-  } else {
-    // 鏂板妯″紡锛岄噸缃〃鍗�
-    Object.keys(scheduleForm).forEach(key => {
-      scheduleForm[key] = ''
+    // 缂栬緫妯″紡锛屽鍒舵暟鎹紝灏嗗憳宸D瀛楃涓茶浆鎹负鏁扮粍鏍煎紡锛屽苟澶勭悊鏃堕棿瀛楁
+    Object.assign(scheduleForm, {
+      ...data,
+		lunchTime: Number(data.lunchTime),
+      staffIds: data.staffId ? data.staffId.split(',').map(id => parseInt(id)) : [],
+      // 浣跨敤鍘熷鏃堕棿瀛楃涓诧紝鍥犱负琛ㄦ牸涓樉绀虹殑鏄牸寮忓寲鍚庣殑HH:mm
+      workStartTime: data.originalWorkStartTime || '',
+      workEndTime: data.originalWorkEndTime || ''
     })
-    // scheduleForm.status = '宸插畨鎺�'
-    scheduleForm.workDate = new Date().toISOString().split('T')[0]
-  }
+  } else {
+      // 鏂板妯″紡锛岄噸缃〃鍗�
+      Object.keys(scheduleForm).forEach(key => {
+        if (key === 'staffIds') {
+          scheduleForm[key] = []
+        } else if (key === 'lunchTime') {
+          scheduleForm[key] = 3
+        } else {
+          scheduleForm[key] = ''
+        }
+      })
+      // scheduleForm.status = '宸插畨鎺�'
+      // scheduleForm.workDate = new Date().toISOString().split('T')[0]
+    }
 }
 
 // 鍏抽棴鎺掔彮瀵硅瘽妗�
@@ -401,37 +417,86 @@
 
 // 璁$畻宸ヤ綔鏃堕暱
 const calculateWorkHours = () => {
-  if (scheduleForm.workDate && scheduleForm.startTime && scheduleForm.endTime) {
-    // 浣跨敤 workDate 涓� startTime 鍜� endTime 缁勫悎
-    const startDateTime = new Date(`${scheduleForm.workDate} ${scheduleForm.startTime}`)
-    const endDateTime = new Date(`${scheduleForm.workDate} ${scheduleForm.endTime}`)
+  if (!scheduleForm.workStartTime || !scheduleForm.workEndTime) {
+    return;
+  }
+
+  try {
+    // 浣跨敤dayjs姝g‘瑙f瀽鏃堕棿
+    const startDayjs = dayjs(scheduleForm.workStartTime);
+    const endDayjs = dayjs(scheduleForm.workEndTime);
+
+    if (!startDayjs.isValid() || !endDayjs.isValid()) {
+      return;
+    }
+
+    const startDateTime = startDayjs.toDate();
+    const endDateTime = endDayjs.toDate();
 
     // 澶勭悊璺ㄥぉ鎯呭喌锛堢粨鏉熸椂闂存棭浜庡紑濮嬫椂闂达級
     if (endDateTime < startDateTime) {
       // 璺ㄥぉ锛屽皢缁撴潫鏃ユ湡鍔犱竴澶�
-      endDateTime.setDate(endDateTime.getDate() + 1)
+      endDateTime.setDate(endDateTime.getDate() + 1);
     }
-    // 璁$畻宸ヤ綔鏃堕暱锛堝皬鏃讹級
-    const diffMs = endDateTime - startDateTime
-    const diffHours = diffMs / (1000 * 60 * 60)
-    scheduleForm.workHours = Math.round(diffHours * 100) / 100
-    scheduleForm.workStartTime = dayjs(startDateTime).format("YYYY-MM-DD HH:mm:ss")
-    scheduleForm.workEndTime = dayjs(endDateTime).format("YYYY-MM-DD HH:mm:ss")
 
+    // 璁$畻宸ヤ綔鏃堕暱锛堝皬鏃讹級
+    const diffMs = endDateTime - startDateTime;
+    const diffHours = diffMs / (1000 * 60 * 60);
+    scheduleForm.workHours = Math.round(diffHours * 100) / 100;
+  } catch (error) {
+    console.error('鏃堕棿璁$畻閿欒:', error);
   }
 }
+
+// 鐩戝惉鏃堕棿瀛楁鍙樺寲锛岃嚜鍔ㄨ绠楀伐浣滄椂闀�
+watch(
+  () => [scheduleForm.workStartTime, scheduleForm.workEndTime],
+  () => {
+    calculateWorkHours()
+  },
+  { deep: true }
+)
 
 // 鎻愪氦鎺掔彮琛ㄥ崟
 const submitScheduleForm = async () => {
   const valid = await scheduleFormRef.value.validate()
   if (!valid) return
 
-  calculateWorkHours()
-  const newSchedule = {...scheduleForm}
+  // 鐢变簬鍛樺伐鏄閫夛紝闇�瑕佷负姣忎釜閫変腑鐨勫憳宸ュ垱寤烘帓鐝褰�
+  const selectedStaffIds = scheduleForm.staffIds || []
+  
+  if (selectedStaffIds.length === 0) {
+    ElMessage.warning('璇疯嚦灏戦�夋嫨涓�涓憳宸�')
+    return
+  }
 
   try {
+    // 鑾峰彇閫変腑鐨勫憳宸ュ鍚嶅垪琛�
+    const selectedStaffNames = selectedStaffIds.map(staffId => {
+      const staff = personList.value.find(item => item.userId === staffId)
+      return staff ? staff.nickName : ''
+    }).filter(name => name !== '')
+    
+    // 灏嗗憳宸ュ鍚嶇粍瑁呮垚閫楀彿鍒嗛殧鐨勫瓧绗︿覆
+    const staffNameString = selectedStaffNames.join(',')
+    
+    // 鍒涘缓鎺掔彮璁板綍锛屽皢鍛樺伐濮撳悕淇濆瓨涓哄瓧绗︿覆鏍煎紡
+    const newSchedule = {
+      ...scheduleForm,
+      staffName: staffNameString,
+      staffId: selectedStaffIds.join(','), // 灏嗗憳宸D涔熶繚瀛樹负閫楀彿鍒嗛殧鐨勫瓧绗︿覆
+      // 璁剧疆鍏朵粬蹇呰瀛楁鐨勯粯璁ゅ��
+      staffNo: '', // 鍙互鏍规嵁闇�瑕佷粠personList涓幏鍙�
+      department: '',
+      shiftType: '',
+      workDate: '',
+      status: '',
+      remark: ''
+    }
+    
     await save(newSchedule)
-    ElMessage.success('淇濆瓨鎺掔彮鎴愬姛')
+    
+    ElMessage.success(`鎴愬姛涓� ${selectedStaffNames.length} 涓憳宸ュ垱寤烘帓鐝璥)
 
     handleFilter()
     closeScheduleDialog()
@@ -491,11 +556,7 @@
 // 瀵煎嚭
 const handleExport = () => {
   let searchForm = {
-    ...filterForm,
-    ...(filterForm.dateRange.length > 0 && {
-      startDate: filterForm.dateRange[0],
-      endDate: filterForm.dateRange[1],
-    })
+    ...filterForm
   }
   proxy.download('/staff/staffScheduling/export', {}, '浜哄憳鎺掔彮.xlsx')
 }

--
Gitblit v1.9.3