Browse Source

feat(common): 新增请假、打卡、签退功能

- 添加了请假、打卡、签退相关的 API 接口
- 实现了请假、打卡、签退的前端逻辑和界面
- 增加了用户信息获取和校区选择功能
- 优化了考勤列表的展示方式
master
liutong 10 months ago
parent
commit
a699964802
  1. 14
      api/apiRoute.js
  2. 489
      pages/common/my_attendance.vue

14
api/apiRoute.js

@ -38,6 +38,20 @@ export default {
return res;
})
},
//教师/销售端请假/打卡/签退-编辑
common_attendanceEdit(data = {}) {
let url = '/attendance/edit'
return http.post(url, data).then(res => {
return res;
})
},
//教师/销售端请假/打卡/签退-列表
common_attendanceIndex(data = {}) {
let url = '/attendance/index'
return http.post(url, data).then(res => {
return res;
})
},
//获取字典数据
common_Dictionary(data = {}) {
let url = '/common/getDictionary'

489
pages/common/my_attendance.vue

@ -3,7 +3,7 @@
<view class="main_box">
<fui-segmented-control
:values="optionTable"
:current="(Number(filteredData.status))"
:current="(Number(currentIndex))"
type="text"
activeColor="#29d3b4"
color="#fff"
@ -11,13 +11,11 @@
</fui-segmented-control>
<view class="main_section">
<!-- 教练端-->
<view v-if="userType == 1">
<view>
<!--全部-->
<scroll-view
class="section_1"
v-if="filteredData.status == '0'"
v-if="currentIndex == '0'"
scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="loadMoreData"
@ -29,85 +27,34 @@
:key="k"
>
<view class="left">
<image :src="$util.img(v.courses.thumbnail)" model="aspectFill"></image>
<!-- <image src="http://www.firstui.cn:4000/vipdoc/img/img_logo.png" model="aspectFill"></image>-->
</view>
<view class="right">
<view class="content">{{v.courses.name}}</view>
<view class="content">普通考勤</view>
<view class="content">
{{v.status == 1 ? '考勤正常':'请假'}}
</view>
<view class="content">{{v.add_time}} - {{v.end_time}}</view>
</view>
{{v.status_name}}
</view>
</view>
</scroll-view>
<!--考勤-->
<scroll-view
class="section_1"
v-if="filteredData.status == '1'"
scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="loadMoreData"
style="height: 80vh;"
>
<view class="ul">
<view class="li"
v-for="(v,k) in tableList"
:key="k"
>
<view class="left">
<image :src="$util.img(v.courses.thumbnail)" model="aspectFill"></image>
</view>
<view class="right">
<view class="content">{{v.courses.name}}</view>
<view class="content">
{{v.status == 1 ? '考勤正常':'请假'}}
</view>
<view class="content">{{v.add_time}} - {{v.end_time}}</view>
</view>
</view>
校区{{v.campus_id_name}}
</view>
</scroll-view>
<!--请假-->
<scroll-view
class="section_1"
v-if="filteredData.status == '2'"
scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="loadMoreData"
style="height: 80vh;"
>
<view class="ul">
<view class="li"
v-for="(v,k) in tableList"
:key="k"
>
<view class="left">
<image :src="$util.img(v.courses.thumbnail)" model="aspectFill"></image>
<view class="content">
备注{{v.remarks || ''}}
</view>
<view class="right">
<view class="content">{{v.courses.name}}</view>
<view class="content">
{{v.status == 1 ? '考勤正常':'请假'}}
<text>{{v.attendance_date}} {{v.check_in_time}}</text>
<text v-if="v.check_out_time" style="padding: 0 20rpx">-</text>
<text v-if="v.check_out_time">{{v.attendance_date}} {{v.check_out_time}}</text>
</view>
<view class="content">{{v.add_time}} - {{v.end_time}}</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 销售端-->
<view v-else>
<!--全部-->
<!--考勤-->
<scroll-view
class="section_1"
v-if="filteredData.status == '0'"
v-if="currentIndex == '1'"
scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="loadMoreData"
@ -124,48 +71,28 @@
<view class="right">
<view class="content">普通考勤</view>
<view class="content">
{{v.status == 1 ? '考勤正常':'请假'}}
</view>
<view class="content">{{v.add_time}} - {{v.end_time}}</view>
</view>
{{v.status_name}}
</view>
<view class="content">
校区{{v.campus_id_name}}
</view>
</scroll-view>
<!--考勤-->
<scroll-view
class="section_1"
v-if="filteredData.status == '1'"
scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="loadMoreData"
style="height: 80vh;"
>
<view class="ul">
<view class="li"
v-for="(v,k) in tableList"
:key="k"
>
<view class="left">
<!-- <image src="http://www.firstui.cn:4000/vipdoc/img/img_logo.png" model="aspectFill"></image>-->
<view class="content">
备注{{v.remarks || ''}}
</view>
<view class="right">
<view class="content">普通考勤</view>
<view class="content">
{{v.status == 1 ? '考勤正常':'请假'}}
<text>{{v.attendance_date}} {{v.check_in_time}}</text>
<text v-if="v.check_out_time" style="padding: 0 20rpx">-</text>
<text v-if="v.check_out_time">{{v.attendance_date}} {{v.check_out_time}}</text>
</view>
<view class="content">{{v.add_time}} - {{v.end_time}}</view>
</view>
</view>
</view>
</scroll-view>
<!--请假-->
<scroll-view
class="section_1"
v-if="filteredData.status == '2'"
v-if="currentIndex == '2'"
scroll-y="true"
:lower-threshold="lowerThreshold"
@scrolltolower="loadMoreData"
@ -182,9 +109,19 @@
<view class="right">
<view class="content">普通考勤</view>
<view class="content">
{{v.status == 1 ? '考勤正常':'请假'}}
{{v.status_name}}
</view>
<view class="content">
校区{{v.campus_id_name}}
</view>
<view class="content">
备注{{v.remarks || ''}}
</view>
<view class="content">
<text>{{v.attendance_date}} {{v.check_in_time}}</text>
<text v-if="v.check_out_time" style="padding: 0 20rpx">-</text>
<text v-if="v.check_out_time">{{v.attendance_date}} {{v.check_out_time}}</text>
</view>
<view class="content">{{v.add_time}} - {{v.end_time}}</view>
</view>
</view>
</view>
@ -192,19 +129,100 @@
</view>
<!--请假按钮-->
<view class="section_btn">
<view class="btn" @click="openShow()">请假</view>
<!--打卡按钮-->
<view class="btn" style="background-color: #20CAAFFF;" @click="openSignInShow('present')">打卡</view>
<!--请假按钮-->
<view class="btn" style="background-color: #ffb703;" @click="openSignInShow('leave')">请假</view>
<!--签退按钮-->
<view class="btn" style="background-color: #465cff;" @click="openSignInShow('sign_out')">签退</view>
</view>
</view>
<!--打卡提示框-->
<fui-modal :buttons="[]" width="600" :show="signIn_show">
<text class="fui-title">{{signIn_title}}</text>
<text class="fui-descr">{{signIn_content}}</text>
<fui-form class="form-section" ref="form" top="0" :model="formData" :show="false">
<view class="input-style">
<!--校区选择-->
<fui-form-item
label="选择校区"
asterisk
asteriskPosition="right"
labelSize='26'
prop=""
background='#fff'
labelColor='#000'
:bottomBorder='true'
>
<view class="input-title" style="margin-right:14rpx;">
<view
class="input-title"
style="margin-right:14rpx;"
@click="openCicker()">
{{ (formData.campus_id) ? formData.campus_id_name : '点击选择' }}
</view>
</view>
</fui-form-item>
<!--备注-->
<fui-form-item
v-if="formData.status == 'leave'"
label="备注"
labelSize='26'
prop=""
background='#fff'
labelColor='#000'
:bottomBorder='true'
>
<view class="input-title" style="margin-right:14rpx;">
<fui-input
:borderBottom="false"
:padding="[0]"
placeholder="请输入备注"
v-model="formData.remarks"
backgroundColor="#fff"
size="26"
color="#000"
></fui-input>
</view>
</fui-form-item>
</view>
<view class="button_box">
<fui-button background="#fff" color="#414141" borderColor="#465CFF" btnSize="small" @click="closeSignInShow">取消</fui-button>
<fui-button background="#fff" color="#465CFF" borderColor="#465CFF" btnSize="small" @click="clickSignIn({index:1})">确定</fui-button>
</view>
</fui-form>
<view class="fui-icon__close" @tap="closeSignInShow">
<fui-icon name="close" color="#B2B2B2" :size="48"></fui-icon>
</view>
</fui-modal>
<!-- 选择器 -->
<fui-picker
:linkage='picker_linkage'
:options="picker_options"
:layer="1"
:show="picker_show"
@change="changeCicker"
@cancel="cancelCicker">
</fui-picker>
<!-- 请假提示框-->
<fui-dialog :show="show" :content="content" maskClosable @click="onClick" @close="closeShow"></fui-dialog>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js';
import commonApi from '@/api/common.js';
import marketApi from '@/api/market.js';
import AQTabber from "@/components/AQ/AQTabber.vue"
@ -221,17 +239,23 @@ export default {
optionTable: [
{
id: 0,
name: '全部'
name: '全部',
value:[]
},
{
id: 1,
name: '考勤'
name: '考勤',
value:['present']
},
{
id: 2,
name: '请假'
name: '请假',
value:['leave']
}
],
currentIndex:0,
userInfo:{},//
loading:false,//
@ -243,18 +267,43 @@ export default {
page:1,//
limit:10,//
total:10,//
status:'0'//0 1 2
status_arr:[]//: present-, absent-, late-, leave_early-退,leave-
},
tableList:[],//
//
//--退
formData:{
date:'',//
campus_id:'',//ID
campus_id_name:'',//ID
status:'',//: present-, absent-, late-, leave_early-退,leave-,sign_out-退
remarks:'',//
attendance_date:'',///
longitude:'',//
latitude:'',//
},
//
show:false,//
content:'',//
//
signIn_show:false,
signIn_title:'',//
signIn_content:'',//
//
picker_input_name:'',//input_name
picker_show:false,//
picker_linkage:true,//
picker_options:[
// {
// text:'',
// value:'1'
// },
],//
}
},
onLoad(options) {},
@ -265,9 +314,32 @@ export default {
//
async init(){
this.userType = uni.getStorageSync('userType')
// this.userType = 2
this.getCurrentDate()//
await this.getList();
await this.getUserInfo()//
// await this.getList();
this.getList();
},
//
async getUserInfo(){
let res = await apiRoute.getPersonnelInfo({})
if (res.code != 1) {
uni.showToast({
title: res.msg,
icon: 'none'
})
return
}
this.userInfo = res.data
this.picker_options = []
res.data.cameus_dept_arr.forEach((v,k)=>{
this.picker_options.push({
text: v.campus_id_name,
value: v.campus_id
})
})
console.log(123123,this.picker_options)
},
//
@ -280,20 +352,32 @@ export default {
let res = `${year}-${month}-${day}`
this.content = `${res} 是否确认请假?`
this.formData.date = res
this.formData.attendance_date = res
},
//tag
async segmented(e) {
console.log(e)
this.currentIndex = e.id
//
await this.resetFilteredData()
//e.id|0 1 2
let status = e.id
this.filteredData.status = String(status)
let status_arr = []
switch (status) {
case 1:
status_arr = ['present']
break;
case 2:
status_arr = ['leave']
break;
}
this.filteredData.status_arr = status_arr
await this.getList()
},
@ -333,7 +417,7 @@ export default {
this.tableList = []
}
let res = await commonApi.clockingList(data)
let res = await apiRoute.common_attendanceIndex(data)
this.loading = false
this.isReachedBottom = false;
if (res.code != 1){
@ -346,7 +430,7 @@ export default {
this.tableList = this.tableList.concat(res.data.data); // 使 concat
console.log('列表',this.tableList)
console.log('列表',res.data.data)
this.filteredData.total = res.data.total
this.filteredData.page++
},
@ -360,24 +444,139 @@ export default {
this.show = false
},
//
onClick(e){
async onClick(e){
console.log('xxx',e)
if(e.index == 0){
//
this.closeShow()
}else{
if(!this.formData.campus_id){
uni.showToast({
title: '请选择校区',
icon: 'none'
})
return
}
//
this.submitRest()
this.formData.status='absent'//|present-, absent-
await this.getLocation()//
await this.submitFormData()
}
},
//
async submitRest() {
this.closeShow()
//
//
openSignInShow(status){
this.signIn_show = true
this.formData.status = status
switch (status){
case 'present':
this.signIn_title = `是否确认打卡?`
this.signIn_content = `${this.formData.attendance_date} 是否确认打卡?`
break;
case 'leave':
this.signIn_title = `是否确认请假?`
this.signIn_content = `${this.formData.attendance_date} 是否确认请假?`
break;
case 'sign_out':
this.signIn_title = `是否确认签退?`
this.signIn_content = `${this.formData.attendance_date} 是否确认签退?`
break;
}
//
this.formData.campus_id = ''
this.formData.campus_id_name = ''
this.formData.remarks = ''
this.formData.longitude = ''
this.formData.latitude = ''
},
//
closeSignInShow(){
this.signIn_show = false
},
//-
async clickSignIn(e){
console.log('xxx',e)
if(e.index == 0){
//
this.closeSignInShow()
}else{
if(!this.formData.campus_id){
uni.showToast({
title: '请选择校区',
icon: 'none'
})
return
}
await this.getLocation()//
await this.submitFormData()
}
},
//-
openCicker(e){
// 使 nextTick picker
this.$nextTick(() => {
this.picker_show = true
})
},
//-
changeCicker(e) {
console.log('监听-下拉选择器', e)
this.formData.campus_id = e.value
this.formData.campus_id_name = e.text
this.cancelCicker()
},
//
cancelCicker() {
this.picker_show = false
},
//
async getLocation() {
try {
const location = await new Promise((resolve, reject) => {
uni.getLocation({
type: 'wgs84', // wgs84 gps gcj02
success: (res) => {
resolve(res);
},
fail: (err) => {
reject(err);
}
});
});
console.log('纬度:', location.latitude); //
console.log('经度:', location.longitude); //
// data
this.formData.latitude = location.latitude;
this.formData.longitude = location.longitude;
// uni.showToast({
// title: '',
// icon: 'success'
// });
} catch (error) {
uni.showToast({
title: '获取位置失败',
icon: 'none'
});
console.error('获取位置失败:', error);
}
},
///
async submitFormData() {
this.closeSignInShow()
let param = {...this.formData}
let res = await commonApi.clockingRest(param)
let res = await apiRoute.common_attendanceEdit(param)
if (res.code != 1) {
uni.showToast({
title: res.msg,
@ -443,6 +642,9 @@ export default {
flex-direction: column;
gap: 24rpx;
.li{
border: 1px solid #5f5f5f;
border-radius: 15rpx;
padding: 20rpx 0;
display: flex;
align-items: center;
gap: 43rpx;
@ -480,12 +682,13 @@ export default {
.section_btn{
display: flex;
justify-content: center;
display: flex;
justify-content: space-between;
.btn{
width: 722rpx;
width: 30%;
height: 64rpx;
line-height: 64rpx;
border-radius: 8rpx;
background-color: rgba(32,202,175,1);
color: rgba(255,255,255,1);
font-size: 28rpx;
text-align: center;
@ -495,4 +698,42 @@ export default {
}
//
.fui-title {
font-size: 32rpx;
padding-top: 24rpx;
}
.fui-descr {
font-size: 24rpx;
color: #B2B2B2;
padding-top: 12rpx;
padding-bottom: 48rpx;
}
.fui-icon__close {
position: absolute;
right: 24rpx;
top: 20rpx;
}
.form-section{
.input-style {
text-align: right !important;
}
.button_box{
margin-top: 30rpx;
padding: 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
}
}
</style>
Loading…
Cancel
Save