<template>
|
<div class="attendance-detail">
|
<!-- 员工基本信息 -->
|
<el-card class="employee-info-card">
|
<div class="employee-header">
|
<div class="employee-basic">
|
<el-avatar
|
:size="60"
|
:src="employeeInfo.avatar"
|
class="employee-avatar"
|
>
|
{{ employeeInfo.name.charAt(0) }}
|
</el-avatar>
|
<div class="employee-details">
|
<h3>{{ employeeInfo.name }}</h3>
|
<p class="employee-department">
|
{{ employeeInfo.department }} · {{ employeeInfo.position }}
|
</p>
|
<p class="employee-contact">
|
<span>工号: {{ employeeInfo.employeeId }}</span>
|
<span>电话: {{ employeeInfo.phone }}</span>
|
</p>
|
</div>
|
</div>
|
<div class="employee-stats">
|
<div class="stat-item">
|
<div class="stat-value">{{ employeeStats.attendanceRate }}%</div>
|
<div class="stat-label">本月出勤率</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-value">{{ employeeStats.workHours }}h</div>
|
<div class="stat-label">总工作时长</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-value">{{ employeeStats.businessTripDays }}</div>
|
<div class="stat-label">出差天数</div>
|
</div>
|
</div>
|
</div>
|
</el-card>
|
|
<!-- 选项卡 -->
|
<el-card>
|
<el-tabs v-model="activeTab">
|
<el-tab-pane label="出勤记录" name="attendanceList">
|
<personal-attendance-table
|
:data="attendanceData"
|
:loading="loading"
|
/>
|
</el-tab-pane>
|
|
<el-tab-pane label="出差记录" name="businessTripList">
|
<personal-business-trip-table
|
:data="businessTripData"
|
:loading="loading"
|
/>
|
</el-tab-pane>
|
<el-tab-pane label="日历视图" name="calendar">
|
<attendance-calendar
|
:attendance-data="attendanceData"
|
:business-trip-data="businessTripData"
|
/>
|
</el-tab-pane>
|
<el-tab-pane label="统计报表" name="report">
|
<personal-attendance-report
|
:stats="employeeStats"
|
:attendance-data="attendanceData"
|
/>
|
</el-tab-pane>
|
</el-tabs>
|
</el-card>
|
</div>
|
</template>
|
|
<script>
|
import AttendanceCalendar from "./components/AttendanceCalendar.vue";
|
import PersonalAttendanceTable from "./components/PersonalAttendanceTable.vue";
|
import PersonBusiness from "./components/PersonBusiness.vue";
|
import PersonalAttendanceReport from "./components/PersonalAttendanceReport.vue";
|
|
export default {
|
name: "AttendanceDetail",
|
components: {
|
AttendanceCalendar,
|
PersonalAttendanceTable,
|
PersonBusiness,
|
PersonalAttendanceReport
|
},
|
data() {
|
return {
|
activeTab: "calendar",
|
loading: false,
|
employeeInfo: {
|
name: "",
|
department: "",
|
position: "",
|
employeeId: "",
|
phone: "",
|
avatar: ""
|
},
|
employeeStats: {
|
attendanceRate: 0,
|
workHours: 0,
|
businessTripDays: 0,
|
lateTimes: 0,
|
leaveEarlyTimes: 0
|
},
|
attendanceData: [],
|
businessTripData: []
|
};
|
},
|
created() {
|
this.getEmployeeInfo();
|
this.loadAttendanceData();
|
},
|
methods: {
|
getEmployeeInfo() {
|
const { employeeId, employeeName } = this.$route.query;
|
// 模拟员工信息
|
this.employeeInfo = {
|
name: employeeName || "张三",
|
department: "OPO项目部",
|
position: "项目经理",
|
employeeId: employeeId || "OPO001",
|
phone: "138****1234",
|
avatar: ""
|
};
|
},
|
|
async loadAttendanceData() {
|
this.loading = true;
|
try {
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
// 生成个人考勤模拟数据
|
this.attendanceData = this.generatePersonalAttendanceData();
|
this.businessTripData = this.generatePersonalBusinessTripData();
|
this.calculateStats();
|
} catch (error) {
|
console.error("加载数据失败:", error);
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
generatePersonalAttendanceData() {
|
const data = [];
|
const currentMonth = 12; // 12月
|
|
for (let day = 1; day <= 31; day++) {
|
if (Math.random() > 0.2) {
|
// 80%的出勤率
|
data.push({
|
id: day,
|
date: `2024-${currentMonth
|
.toString()
|
.padStart(2, "0")}-${day.toString().padStart(2, "0")}`,
|
checkIn: `08:${String(Math.floor(Math.random() * 30)).padStart(
|
2,
|
"0"
|
)}`,
|
checkOut: `18:${String(Math.floor(Math.random() * 30)).padStart(
|
2,
|
"0"
|
)}`,
|
status: Math.random() > 0.1 ? "正常" : "迟到",
|
workHours: (8 + Math.random() * 2).toFixed(1)
|
});
|
}
|
}
|
return data;
|
},
|
|
generatePersonalBusinessTripData() {
|
return [
|
{
|
id: 1,
|
tripNumber: "BT202412001",
|
startCity: "北京",
|
endCity: "上海",
|
startDate: "2024-12-05",
|
endDate: "2024-12-08",
|
distance: 1200,
|
purpose: "客户会议",
|
status: "已完成"
|
},
|
{
|
id: 2,
|
tripNumber: "BT202412002",
|
startCity: "北京",
|
endCity: "广州",
|
startDate: "2024-12-15",
|
endDate: "2024-12-18",
|
distance: 1900,
|
purpose: "项目调研",
|
status: "已完成"
|
}
|
];
|
},
|
|
calculateStats() {
|
const totalDays = 31;
|
const attendanceDays = this.attendanceData.length;
|
|
this.employeeStats = {
|
attendanceRate: Math.round((attendanceDays / totalDays) * 100),
|
workHours: this.attendanceData
|
.reduce((sum, item) => sum + parseFloat(item.workHours), 0)
|
.toFixed(1),
|
businessTripDays: this.businessTripData.reduce((sum, item) => {
|
const start = new Date(item.startDate);
|
const end = new Date(item.endDate);
|
return sum + Math.ceil((end - start) / (1000 * 60 * 60 * 24)) + 1;
|
}, 0),
|
lateTimes: this.attendanceData.filter(item => item.status === "迟到")
|
.length,
|
leaveEarlyTimes: this.attendanceData.filter(
|
item => item.status === "早退"
|
).length
|
};
|
}
|
}
|
};
|
</script>
|
|
<style scoped>
|
.attendance-detail {
|
padding: 20px;
|
}
|
|
.employee-info-card {
|
margin-bottom: 20px;
|
}
|
|
.employee-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
.employee-basic {
|
display: flex;
|
align-items: center;
|
}
|
|
.employee-avatar {
|
margin-right: 16px;
|
background-color: #409eff;
|
}
|
|
.employee-details h3 {
|
margin: 0 0 8px 0;
|
font-size: 24px;
|
color: #303133;
|
}
|
|
.employee-department {
|
margin: 0 0 8px 0;
|
color: #606266;
|
}
|
|
.employee-contact {
|
margin: 0;
|
color: #909399;
|
font-size: 14px;
|
}
|
|
.employee-contact span {
|
margin-right: 16px;
|
}
|
|
.employee-stats {
|
display: flex;
|
gap: 30px;
|
}
|
|
.stat-item {
|
text-align: center;
|
}
|
|
.stat-value {
|
font-size: 28px;
|
font-weight: bold;
|
color: #409eff;
|
margin-bottom: 4px;
|
}
|
|
.stat-label {
|
color: #909399;
|
font-size: 14px;
|
}
|
</style>
|