feat: 增加智慧库房,完善项目盈亏

This commit is contained in:
砂糖
2025-08-20 16:07:18 +08:00
parent f5ab69a431
commit 26ee6430c0
40 changed files with 14771 additions and 485 deletions

View 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>