367 lines
7.9 KiB
Vue
367 lines
7.9 KiB
Vue
<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> |