1.合并前段时间写的页面

This commit is contained in:
2025-07-24 16:00:29 +08:00
parent 33ef7f5cdb
commit 658d21399d
89 changed files with 7649 additions and 243 deletions

View File

@@ -0,0 +1,507 @@
<template>
<view class="my-record-container">
<!-- 顶部导航栏 -->
<view class="header">
<image class="back-btn" src="/static/ic_back.png" @click="goBack" />
<text class="page-title">我的考勤</text>
</view>
<!-- 月份标题和切换 -->
<view class="month-header">
<view class="month-nav">
<view class="month-arrow" @click="prevMonth">
<image class="arrow-icon" src="/static/ic_arrow_gray.webp" mode="aspectFit" style="transform: rotate(180deg)" />
</view>
<text class="month-title">{{ currentYear }}{{ currentMonth }}</text>
<view class="month-arrow" @click="nextMonth">
<image class="arrow-icon" src="/static/ic_arrow_gray.webp" mode="aspectFit" />
</view>
</view>
</view>
<!-- 考勤统计 -->
<view class="attendance-stats">
<view class="stat-item">
<text class="stat-value">17</text>
<text class="stat-label">出勤天数</text>
</view>
<view class="stat-item">
<text class="stat-value orange">2</text>
<text class="stat-label">迟到</text>
</view>
<view class="stat-item">
<text class="stat-value orange">1</text>
<text class="stat-label">早退</text>
</view>
<view class="stat-item">
<text class="stat-value">0</text>
<text class="stat-label">补卡</text>
</view>
</view>
<!-- 日历 -->
<view class="calendar">
<view class="weekdays">
<text v-for="day in weekdays" :key="day" class="weekday">{{ day }}</text>
</view>
<view class="dates" v-if="calendarExpanded">
<view
v-for="(date, index) in allDates"
:key="index"
:class="getDateClass(date)"
@click="selectDate(date)"
>
<text v-if="date.value" class="date-text">{{ date.value }}</text>
<view v-if="date.hasRecord" class="record-dot"></view>
</view>
</view>
<view class="dates" v-else>
<view
v-for="(date, index) in currentWeekDates"
:key="index"
:class="getDateClass(date)"
@click="selectDate(date)"
>
<text v-if="date.value" class="date-text">{{ date.value }}</text>
<view v-if="date.hasRecord" class="record-dot"></view>
</view>
</view>
<view class="calendar-toggle" @click="toggleCalendar">
<text class="toggle-text">{{ calendarExpanded ? '收起' : '展开' }}</text>
</view>
</view>
<!-- 固定班次 -->
<view class="fixed-shifts">
<text class="shifts-title">固定班次</text>
<view class="shift-item">
<view class="shift-time">
<view class="time-dot"></view>
<text class="time-text">应上班 09:00</text>
</view>
<view class="record leave-record">
<text class="record-text">已请假 09:0012:00</text>
</view>
</view>
<view class="shift-item">
<view class="shift-time">
<view class="time-dot"></view>
<text class="time-text">应下班 18:00</text>
</view>
<view class="record clock-record">
<text class="record-text">已打卡 18:02:18</text>
<text class="location">@某综合服务中心1栋</text>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* 我的考勤页面
* @author lyc
* @description 显示用户考勤记录,包含可展开收缩的日历
*/
export default {
data() {
return {
// 星期标题
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
// 日历是否展开
calendarExpanded: false,
// 当前选中的日期
selectedDate: 8,
// 当前年份
currentYear: 2025,
// 当前月份
currentMonth: 7,
// 完整月份日期数据
allDates: []
};
},
computed: {
// 当前周的日期(收缩状态显示)
currentWeekDates() {
// 找到选中日期所在的周
const selectedIndex = this.allDates.findIndex(date => date.selected);
if (selectedIndex === -1) return this.allDates.slice(0, 7);
const startOfWeek = Math.floor(selectedIndex / 7) * 7;
return this.allDates.slice(startOfWeek, startOfWeek + 7);
}
},
created() {
// 初始化日历数据
this.generateCalendarDates();
},
methods: {
/**
* 返回上一页
*/
goBack() {
uni.navigateBack();
},
/**
* 切换日历展开/收缩状态
*/
toggleCalendar() {
this.calendarExpanded = !this.calendarExpanded;
},
/**
* 选择日期
* @param {Object} date 日期对象
*/
selectDate(date) {
if (!date.value) return;
// 清除之前选中的日期
this.allDates.forEach(d => d.selected = false);
// 设置新选中的日期
date.selected = true;
this.selectedDate = date.value;
},
/**
* 获取日期样式类名
* @param {Object} date 日期对象
* @returns {Object} 样式类名对象
*/
getDateClass(date) {
return {
'date-item': true,
'selected': date.selected,
'has-record': date.hasRecord,
'empty': !date.value
};
},
/**
* 切换到上一个月
*/
prevMonth() {
if (this.currentMonth === 1) {
this.currentYear--;
this.currentMonth = 12;
} else {
this.currentMonth--;
}
this.generateCalendarDates();
},
/**
* 切换到下一个月
*/
nextMonth() {
if (this.currentMonth === 12) {
this.currentYear++;
this.currentMonth = 1;
} else {
this.currentMonth++;
}
this.generateCalendarDates();
},
/**
* 生成日历数据
*/
generateCalendarDates() {
const year = this.currentYear;
const month = this.currentMonth;
// 获取当月第一天是星期几
const firstDay = new Date(year, month - 1, 1).getDay();
// 获取当月总天数
const daysInMonth = new Date(year, month, 0).getDate();
// 清空日历数据
this.allDates = [];
// 添加上月的空白日期
for (let i = 0; i < firstDay; i++) {
this.allDates.push({ value: null });
}
// 添加当月日期
for (let i = 1; i <= daysInMonth; i++) {
// 模拟考勤记录数据这里假设前20天有考勤记录
const hasRecord = i <= 20;
// 默认选中当月8号
const selected = i === 8 && month === 7 && year === 2025;
this.allDates.push({
value: i,
hasRecord,
selected
});
}
// 计算需要添加的下月空白日期数量使总数为7的倍数
const remainingDays = 7 - (this.allDates.length % 7);
if (remainingDays < 7) {
for (let i = 0; i < remainingDays; i++) {
this.allDates.push({ value: null });
}
}
}
}
};
</script>
<style scoped>
.my-record-container {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 顶部导航栏 */
.header {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
background-color: #fff;
position: relative;
}
.back-btn {
width: 40rpx;
height: 40rpx;
margin-right: 20rpx;
}
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
/* 月份标题和导航 */
.month-header {
padding: 20rpx 30rpx;
background-color: #fff;
}
.month-nav {
display: flex;
align-items: center;
justify-content: space-between;
}
.month-arrow {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.arrow-icon {
width: 32rpx;
height: 32rpx;
}
.month-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
/* 考勤统计 */
.attendance-stats {
display: flex;
justify-content: space-around;
padding: 40rpx 30rpx;
margin: 20rpx 30rpx;
background-color: #fff;
border-radius: 16rpx;
border: 2rpx dashed #e0e0e0;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-value {
font-size: 48rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.stat-value.orange {
color: #ff6b35;
}
.stat-label {
font-size: 24rpx;
color: #666;
}
/* 日历 */
.calendar {
margin: 20rpx 30rpx;
background-color: #fff;
border-radius: 16rpx;
border: 2rpx dashed #e0e0e0;
overflow: hidden;
}
.weekdays {
display: flex;
background-color: #f8f8f8;
padding: 20rpx 0;
}
.weekday {
flex: 1;
text-align: center;
font-size: 28rpx;
color: #666;
}
.dates {
display: flex;
flex-wrap: wrap;
padding: 20rpx 0;
}
.date-item {
width: 14.28%;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20rpx;
}
.date-item.empty {
color: #ccc;
}
.date-item.selected {
background-color: #007aff;
border-radius: 50%;
width: 60rpx;
height: 60rpx;
margin: 10rpx auto;
}
.date-item.selected .date-text {
color: #fff;
}
.date-text {
font-size: 28rpx;
color: #333;
}
.record-dot {
position: absolute;
bottom: 8rpx;
left: 50%;
transform: translateX(-50%);
width: 8rpx;
height: 8rpx;
background-color: #007aff;
border-radius: 50%;
}
.date-item.selected .record-dot {
background-color: #fff;
}
.calendar-toggle {
padding: 20rpx;
text-align: center;
border-top: 1rpx solid #e0e0e0;
background-color: #f8f8f8;
}
.toggle-text {
font-size: 28rpx;
color: #666;
}
/* 固定班次 */
.fixed-shifts {
margin: 20rpx 30rpx;
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
}
.shifts-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
}
.shift-item {
margin-bottom: 40rpx;
}
.shift-item:last-child {
margin-bottom: 0;
}
.shift-time {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.time-dot {
width: 12rpx;
height: 12rpx;
background-color: #ff6b35;
border-radius: 50%;
margin-right: 16rpx;
}
.time-text {
font-size: 28rpx;
color: #333;
}
.record {
border: 2rpx dashed #e0e0e0;
border-radius: 12rpx;
padding: 24rpx;
margin-left: 28rpx;
}
.leave-record {
background-color: #fff7f0;
border-color: #ffb366;
}
.clock-record {
background-color: #f0f9ff;
border-color: #66b3ff;
}
.record-text {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
}
.location {
font-size: 24rpx;
color: #007aff;
display: block;
}
</style>

View File

@@ -0,0 +1,405 @@
<template>
<view class="my-record-container">
<!-- 顶部导航栏 -->
<view class="header">
<image class="back-btn" src="/static/ic_back.png" @click="goBack" />
<text class="page-title">我的考勤</text>
</view>
<!-- 月份标题 -->
<view class="month-header">
<text class="month-title">2025年7月</text>
</view>
<!-- 考勤统计 -->
<view class="attendance-stats">
<view class="stat-item">
<text class="stat-value">17</text>
<text class="stat-label">出勤天数</text>
</view>
<view class="stat-item">
<text class="stat-value orange">2</text>
<text class="stat-label">迟到</text>
</view>
<view class="stat-item">
<text class="stat-value orange">1</text>
<text class="stat-label">早退</text>
</view>
<view class="stat-item">
<text class="stat-value">0</text>
<text class="stat-label">补卡</text>
</view>
</view>
<!-- 日历 -->
<view class="calendar">
<view class="weekdays">
<text v-for="day in weekdays" :key="day" class="weekday">{{ day }}</text>
</view>
<view class="dates" v-if="calendarExpanded">
<view
v-for="(date, index) in allDates"
:key="index"
:class="getDateClass(date)"
@click="selectDate(date)"
>
<text v-if="date.value" class="date-text">{{ date.value }}</text>
<view v-if="date.hasRecord" class="record-dot"></view>
</view>
</view>
<view class="dates" v-else>
<view
v-for="(date, index) in currentWeekDates"
:key="index"
:class="getDateClass(date)"
@click="selectDate(date)"
>
<text v-if="date.value" class="date-text">{{ date.value }}</text>
<view v-if="date.hasRecord" class="record-dot"></view>
</view>
</view>
<view class="calendar-toggle" @click="toggleCalendar">
<text class="toggle-text">{{ calendarExpanded ? '收起' : '展开' }}</text>
</view>
</view>
<!-- 固定班次 -->
<view class="fixed-shifts">
<text class="shifts-title">固定班次</text>
<view class="shift-item">
<view class="shift-time">
<view class="time-dot"></view>
<text class="time-text">应上班 09:00</text>
</view>
<view class="record leave-record">
<text class="record-text">已请假 09:0012:00</text>
</view>
</view>
<view class="shift-item">
<view class="shift-time">
<view class="time-dot"></view>
<text class="time-text">应下班 18:00</text>
</view>
<view class="record clock-record">
<text class="record-text">已打卡 18:02:18</text>
<text class="location">@某综合服务中心1栋</text>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* 我的考勤页面
* @author lyc
* @description 显示用户考勤记录,包含可展开收缩的日历
*/
export default {
data() {
return {
// 星期标题
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
// 日历是否展开
calendarExpanded: false,
// 当前选中的日期
selectedDate: 8,
// 完整月份日期数据
allDates: [
{ value: null }, { value: null }, { value: 1, hasRecord: true }, { value: 2, hasRecord: true }, { value: 3, hasRecord: true }, { value: 4, hasRecord: true }, { value: 5, hasRecord: true },
{ value: 6, hasRecord: true }, { value: 7, hasRecord: true }, { value: 8, hasRecord: true, selected: true }, { value: 9, hasRecord: true }, { value: 10, hasRecord: true }, { value: 11, hasRecord: true }, { value: 12, hasRecord: true },
{ value: 13, hasRecord: true }, { value: 14, hasRecord: true }, { value: 15, hasRecord: true }, { value: 16, hasRecord: true }, { value: 17 }, { value: 18 }, { value: 19 },
{ value: 20 }, { value: 21 }, { value: 22 }, { value: 23 }, { value: 24 }, { value: 25 }, { value: 26 },
{ value: 27 }, { value: 28 }, { value: 29 }, { value: 30 }, { value: 31 }, { value: null }, { value: null }
]
};
},
computed: {
// 当前周的日期(收缩状态显示)
currentWeekDates() {
// 找到选中日期所在的周
const selectedIndex = this.allDates.findIndex(date => date.selected);
const startOfWeek = Math.floor(selectedIndex / 7) * 7;
return this.allDates.slice(startOfWeek, startOfWeek + 7);
}
},
methods: {
/**
* 返回上一页
*/
goBack() {
uni.navigateBack();
},
/**
* 切换日历展开/收缩状态
*/
toggleCalendar() {
this.calendarExpanded = !this.calendarExpanded;
},
/**
* 选择日期
* @param {Object} date 日期对象
*/
selectDate(date) {
if (!date.value) return;
// 清除之前选中的日期
this.allDates.forEach(d => d.selected = false);
// 设置新选中的日期
date.selected = true;
this.selectedDate = date.value;
},
/**
* 获取日期样式类名
* @param {Object} date 日期对象
* @returns {Object} 样式类名对象
*/
getDateClass(date) {
return {
'date-item': true,
'selected': date.selected,
'has-record': date.hasRecord,
'empty': !date.value
};
}
}
};
</script>
<style scoped>
.my-record-container {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 顶部导航栏 */
.header {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
background-color: #fff;
position: relative;
}
.back-btn {
width: 40rpx;
height: 40rpx;
margin-right: 20rpx;
}
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
/* 月份标题 */
.month-header {
padding: 30rpx;
background-color: #fff;
}
.month-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
/* 考勤统计 */
.attendance-stats {
display: flex;
justify-content: space-around;
padding: 40rpx 30rpx;
margin: 20rpx 30rpx;
background-color: #fff;
border-radius: 16rpx;
border: 2rpx dashed #e0e0e0;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-value {
font-size: 48rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.stat-value.orange {
color: #ff6b35;
}
.stat-label {
font-size: 24rpx;
color: #666;
}
/* 日历 */
.calendar {
margin: 20rpx 30rpx;
background-color: #fff;
border-radius: 16rpx;
border: 2rpx dashed #e0e0e0;
overflow: hidden;
}
.weekdays {
display: flex;
background-color: #f8f8f8;
padding: 20rpx 0;
}
.weekday {
flex: 1;
text-align: center;
font-size: 28rpx;
color: #666;
}
.dates {
display: flex;
flex-wrap: wrap;
padding: 20rpx 0;
}
.date-item {
width: 14.28%;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 20rpx;
}
.date-item.empty {
color: #ccc;
}
.date-item.selected {
background-color: #007aff;
border-radius: 50%;
width: 60rpx;
height: 60rpx;
margin: 10rpx auto;
}
.date-item.selected .date-text {
color: #fff;
}
.date-text {
font-size: 28rpx;
color: #333;
}
.record-dot {
position: absolute;
bottom: 8rpx;
left: 50%;
transform: translateX(-50%);
width: 8rpx;
height: 8rpx;
background-color: #007aff;
border-radius: 50%;
}
.date-item.selected .record-dot {
background-color: #fff;
}
.calendar-toggle {
padding: 20rpx;
text-align: center;
border-top: 1rpx solid #e0e0e0;
background-color: #f8f8f8;
}
.toggle-text {
font-size: 28rpx;
color: #666;
}
/* 固定班次 */
.fixed-shifts {
margin: 20rpx 30rpx;
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
}
.shifts-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
}
.shift-item {
margin-bottom: 40rpx;
}
.shift-item:last-child {
margin-bottom: 0;
}
.shift-time {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.time-dot {
width: 12rpx;
height: 12rpx;
background-color: #ff6b35;
border-radius: 50%;
margin-right: 16rpx;
}
.time-text {
font-size: 28rpx;
color: #333;
}
.record {
border: 2rpx dashed #e0e0e0;
border-radius: 12rpx;
padding: 24rpx;
margin-left: 28rpx;
}
.leave-record {
background-color: #fff7f0;
border-color: #ffb366;
}
.clock-record {
background-color: #f0f9ff;
border-color: #66b3ff;
}
.record-text {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
}
.location {
font-size: 24rpx;
color: #007aff;
display: block;
}
</style>