Files
im-uniapp/pages/workbench/attendance/attendance.vue
2025-08-20 16:07:18 +08:00

367 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<!-- 标题和日期选择区 -->
<view class="header">
<view class="title-bar">
<text class="title">人员签到表</text>
</view>
<view class="date-picker-container">
<picker
mode="date"
fields="month"
:value="currentDate"
@change="onDateChange"
class="date-picker"
>
<view class="picker-view">
<text>{{ formattedDate }}</text>
</view>
</picker>
</view>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-view">
<loading class="loading" color="#007aff" size="16"></loading>
<text class="loading-text">加载中...</text>
</view>
<!-- 签到表区域支持横向滚动 -->
<scroll-view
v-else
scroll-x="true"
class="table-scroll"
@scroll="onScroll"
>
<view class="table-container">
<!-- 表头 -->
<view class="table-header">
<view class="table-cell name-cell">姓名</view>
<view
class="table-cell day-cell"
v-for="(day, index) in dateLength"
:key="index"
>
{{ index + 1 }}
</view>
</view>
<!-- 表格内容 -->
<view class="table-body">
<view
class="table-row"
v-for="(user, userIndex) in userList"
:key="userIndex"
:class="{ 'odd-row': userIndex % 2 === 1 }"
>
<view class="table-cell name-cell">{{ user.nickName }}</view>
<view
class="table-cell day-cell"
v-for="(day, dayIndex) in dateLength"
:key="dayIndex"
:style="getCellStyle(user, dayIndex + 1)"
>
{{ getAttendanceStatusText(user, dayIndex + 1) }}
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 图例说明 -->
<view class="legend-container">
<view class="legend-item">
<view class="legend-color" style="background-color: #fdf6e4;"></view>
<text class="legend-text">出差</text>
</view>
<view class="legend-item">
<view class="legend-color" style="background-color: #e3f2fd;"></view>
<text class="legend-text">请假</text>
</view>
</view>
</view>
</template>
<script>
import { getDateLength, listOaAttendance } from "@/api/oa/oaAttendance";
export default {
data() {
return {
// 用户列表数据
userList: [],
// 当前月份天数
dateLength: 31,
// 当前选中日期
currentDate: '',
// 格式化显示的日期
formattedDate: '',
// 加载状态
loading: true,
// 查询参数
queryParams: {
selectTime: ''
},
// 滚动位置记录
scrollLeft: 0
};
},
onLoad() {
// 初始化日期为当前月份
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
this.currentDate = `${year}-${month.toString().padStart(2, '0')}-01`;
this.formattedDate = `${year}${month}`;
this.queryParams.selectTime = this.currentDate;
// 获取数据
this.getDateCount();
this.getAttendanceList();
},
methods: {
// 获取当月天数
getDateCount() {
getDateLength().then(res => {
this.dateLength = res.data;
}).catch(err => {
console.error('获取日期长度失败', err);
});
},
// 获取签到列表数据
getAttendanceList() {
this.loading = true;
listOaAttendance(this.queryParams).then(res => {
this.userList = res.rows || [];
this.loading = false;
}).catch(err => {
console.error('获取签到数据失败', err);
this.loading = false;
});
},
// 日期选择变化
onDateChange(e) {
const value = e.detail.value;
this.currentDate = value;
// 格式化显示
const [year, month] = value.split('-');
this.formattedDate = `${year}${month}`;
// 更新查询参数并重新加载数据
this.queryParams.selectTime = value + '-01';
this.getAttendanceList();
},
// 处理滚动事件
onScroll(e) {
this.scrollLeft = e.detail.scrollLeft;
},
// 获取单元格样式
getCellStyle(user, dayIndex) {
// 查找对应日期的考勤记录
const attendance = user.attendances
? user.attendances.find(item => item.attendanceDay === dayIndex)
: null;
if (attendance) {
// 根据不同状态返回不同样式
if (attendance.projectId === 0) {
return { backgroundColor: '#fdf6e4' }; // 出差
} else if (attendance.projectId === 1) {
return { backgroundColor: '#e3f2fd' }; // 请假
} else if (attendance.color) {
return { backgroundColor: attendance.color }; // 其他项目颜色
}
}
// 默认样式
return { backgroundColor: '#ffffff' };
},
// 获取考勤状态文本
getAttendanceStatusText(user, dayIndex) {
if (!user.attendances || user.attendances.length === 0) {
return '';
}
const attendance = user.attendances.find(item => item.attendanceDay === dayIndex);
if (!attendance) {
return '';
}
// 根据项目ID返回对应文本
if (attendance.projectId === 0) {
return '出差';
} else if (attendance.projectId === 1) {
return '请假';
}
return '';
}
}
};
</script>
<style scoped>
.container {
flex: 1;
padding: 16rpx;
background-color: #f5f5f5;
min-height: 100vh;
box-sizing: border-box;
}
/* 头部样式 */
.header {
display: flex;
flex-direction: column;
gap: 16rpx;
margin-bottom: 20rpx;
}
.title-bar {
padding: 10rpx 0;
}
.title {
font-size: 34rpx;
font-weight: bold;
color: #333;
}
.date-picker-container {
background-color: #fff;
border-radius: 8rpx;
padding: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.date-picker {
width: 100%;
}
.picker-view {
text-align: center;
font-size: 28rpx;
color: #007aff;
padding: 8rpx 0;
}
/* 加载状态样式 */
.loading-view {
display: flex;
flex-direction: column;
align-items: center;
padding: 60rpx 0;
}
.loading {
margin-bottom: 16rpx;
}
.loading-text {
font-size: 26rpx;
color: #666;
}
/* 表格样式 */
.table-scroll {
width: 100%;
margin-bottom: 20rpx;
background-color: #fff;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.table-container {
min-width: 100%;
display: inline-block;
}
.table-header {
display: flex;
background-color: #f8f9fa;
border-bottom: 1px solid #eee;
}
.table-body {
display: flex;
flex-direction: column;
}
.table-row {
display: flex;
border-bottom: 1px solid #eee;
}
.table-row:last-child {
border-bottom: none;
}
.odd-row {
background-color: #fafafa;
}
.table-cell {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 0;
font-size: 24rpx;
border-right: 1px solid #eee;
box-sizing: border-box;
}
.table-cell:last-child {
border-right: none;
}
.name-cell {
min-width: 140rpx;
flex: 0 0 140rpx;
background-color: #f0f0f0;
font-weight: bold;
color: #333;
}
.day-cell {
min-width: 80rpx;
flex: 0 0 80rpx;
color: #666;
}
/* 图例样式 */
.legend-container {
display: flex;
padding: 16rpx;
background-color: #fff;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
margin-bottom: 20rpx;
}
.legend-item {
display: flex;
align-items: center;
margin-right: 40rpx;
}
.legend-color {
width: 24rpx;
height: 24rpx;
margin-right: 10rpx;
border-radius: 4rpx;
}
.legend-text {
font-size: 24rpx;
color: #666;
}
</style>