Files
SmartParks_uniapp/pages/sys/workbench/leave/leave.vue
liyuanchao c7ff9a5234
Some checks failed
Uniapp 自动化打包 CI/CD / 打包 Uniapp 项目 (push) Has been cancelled
1.
2025-09-05 16:54:53 +08:00

584 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="leave-container">
<!-- tab栏 -->
<view class="leave-tabs">
<view v-for="(tab, idx) in tabs" :key="idx" :class="['leave-tab', {active: idx === activeTab}]"
@click="changeTab(idx)">
{{ tab }}
<view v-if="idx === activeTab" class="tab-underline"></view>
</view>
</view>
<!-- 发起申请 -->
<scroll-view v-show="activeTab === 0" class="leave-form" scroll-y>
<!-- 提交人 -->
<view class="form-row2">
<text class="label">提交人</text>
<view class="leave-round"></view>
<text class="name">马花花</text>
</view>
<!-- 请假类型 -->
<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" />
</view>
</view>
<!-- 开始时间 -->
<view class="form-row">
<text class="label required">开始时间</text>
<picker mode="date" @change="startDateChange">
<view class="picker">
<text :class="!startDate ? 'placeholder' : ''">{{ startDate || '请选择' }}</text>
<image class="right-arrow" src="/static/ic_right_arrow_g.png" />
</view>
</picker>
</view>
<!-- 结束时间 -->
<view class="form-row">
<text class="label required">结束时间</text>
<picker mode="date" @change="endDateChange">
<view class="picker">
<text :class="!endDate ? 'placeholder' : ''">{{ endDate || '请选择' }}</text>
<image class="right-arrow" src="/static/ic_right_arrow_g.png" />
</view>
</picker>
</view>
<!-- 时间小时 -->
<view class="form-row">
<text class="label required">时间小时</text>
<input type="number" v-model="hours" placeholder="请输入时间(小时)" />
</view>
<!-- 请假事由 -->
<view class="form-row">
<text class="label required">请假事由</text>
<textarea v-model="reason" placeholder="请输入请假事由..." />
</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>
<!-- 提交按钮 -->
<button class="submit-btn" @click="submitLeave">提交</button>
</scroll-view>
<!-- 请假记录 -->
<scroll-view v-show="activeTab === 1" class="leave-list-scroll" scroll-y :refresher-enabled="true"
:refresher-triggered="refreshing" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
<view v-for="(item, index) in leaveRecords" :key="index" class="oa-card" @click="goToDetail(item)">
<view class="card-row">
<view class="card-type">{{ item.type }}</view>
<view class="card-status-tag" :class="item.statusClass">{{ item.status }}</view>
</view>
<view class="card-info">
<view class="card-leave-type">请假类型<text class="card-leave-type">{{ item.leaveType }}</text></view>
<view class="card-leave-type">
<text>开始时间{{ item.startTime }}</text>
</view>
<view class="card-leave-type">
<text>结束时间{{ item.endTime }}</text>
</view>
</view>
<view class="card-footer">
<view class="card-user">
<view class="user-avatar" :style="{backgroundColor: item.avatarColor}">{{ item.avatarText }}
</view>
<text class="user-name">{{ item.userName }}</text>
</view>
<text class="card-datetime">{{ item.dateTime }}</text>
</view>
</view>
<!-- 加载更多提示 -->
<view class="loading-more">
<view v-if="loading && !refreshing" class="loading-text">加载中...</view>
<view v-else-if="!hasMore && leaveRecords.length > 0" class="loading-text">没有更多数据了</view>
</view>
</scroll-view>
<!-- 已完成 -->
<view v-show="activeTab === 2" class="completed-tab">
<!-- 暂时留空 -->
</view>
<!-- 弹出层 -->
<u-popup v-model="showLeaveTypePopup" mode="bottom" border-radius="16">
<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)"
>
{{ type }}
</view>
</view>
<button class="popup-submit-btn" @click="confirmLeaveType">提交</button>
</view>
</u-popup>
</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];
}
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>
.leave-container {
height: 100vh;
background: #f7f7f7;
display: flex;
flex-direction: column;
overflow: hidden;
}
.leave-tabs {
display: flex;
align-items: center;
justify-content: space-around;
background: #fff;
height: 80rpx;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
}
.leave-tab {
flex: 1;
text-align: center;
font-size: 32rpx;
color: #737373;
position: relative;
padding: 0 0 10rpx 0;
cursor: pointer;
}
.leave-tab.active {
color: #007CFF;
font-weight: bold;
}
.tab-underline {
width: 60rpx;
height: 6rpx;
background: #2186FF;
border-radius: 3rpx;
margin: 0 auto;
margin-top: 8rpx;
}
/* 让 leave-form 可滚动 */
.leave-form {
flex: 1;
padding: 20rpx;
background: #fff;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
overflow-y: auto;
}
.form-row {
margin-bottom: 34rpx;
display: flex;
flex-direction: column;
margin-left: 20rpx;
margin-right: 20rpx;
}
.form-row2 {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 34rpx;
margin-left: 20rpx;
margin-right: 20rpx;
margin-top: 20rpx;
}
.label {
font-size: 32rpx;
color: #000000;
font-weight: 600;
}
.required::after {
content: "*";
color: #DC9100;
margin-left: 10rpx;
}
.picker {
height: 73rpx;
width: auto;
background: #F7F7F7;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
margin-top: 20rpx;
margin-right: 40rpx;
}
.placeholder {
color: #808080;
}
.right-arrow {
width: 11rpx;
height: 21rpx;
}
input {
height: 73rpx;
width: auto;
background: #F7F7F7;
border-radius: 10rpx;
display: flex;
align-items: center;
padding-left: 20rpx;
margin-top: 20rpx;
}
textarea {
height: 200rpx;
width: auto;
background: #F7F7F7;
border-radius: 10rpx;
font-size: 24rpx;
color: #000000;
padding: 15rpx;
margin-top: 20rpx;
}
.leave-round {
width: 34rpx;
height: 34rpx;
border-radius: 17rpx;
background: #3370FF;
margin-left: 18rpx;
}
.name {
font-size: 24rpx;
color: #000000;
margin-left: 10rpx;
font-weight: 400;
}
.submit-btn {
width: 80vw;
height: 88rpx;
background: linear-gradient(90deg, #005DE9 0%, #4B9BFF 100%);
border-radius: 44rpx;
font-size: 32rpx;
color: #fff;
margin-top: 20rpx;
margin-bottom: 90rpx;
}
.leave-list-scroll {
flex: 1;
padding: 0 35rpx;
overflow-y: auto;
box-sizing: border-box;
margin-top: 20rpx;
}
.oa-card {
background: #fff;
border-radius: 10rpx;
margin-bottom: 26rpx;
padding: 20rpx 20rpx 20rpx 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.card-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 46rpx;
}
.card-type {
width: 126rpx;
height: 46rpx;
font-weight: 600;
font-size: 32rpx;
padding-left: 10rpx;
padding-top: 7rpx;
border-radius: 15rpx;
color: #fff;
background: linear-gradient(90deg, #007CFF 0%, #FFFFFF 100%);
}
.card-status-tag {
font-size: 28rpx;
}
.card-status-tag.orange {
color: #F27A0F;
}
.card-status-tag.green {
color: #0AC88F;
}
.card-status-tag.gray {
color: #8F8F8F;
background-color: rgba(143, 143, 143, 0.1);
border: 1px solid #8F8F8F;
}
.card-info {
font-size: 26rpx;
color: #333;
margin-bottom: 42rpx;
}
.card-leave-type {
color: #626262;
font-size: 28rpx;
margin-bottom: 24rpx;
}
.card-footer {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-user {
display: flex;
align-items: center;
}
.user-avatar {
width: 54rpx;
height: 54rpx;
border-radius: 27rpx;
background-color: #688CFF;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
margin-right: 18rpx;
}
.user-name {
font-size: 24rpx;
color: #626262;
}
.card-datetime {
font-size: 24rpx;
color: #626262;
}
.loading-more {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx 0;
}
.loading-text {
font-size: 28rpx;
color: #999;
}
.upload-style{
margin-left: 15rpx;
margin-right: 15rpx;
}
.custom-leave-type-btn {
width: 100%;
height: 73rpx;
background: #F7F7F7;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
margin-top: 20rpx;
font-size: 32rpx;
color: #000;
}
.popup-content {
width: 100%;
background-color: #fff;
padding: 40rpx;
border-radius: 16rpx 16rpx 0 0;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.popup-title {
font-size: 36rpx;
color: #000;
text-align: center;
margin-bottom: 40rpx;
}
.popup-options {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.popup-option {
width: 30%;
height: 80rpx;
background-color: #f7f7f7;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #333;
margin-bottom: 20rpx;
}
.popup-option.selected {
background-color: #3370FF;
color: #fff;
}
.popup-submit-btn {
width: 80vw;
height: 88rpx;
background: linear-gradient(90deg, #005DE9 0%, #4B9BFF 100%);
border-radius: 44rpx;
font-size: 32rpx;
color: #fff;
margin-top: 40rpx;
}
</style>