This commit is contained in:
420
pages/sys/user/myRecord/cardReplacement.vue
Normal file
420
pages/sys/user/myRecord/cardReplacement.vue
Normal file
@@ -0,0 +1,420 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-section">
|
||||
<view class="form-item">
|
||||
<text class="form-label">补卡时间</text>
|
||||
<picker mode="date" :value="form.date" @change="onDateChange">
|
||||
<view class="picker">{{ form.date }} {{ form.time }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="tip-text">
|
||||
<text>本月已补卡{{ usedTimes }}次,剩余{{ remainTimes }}次。</text>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">补卡说明</text>
|
||||
<textarea v-model="form.remark" placeholder="请输入" class="textarea" />
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">附件</text>
|
||||
<!-- 如果没有图片,显示上传按钮 -->
|
||||
<view class="custom-upload-btn" @click="chooseImage" v-if="!selectedImage">
|
||||
<image class="image" src="/static/ic_camera.png"></image>
|
||||
<text class="text">上传图片</text>
|
||||
</view>
|
||||
|
||||
<!-- 已选图片预览 -->
|
||||
<view class="preview-item" v-else>
|
||||
<image class="preview-img" :src="selectedImage" mode="aspectFill" @click="previewImage"></image>
|
||||
<view class="delete-btn" @click.stop="deletePic">✕</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 审批记录 -->
|
||||
<view class="record-section">
|
||||
<text class="section-title">审批记录</text>
|
||||
<view class="timeline">
|
||||
<view v-for="(item, index) in records" :key="index" class="timeline-item">
|
||||
<!-- 点 -->
|
||||
<view class="timeline-dot"></view>
|
||||
|
||||
<!-- 节点内容 -->
|
||||
<view class="timeline-content">
|
||||
<text class="end-text">{{ item.node }}</text>
|
||||
|
||||
<view class="record-row" v-if="item.status !== '结束'">
|
||||
<image class="avatar" :src="item.avatar" mode="aspectFill" />
|
||||
<view class="name-node-container">
|
||||
<text class="name">{{ item.name }}</text>
|
||||
<text class="status" :class="statusClass(item.status)">{{ item.status }}</text>
|
||||
</view>
|
||||
<text class="time2">{{ item.time }}</text>
|
||||
</view>
|
||||
<text v-else class="time">{{ item.time }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view class="footer">
|
||||
<button class="submit-btn" type="primary" @click="submit">提交</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
usedTimes: 0,
|
||||
remainTimes: 5,
|
||||
selectedImage: '',
|
||||
form: {
|
||||
date: '2025-07-13',
|
||||
time: '09:00',
|
||||
remark: ''
|
||||
},
|
||||
records: [{
|
||||
node: '提交',
|
||||
name: '于永乐',
|
||||
status: '已提交',
|
||||
time: '07-12 18:28:22',
|
||||
avatar: '/static/avatar1.png'
|
||||
},
|
||||
{
|
||||
node: '审批',
|
||||
name: '张桂花',
|
||||
status: '已同意',
|
||||
time: '07-12 18:30:12',
|
||||
avatar: '/static/avatar2.png'
|
||||
},
|
||||
{
|
||||
node: '审批',
|
||||
name: '张桂花',
|
||||
status: '已拒绝',
|
||||
time: '07-12 19:12:45',
|
||||
avatar: '/static/avatar2.png'
|
||||
},
|
||||
{
|
||||
node: '结束',
|
||||
name: '',
|
||||
status: '结束',
|
||||
time: '07-12 20:00:00',
|
||||
avatar: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDateChange(e) {
|
||||
this.form.date = e.detail.value
|
||||
},
|
||||
chooseImage() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: (res) => {
|
||||
this.selectedImage = res.tempFilePaths[0]
|
||||
}
|
||||
})
|
||||
},
|
||||
// 预览图片
|
||||
previewImage() {
|
||||
uni.previewImage({
|
||||
current: this.selectedImage,
|
||||
urls: [this.selectedImage]
|
||||
})
|
||||
},
|
||||
// 删除图片
|
||||
deletePic(event) {
|
||||
this.selectedImage = ''
|
||||
},
|
||||
|
||||
submit() {
|
||||
uni.showToast({
|
||||
title: '提交成功',
|
||||
icon: 'success'
|
||||
})
|
||||
},
|
||||
statusClass(status) {
|
||||
if (status === '已提交') return 'blue'
|
||||
if (status === '已同意') return 'green'
|
||||
if (status === '已拒绝') return 'orange'
|
||||
if (status === '结束') return 'gray'
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: #fff;
|
||||
margin: 20rpx;
|
||||
padding: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 24rpx;
|
||||
color: #737373;
|
||||
margin-bottom: 10rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
height: 73rpx;
|
||||
line-height: 73rpx;
|
||||
background: #F7F7F7;
|
||||
color: #808080;
|
||||
font-size: 24rpx;
|
||||
border-radius: 10rpx;
|
||||
padding-left: 16rpx;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
border: 2rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
padding: 12rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.upload-box {
|
||||
border: 2rpx dashed #aaa;
|
||||
border-radius: 12rpx;
|
||||
padding: 40rpx;
|
||||
text-align: center;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
/* 审批记录 */
|
||||
.record-section {
|
||||
background: #fff;
|
||||
border-radius: 25rpx;
|
||||
margin-left: 32rpx;
|
||||
margin-right: 32rpx;
|
||||
padding: 35rpx 25rpx 35rpx 25rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* timeline 方法二:每个节点自己画线 */
|
||||
.timeline {
|
||||
position: relative;
|
||||
margin-left: 12rpx;
|
||||
margin-top: 45rpx;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
padding-left: 40rpx;
|
||||
padding-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.timeline-item::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: -2rpx;
|
||||
width: 2rpx;
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
/* 第一个节点:去掉上半段 */
|
||||
.timeline-item:first-child::before {
|
||||
top: 20rpx;
|
||||
}
|
||||
|
||||
/* 最后一个节点:去掉下半段 */
|
||||
.timeline-item:last-child::before {
|
||||
bottom: auto;
|
||||
height: 20rpx;
|
||||
}
|
||||
|
||||
/* 状态点 */
|
||||
.timeline-dot {
|
||||
position: absolute;
|
||||
left: -12rpx;
|
||||
top: 10rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border: 1rpx solid #10AF7F;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
margin-left: -20rpx;
|
||||
}
|
||||
|
||||
.record-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 54rpx;
|
||||
height: 54rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 15rpx;
|
||||
background: #688CFF;
|
||||
}
|
||||
|
||||
.name-node-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 28rpx;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status.blue {
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.status.green {
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
.status.orange {
|
||||
color: #ff6b35;
|
||||
}
|
||||
|
||||
.status.gray {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.end-text {
|
||||
font-size: 24rpx;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 24rpx;
|
||||
color: #626262;
|
||||
float: right;
|
||||
margin-right: 40rpx;
|
||||
}
|
||||
|
||||
.time2 {
|
||||
font-size: 24rpx;
|
||||
color: #626262;
|
||||
position: absolute;
|
||||
right: 40rpx;
|
||||
bottom: 70rpx;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
border-radius: 50rpx;
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: #000000;
|
||||
font-size: 24rpx;
|
||||
margin-left: 10rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 55rpx;
|
||||
height: 42rpx;
|
||||
}
|
||||
|
||||
.custom-upload-btn {
|
||||
width: auto;
|
||||
margin-left: 0rpx;
|
||||
margin-right: 0rpx;
|
||||
margin-top: 24rpx;
|
||||
height: 244rpx;
|
||||
background: #f6f6f6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.preview-item {
|
||||
position: relative;
|
||||
width: auto;
|
||||
/* 宽度自动 */
|
||||
margin-left: 40rpx;
|
||||
margin-right: 40rpx;
|
||||
height: 244rpx;
|
||||
margin-top: 24rpx;
|
||||
/* 固定高度 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
/* 水平居中 */
|
||||
align-items: center;
|
||||
/* 垂直居中 */
|
||||
overflow: hidden;
|
||||
/* 裁剪超出部分 */
|
||||
border-radius: 10rpx;
|
||||
/* 可选:圆角 */
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
line-height: 40rpx;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
</style>
|
@@ -1,18 +1,6 @@
|
||||
<template>
|
||||
<view class="my-record-container">
|
||||
|
||||
<!-- 月份标题和切换 -->
|
||||
<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>
|
||||
<text class="month-title">{{ selectedDate.substring(0,4) }}年{{ selectedDate.substring(5,7) }}月</text>
|
||||
|
||||
<!-- 考勤统计 -->
|
||||
<view class="attendance-stats">
|
||||
@@ -36,248 +24,83 @@
|
||||
|
||||
<!-- 日历 -->
|
||||
<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>
|
||||
|
||||
|
||||
<CommonCalendar ref="calendarComponent" :initial-date="selectedDate" :allowWeekSwitch='true' :initialMode='mode' @dateChange="handleDateSelected" @modeChange="handleModeChange" />
|
||||
<!-- 展开/收缩按钮 -->
|
||||
<view class="calendar-toggle" @click="toggleCalendar">
|
||||
<image v-if="calendarExpanded" class='image_zk' src="/static/ic_exp.png"></image>
|
||||
<view class="calendar-toggle" @click="toggleCalendarMode">
|
||||
<image v-if="mode == 'month'" class='image_zk' src="/static/ic_exp.png"></image>
|
||||
<image v-else class='image_sq' src="/static/ic_sq.png"></image>
|
||||
</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:00—12: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 class="fixed-shifts">
|
||||
<text class="shifts-title">固定班次</text>
|
||||
<PunchInfo/>
|
||||
<PunchInfo/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CommonCalendar from '@/components/CommonCalendar.vue'
|
||||
import PunchInfo from '@/components/punchInfo.vue'
|
||||
export default {
|
||||
components: {
|
||||
CommonCalendar,
|
||||
PunchInfo
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
|
||||
calendarExpanded: true,
|
||||
selectedDate: 8,
|
||||
currentYear: 2025,
|
||||
currentMonth: 7,
|
||||
allDates: [],
|
||||
prevMonthDates: [],
|
||||
nextMonthDates: [],
|
||||
swiperCurrent: 1,
|
||||
touchStartX: 0,
|
||||
touchStartY: 0
|
||||
selectedDate: this.getCurrentDate(), // 默认选中的日期
|
||||
mode: 'month'
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.generateCalendarDates();
|
||||
},
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
toggleCalendar() {
|
||||
this.calendarExpanded = !this.calendarExpanded;
|
||||
},
|
||||
selectDate(date) {
|
||||
if (!date.value) return;
|
||||
this.allDates.forEach(d => d.selected = false);
|
||||
date.selected = true;
|
||||
this.selectedDate = date.value;
|
||||
},
|
||||
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() {
|
||||
this.allDates = this.buildMonthDates(this.currentYear, this.currentMonth);
|
||||
|
||||
let prevY = this.currentYear, prevM = this.currentMonth - 1;
|
||||
if (prevM === 0) { prevM = 12; prevY--; }
|
||||
this.prevMonthDates = this.buildMonthDates(prevY, prevM);
|
||||
|
||||
let nextY = this.currentYear, nextM = this.currentMonth + 1;
|
||||
if (nextM === 13) { nextM = 1; nextY++; }
|
||||
this.nextMonthDates = this.buildMonthDates(nextY, nextM);
|
||||
},
|
||||
buildMonthDates(year, month) {
|
||||
const firstDay = new Date(year, month - 1, 1).getDay();
|
||||
const daysInMonth = new Date(year, month, 0).getDate();
|
||||
let dates = [];
|
||||
for (let i = 0; i < firstDay; i++) dates.push({ value: null });
|
||||
for (let i = 1; i <= daysInMonth; i++) {
|
||||
dates.push({ value: i, hasRecord: i <= 20, selected: i === this.selectedDate });
|
||||
}
|
||||
const remaining = 7 - (dates.length % 7);
|
||||
if (remaining < 7) for (let i = 0; i < remaining; i++) dates.push({ value: null });
|
||||
return dates;
|
||||
},
|
||||
isInCurrentWeek(date) {
|
||||
if (!date.value) return false;
|
||||
const index = this.allDates.findIndex(d => d.value === this.selectedDate);
|
||||
if (index === -1) return false;
|
||||
const start = Math.floor(index / 7) * 7;
|
||||
const week = this.allDates.slice(start, start + 7);
|
||||
return week.includes(date);
|
||||
},
|
||||
onTouchStart(e) {
|
||||
this.touchStartX = e.changedTouches[0].clientX;
|
||||
this.touchStartY = e.changedTouches[0].clientY;
|
||||
},
|
||||
onTouchEnd(e) {
|
||||
const endX = e.changedTouches[0].clientX;
|
||||
const endY = e.changedTouches[0].clientY;
|
||||
const deltaX = endX - this.touchStartX;
|
||||
const deltaY = endY - this.touchStartY;
|
||||
|
||||
// 上下滑切换月/周视图
|
||||
if (Math.abs(deltaY) > Math.abs(deltaX)) {
|
||||
if (deltaY < -50 && this.calendarExpanded) this.calendarExpanded = false;
|
||||
else if (deltaY > 50 && !this.calendarExpanded) this.calendarExpanded = true;
|
||||
}
|
||||
},
|
||||
onSwiperChange(e) {
|
||||
const current = e.detail.current;
|
||||
if (current === 0) { this.prevMonth(); this.swiperCurrent = 1; }
|
||||
else if (current === 2) { this.nextMonth(); this.swiperCurrent = 1; }
|
||||
}
|
||||
// 获取当前日期
|
||||
getCurrentDate() {
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(today.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
},
|
||||
|
||||
// 处理日期选择
|
||||
handleDateSelected(date) {
|
||||
this.selectedDate = date;
|
||||
},
|
||||
|
||||
// 处理日历模式变化
|
||||
handleModeChange(mode) {
|
||||
this.mode = mode;
|
||||
},
|
||||
|
||||
// 切换日历模式
|
||||
toggleCalendarMode() {
|
||||
// 调用日历组件的toggleMode方法
|
||||
if (this.$refs.calendarComponent) {
|
||||
this.$refs.calendarComponent.toggleMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.my-record-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.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;
|
||||
margin-left: 180rpx;
|
||||
margin-right: 180rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.month-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.month-arrow {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
.month-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-left: 35rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@@ -286,7 +109,7 @@ export default {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 40rpx 30rpx;
|
||||
margin: 20rpx 30rpx;
|
||||
margin: 28rpx 30rpx 15rpx 30rpx;
|
||||
background-color: #f7f7f7;
|
||||
border-radius: 10rpx;
|
||||
|
||||
@@ -315,90 +138,28 @@ export default {
|
||||
}
|
||||
|
||||
/* 日历样式 */
|
||||
.calendar { overflow: hidden; margin-left: 20rpx; margin-right: 20rpx}
|
||||
.weekdays { display: flex; 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: 70rpx; 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 { overflow: hidden; margin-left: 10rpx; margin-right: 10rpx}
|
||||
|
||||
.calendar-toggle { text-align: center; background-color: #fff; margin-top: -30rpx;}
|
||||
.calendar-toggle { text-align: center; background-color: #fff;}
|
||||
.image_sq { width: 37rpx; height: 6rpx; }
|
||||
.image_zk { width: 52rpx; height: 19rpx; }
|
||||
|
||||
/* 固定班次 */
|
||||
.fixed-shifts {
|
||||
margin: 20rpx 30rpx;
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.shifts-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
color: #737373;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.shift-item {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.shift-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.shift-time {
|
||||
margin-top: 7rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.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>
|
||||
|
@@ -34,7 +34,7 @@
|
||||
<image class="ins-line-image" src="/static/ic_my_repair_03.png" />
|
||||
<view class="ins-info">巡检人:{{ item.actUserName || '' }}</view>
|
||||
<view class="ins-info">计划完成时间:{{ item.planInsTime || '' }}</view>
|
||||
<view class="ins-info">实际完成时间:{{ item.location || '' }}</view>
|
||||
<view class="ins-info">实际完成时间:{{ item.compleTime || '' }}</view>
|
||||
<view class="ins-info">巡检进度:{{ item.inspectionProgress || '' }}</view>
|
||||
</view>
|
||||
|
||||
|
@@ -56,13 +56,13 @@
|
||||
</view>
|
||||
|
||||
<!-- 操作区:宽度跟随标题块,内部居中 -->
|
||||
<view class="ops" v-if="item.inspectionState ==0">
|
||||
<view class="ops" v-if="item.inspectionState == 0">
|
||||
<view class="btn-outline" @click="startTask(item)">立即巡检</view>
|
||||
</view>
|
||||
|
||||
<view class="status" v-else v-if="item.inspectionResults == 1" @click="goDetail(item)">
|
||||
<view class="status" v-else-if="item.inspectionResults == 1" @click="goDetail(item)">
|
||||
<view>完成巡检</view>
|
||||
<view class="badge" :class="item.inspectionResults ==1 ? 'badge-success' : 'badge-warn'">
|
||||
<view class="badge" :class="item.inspectionResults == 1 ? 'badge-success' : 'badge-warn'">
|
||||
{{ item.inspectionResults == 1 ? '正常' : '异常' }}
|
||||
</view>
|
||||
</view>
|
||||
|
@@ -20,23 +20,23 @@
|
||||
|
||||
<!-- 请假类型 -->
|
||||
<view class="form-row">
|
||||
<text class="label required">请假类型</text>
|
||||
<view class="picker" @click="openLeaveTypePopup">
|
||||
<text
|
||||
:class="leaveTypeIndex === 0 ? 'placeholder' : ''">{{ leaveTypes[leaveTypeIndex] || '请选择' }}</text>
|
||||
<image class="right-arrow" src="/static/ic_right_arrow_g.png" />
|
||||
<text class="label required">请假类型</text>
|
||||
<view class="picker" @click="openLeaveTypePopup">
|
||||
<text
|
||||
:class="leaveTypeIndex === 0 ? 'placeholder' : ''">{{ leaveTypes[leaveTypeIndex] || '请选择' }}</text>
|
||||
<image class="right-arrow" src="/static/ic_right_arrow_g.png" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 开始时间 -->
|
||||
<view class="form-row">
|
||||
<text class="label required">开始时间</text>
|
||||
<picker mode="date" @change="startDateChange">
|
||||
<view class="picker">
|
||||
<!-- <picker mode="date" @change="startDateChange"> -->
|
||||
<view class="picker" @click="startDateSelect">
|
||||
<text :class="!startDate ? 'placeholder' : ''">{{ startDate || '请选择' }}</text>
|
||||
<image class="right-arrow" src="/static/ic_right_arrow_g.png" />
|
||||
</view>
|
||||
</picker>
|
||||
<!-- </picker> -->
|
||||
</view>
|
||||
|
||||
<!-- 结束时间 -->
|
||||
@@ -63,8 +63,8 @@
|
||||
</view>
|
||||
|
||||
<!-- 上传图片 -->
|
||||
<u-upload class="upload-style" :fileList="selectedImages" @on-list-change="onListChange" @delete="deletePic" name="upload"
|
||||
multiple maxCount="3" width="180" height="180" :autoUpload="false"></u-upload>
|
||||
<u-upload class="upload-style" :fileList="selectedImages" @on-list-change="onListChange" @delete="deletePic"
|
||||
name="upload" multiple maxCount="3" width="180" height="180" :autoUpload="false"></u-upload>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<button class="submit-btn" @click="submitLeave">提交</button>
|
||||
@@ -113,13 +113,8 @@
|
||||
<view class="popup-content">
|
||||
<view class="popup-title">请假类型</view>
|
||||
<view class="popup-options">
|
||||
<view
|
||||
v-for="(type, index) in leaveTypes"
|
||||
:key="index"
|
||||
class="popup-option"
|
||||
:class="{ 'selected': leaveTypeIndex === index }"
|
||||
@click="selectLeaveType(index)"
|
||||
>
|
||||
<view v-for="(type, index) in leaveTypes" :key="index" class="popup-option"
|
||||
:class="{ selected: leaveTypeIndex === index }" @click="selectLeaveType(index)">
|
||||
{{ type }}
|
||||
</view>
|
||||
</view>
|
||||
@@ -127,127 +122,133 @@
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<SelectCalendarDialog :visible.sync="showCalendarDialog" @confirm="onConfirm" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tabs: ['发起申请', '请假记录', '已完成'],
|
||||
activeTab: 0,
|
||||
leaveTypes: ['事假', '调休', '病假', '年假', '产假', '陪产假', '婚假', '例假', '丧假', '哺乳假', '其他'],
|
||||
leaveTypeIndex: -1, // 初始化为-1表示未选择
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
hours: '',
|
||||
reason: '',
|
||||
selectedImages: [],
|
||||
leaveRecords: [],
|
||||
loading: false,
|
||||
hasMore: true,
|
||||
refreshing: false,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
showLeaveTypePopup: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeTab(idx) {
|
||||
this.activeTab = idx;
|
||||
},
|
||||
bindPickerChange(e) {
|
||||
this.leaveTypeIndex = e.detail.value;
|
||||
},
|
||||
startDateChange(e) {
|
||||
this.startDate = e.detail.value;
|
||||
},
|
||||
endDateChange(e) {
|
||||
this.endDate = e.detail.value;
|
||||
},
|
||||
submitLeave() {
|
||||
// 提交请假逻辑
|
||||
},
|
||||
deletePic(event) {
|
||||
this.selectedImages.splice(event.index, 1);
|
||||
},
|
||||
onListChange(list) {
|
||||
this.selectedImages = list;
|
||||
},
|
||||
goToDetail(item) {
|
||||
// 跳转到详情页逻辑
|
||||
},
|
||||
async onRefresh() {
|
||||
this.refreshing = true;
|
||||
this.pageNum = 1;
|
||||
await this.loadLeaveRecords();
|
||||
this.refreshing = false;
|
||||
},
|
||||
async onLoadMore() {
|
||||
if (!this.hasMore || this.loading) return;
|
||||
this.pageNum++;
|
||||
await this.loadLeaveRecords();
|
||||
},
|
||||
async loadLeaveRecords() {
|
||||
this.loading = true;
|
||||
let data = [
|
||||
{
|
||||
type: '请假',
|
||||
leaveType: '病假',
|
||||
status: '已通过',
|
||||
statusClass: 'green',
|
||||
startTime: '2025-07-13',
|
||||
endTime: '2025-07-15',
|
||||
userName: '余永乐',
|
||||
avatarText: '余',
|
||||
avatarColor: '#4B7BF5',
|
||||
dateTime: '07-12 18:28:22'
|
||||
},
|
||||
{
|
||||
type: '请假',
|
||||
leaveType: '病假',
|
||||
status: '待审核',
|
||||
statusClass: 'orange',
|
||||
startTime: '2025-07-13',
|
||||
endTime: '2025-07-15',
|
||||
userName: '余永乐',
|
||||
avatarText: '余',
|
||||
avatarColor: '#4B7BF5',
|
||||
dateTime: '07-12 18:28:22'
|
||||
}
|
||||
];
|
||||
const start = (this.pageNum - 1) * this.pageSize;
|
||||
const end = start + this.pageSize;
|
||||
const pageData = data.slice(start, end);
|
||||
await new Promise(res => setTimeout(res, 300));
|
||||
if (this.pageNum === 1) {
|
||||
this.leaveRecords = pageData;
|
||||
} else {
|
||||
this.leaveRecords = [...this.leaveRecords, ...pageData];
|
||||
import SelectCalendarDialog from '@/components/SelectCalendarDialog.vue'
|
||||
export default {
|
||||
components: { SelectCalendarDialog },
|
||||
data() {
|
||||
return {
|
||||
tabs: ['发起申请', '请假记录', '已完成'],
|
||||
activeTab: 0,
|
||||
leaveTypes: ['事假', '调休', '病假', '年假', '产假', '陪产假', '婚假', '例假', '丧假', '哺乳假', '其他'],
|
||||
leaveTypeIndex: -1, // 初始化为-1表示未选择
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
hours: '',
|
||||
reason: '',
|
||||
selectedImages: [],
|
||||
leaveRecords: [],
|
||||
loading: false,
|
||||
hasMore: true,
|
||||
refreshing: false,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
showLeaveTypePopup: false,
|
||||
showCalendarDialog: false,
|
||||
}
|
||||
this.hasMore = end < data.length;
|
||||
this.loading = false;
|
||||
},
|
||||
openLeaveTypePopup() {
|
||||
this.showLeaveTypePopup = true;
|
||||
},
|
||||
selectLeaveType(index) {
|
||||
this.leaveTypeIndex = index;
|
||||
},
|
||||
confirmLeaveType() {
|
||||
if (this.leaveTypeIndex !== -1) {
|
||||
this.showLeaveTypePopup = false;
|
||||
// 更新表单中的请假类型
|
||||
this.leaveType = this.leaveTypes[this.leaveTypeIndex];
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '请选择请假类型',
|
||||
icon: 'none'
|
||||
});
|
||||
methods: {
|
||||
changeTab(idx) {
|
||||
this.activeTab = idx;
|
||||
},
|
||||
bindPickerChange(e) {
|
||||
this.leaveTypeIndex = e.detail.value;
|
||||
},
|
||||
startDateSelect() {
|
||||
this.showCalendarDialog = true
|
||||
},
|
||||
onConfirm(selectedDate){
|
||||
this.startDate = selectedDate
|
||||
},
|
||||
endDateChange(e) {
|
||||
this.endDate = e.detail.value;
|
||||
},
|
||||
submitLeave() {
|
||||
// 提交请假逻辑
|
||||
},
|
||||
deletePic(event) {
|
||||
this.selectedImages.splice(event.index, 1);
|
||||
},
|
||||
onListChange(list) {
|
||||
this.selectedImages = list;
|
||||
},
|
||||
goToDetail(item) {
|
||||
// 跳转到详情页逻辑
|
||||
},
|
||||
async onRefresh() {
|
||||
this.refreshing = true;
|
||||
this.pageNum = 1;
|
||||
await this.loadLeaveRecords();
|
||||
this.refreshing = false;
|
||||
},
|
||||
async onLoadMore() {
|
||||
if (!this.hasMore || this.loading) return;
|
||||
this.pageNum++;
|
||||
await this.loadLeaveRecords();
|
||||
},
|
||||
async loadLeaveRecords() {
|
||||
this.loading = true;
|
||||
let data = [{
|
||||
type: '请假',
|
||||
leaveType: '病假',
|
||||
status: '已通过',
|
||||
statusClass: 'green',
|
||||
startTime: '2025-07-13',
|
||||
endTime: '2025-07-15',
|
||||
userName: '余永乐',
|
||||
avatarText: '余',
|
||||
avatarColor: '#4B7BF5',
|
||||
dateTime: '07-12 18:28:22'
|
||||
},
|
||||
{
|
||||
type: '请假',
|
||||
leaveType: '病假',
|
||||
status: '待审核',
|
||||
statusClass: 'orange',
|
||||
startTime: '2025-07-13',
|
||||
endTime: '2025-07-15',
|
||||
userName: '余永乐',
|
||||
avatarText: '余',
|
||||
avatarColor: '#4B7BF5',
|
||||
dateTime: '07-12 18:28:22'
|
||||
}
|
||||
];
|
||||
const start = (this.pageNum - 1) * this.pageSize;
|
||||
const end = start + this.pageSize;
|
||||
const pageData = data.slice(start, end);
|
||||
await new Promise(res => setTimeout(res, 300));
|
||||
if (this.pageNum === 1) {
|
||||
this.leaveRecords = pageData;
|
||||
} else {
|
||||
this.leaveRecords = [...this.leaveRecords, ...pageData];
|
||||
}
|
||||
this.hasMore = end < data.length;
|
||||
this.loading = false;
|
||||
},
|
||||
openLeaveTypePopup() {
|
||||
this.showLeaveTypePopup = true;
|
||||
},
|
||||
selectLeaveType(index) {
|
||||
this.leaveTypeIndex = index;
|
||||
},
|
||||
confirmLeaveType() {
|
||||
if (this.leaveTypeIndex !== -1) {
|
||||
this.showLeaveTypePopup = false;
|
||||
// 更新表单中的请假类型
|
||||
this.leaveType = this.leaveTypes[this.leaveTypeIndex];
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '请选择请假类型',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -513,7 +514,8 @@ export default {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
.upload-style{
|
||||
|
||||
.upload-style {
|
||||
margin-left: 15rpx;
|
||||
margin-right: 15rpx;
|
||||
}
|
||||
@@ -548,22 +550,21 @@ export default {
|
||||
}
|
||||
|
||||
.popup-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
/* 三列,可改成2/4列 */
|
||||
gap: 20rpx;
|
||||
/* 行列间距 */
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.popup-option {
|
||||
width: 30%;
|
||||
height: 80rpx;
|
||||
background-color: #f7f7f7;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
background-color: #f5f5f5;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.popup-option.selected {
|
||||
@@ -580,4 +581,4 @@ export default {
|
||||
color: #fff;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
</style>
|
||||
</style>
|
@@ -74,11 +74,11 @@
|
||||
text: '报事报修',
|
||||
url:'/pages/sys/user/myRepair/myRepair'
|
||||
},
|
||||
// {
|
||||
// icon: 'https://picsum.photos/80/80?random=3',
|
||||
// text: '工作巡检',
|
||||
// url:'/pages/sys/workbench/inspection/inspection'
|
||||
// },
|
||||
{
|
||||
icon: 'https://picsum.photos/80/80?random=3',
|
||||
text: '工作巡检',
|
||||
url:'/pages/sys/workbench/inspection/inspection'
|
||||
},
|
||||
// {
|
||||
// icon: 'https://picsum.photos/80/80?random=3',
|
||||
// text: '通讯录',
|
||||
|
Reference in New Issue
Block a user