<template>
|
<div class="attendance-management">
|
<!-- 顶部统计信息 -->
|
<el-card class="statistics-card">
|
<div class="statistics-header">
|
<h3>考勤统计概览</h3>
|
<span class="statistics-date">{{ currentDate }}</span>
|
</div>
|
|
<el-row :gutter="20" class="statistics-content">
|
<el-col :span="6">
|
<div class="stat-item">
|
<div class="stat-value">{{ statistics.totalEmployees }}</div>
|
<div class="stat-label">总员工数</div>
|
</div>
|
</el-col>
|
<el-col :span="6">
|
<div class="stat-item">
|
<div class="stat-value text-success">{{ statistics.onDuty }}</div>
|
<div class="stat-label">今日出勤</div>
|
</div>
|
</el-col>
|
<el-col :span="6">
|
<div class="stat-item">
|
<div class="stat-value text-warning">{{ statistics.onBusinessTrip }}</div>
|
<div class="stat-label">出差中</div>
|
</div>
|
</el-col>
|
<el-col :span="6">
|
<div class="stat-item">
|
<div class="stat-value text-danger">{{ statistics.absent }}</div>
|
<div class="stat-label">缺勤</div>
|
</div>
|
</el-col>
|
</el-row>
|
</el-card>
|
|
<!-- 搜索和筛选区域 -->
|
<el-card class="filter-card">
|
<el-form :model="queryParams" inline>
|
<el-form-item label="年月">
|
<el-date-picker
|
v-model="queryParams.month"
|
type="month"
|
placeholder="选择年月"
|
value-format="yyyy-MM"
|
/>
|
</el-form-item>
|
<el-form-item label="员工姓名">
|
<el-input
|
v-model="queryParams.employeeName"
|
placeholder="请输入员工姓名"
|
clearable
|
/>
|
</el-form-item>
|
<el-form-item label="考勤类型">
|
<el-select v-model="queryParams.attendanceType" clearable>
|
<el-option label="全部" value="" />
|
<el-option label="出勤" value="attendance" />
|
<el-option label="出差" value="business_trip" />
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="handleQuery">查询</el-button>
|
<el-button @click="handleReset">重置</el-button>
|
</el-form-item>
|
</el-form>
|
</el-card>
|
|
<!-- 选项卡 -->
|
<el-card>
|
<el-tabs v-model="activeTab" @tab-click="handleTabChange">
|
<el-tab-pane label="出勤记录" name="attendance">
|
<attendance-table
|
:data="attendanceData"
|
:loading="loading"
|
@view-detail="handleViewDetail"
|
/>
|
</el-tab-pane>
|
|
<el-tab-pane label="出差记录" name="businessTrip">
|
<business-trip-table
|
:data="businessTripData"
|
:loading="loading"
|
@view-detail="handleViewDetail"
|
/>
|
</el-tab-pane>
|
|
<el-tab-pane label="统计出勤" name="statistics">
|
<attendance-statistics
|
:data="statisticsData"
|
:loading="loading"
|
/>
|
</el-tab-pane>
|
|
<el-tab-pane label="出差里程核算" name="mileage">
|
<mileage-calculation
|
:data="mileageData"
|
:loading="loading"
|
/>
|
</el-tab-pane>
|
</el-tabs>
|
</el-card>
|
</div>
|
</template>
|
|
<script>
|
import AttendanceTable from './components/AttendanceTable.vue'
|
import BusinessTripTable from './components/BusinessTripTable.vue'
|
import AttendanceStatistics from './components/AttendanceStatistics.vue'
|
import MileageCalculation from './components/MileageCalculation.vue'
|
|
export default {
|
name: 'AttendanceList',
|
components: {
|
AttendanceTable,
|
BusinessTripTable,
|
AttendanceStatistics,
|
MileageCalculation
|
},
|
data() {
|
return {
|
currentDate: new Date().toLocaleDateString('zh-CN'),
|
statistics: {
|
totalEmployees: 156,
|
onDuty: 142,
|
onBusinessTrip: 8,
|
absent: 6
|
},
|
queryParams: {
|
month: '',
|
employeeName: '',
|
attendanceType: ''
|
},
|
activeTab: 'attendance',
|
loading: false,
|
attendanceData: [],
|
businessTripData: [],
|
statisticsData: [],
|
mileageData: []
|
}
|
},
|
created() {
|
this.loadData()
|
},
|
methods: {
|
// 加载模拟数据
|
async loadData() {
|
this.loading = true
|
try {
|
// 模拟API调用延迟
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
// 生成模拟数据
|
this.attendanceData = this.generateAttendanceData()
|
this.businessTripData = this.generateBusinessTripData()
|
this.statisticsData = this.generateStatisticsData()
|
this.mileageData = this.generateMileageData()
|
} catch (error) {
|
console.error('加载数据失败:', error)
|
} finally {
|
this.loading = false
|
}
|
},
|
|
// 生成出勤模拟数据
|
generateAttendanceData() {
|
const employees = ['张三', '李四', '王五', '赵六', '钱七', '孙八']
|
const statuses = ['正常', '迟到', '早退', '缺勤']
|
const data = []
|
|
for (let i = 0; i < 20; i++) {
|
data.push({
|
id: i + 1,
|
employeeName: employees[Math.floor(Math.random() * employees.length)],
|
date: `2024-12-${String(Math.floor(Math.random() * 28) + 1).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: statuses[Math.floor(Math.random() * statuses.length)],
|
workHours: (8 + Math.random() * 2).toFixed(1)
|
})
|
}
|
return data
|
},
|
|
// 生成出差模拟数据
|
generateBusinessTripData() {
|
const cities = ['北京', '上海', '广州', '深圳', '杭州', '成都']
|
const data = []
|
|
for (let i = 0; i < 15; i++) {
|
const startDate = new Date(2024, 11, Math.floor(Math.random() * 28) + 1)
|
const endDate = new Date(startDate.getTime() + Math.random() * 5 * 24 * 60 * 60 * 1000)
|
|
data.push({
|
id: i + 1,
|
employeeName: `员工${String(i + 1).padStart(3, '0')}`,
|
tripNumber: `BT202412${String(i + 1).padStart(3, '0')}`,
|
startCity: cities[Math.floor(Math.random() * cities.length)],
|
endCity: cities[Math.floor(Math.random() * cities.length)],
|
startDate: startDate.toISOString().split('T')[0],
|
endDate: endDate.toISOString().split('T')[0],
|
distance: Math.floor(Math.random() * 1000) + 200,
|
status: ['进行中', '已完成'][Math.floor(Math.random() * 2)]
|
})
|
}
|
return data
|
},
|
|
// 生成统计模拟数据
|
generateStatisticsData() {
|
const months = ['2024-01', '2024-02', '2024-03', '2024-04', '2024-05', '2024-06']
|
return months.map(month => ({
|
month,
|
attendanceDays: Math.floor(Math.random() * 20) + 15,
|
lateTimes: Math.floor(Math.random() * 5),
|
leaveEarlyTimes: Math.floor(Math.random() * 3),
|
absenceDays: Math.floor(Math.random() * 3)
|
}))
|
},
|
|
// 生成里程模拟数据
|
generateMileageData() {
|
return this.businessTripData.map(item => ({
|
...item,
|
calculatedDistance: item.distance,
|
fuelCost: (item.distance * 0.8).toFixed(2),
|
allowance: (item.distance * 0.5).toFixed(2)
|
}))
|
},
|
|
handleQuery() {
|
this.loadData()
|
},
|
|
handleReset() {
|
this.queryParams = {
|
month: '',
|
employeeName: '',
|
attendanceType: ''
|
}
|
this.loadData()
|
},
|
|
handleTabChange(tab) {
|
this.activeTab = tab.name
|
},
|
|
handleViewDetail(employee) {
|
this.$router.push({
|
path: '/office/checkingInInfo',
|
query: {
|
employeeId: employee.id,
|
employeeName: employee.employeeName
|
}
|
})
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.attendance-management {
|
padding: 20px;
|
}
|
|
.statistics-card {
|
margin-bottom: 20px;
|
}
|
|
.statistics-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
}
|
|
.statistics-header h3 {
|
margin: 0;
|
color: #303133;
|
}
|
|
.statistics-date {
|
color: #909399;
|
font-size: 14px;
|
}
|
|
.statistics-content {
|
text-align: center;
|
}
|
|
.stat-item {
|
padding: 20px;
|
border-radius: 8px;
|
background: #f8f9fa;
|
}
|
|
.stat-value {
|
font-size: 32px;
|
font-weight: bold;
|
color: #409eff;
|
margin-bottom: 8px;
|
}
|
|
.stat-label {
|
color: #606266;
|
font-size: 14px;
|
}
|
|
.text-success { color: #67c23a; }
|
.text-warning { color: #e6a23c; }
|
.text-danger { color: #f56c6c; }
|
|
.filter-card {
|
margin-bottom: 20px;
|
}
|
</style>
|