✨ feat: 增加智慧库房,完善项目盈亏
This commit is contained in:
367
pages/workbench/attendance/attendance.vue
Normal file
367
pages/workbench/attendance/attendance.vue
Normal file
@@ -0,0 +1,367 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user