h5
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

412 lines
13 KiB

<template>
<view>
<!-- 标题 + 筛选 -->
<view class="check-work-title px-3 fixed top-0 z-10 w-full flex justify-between items-center bg-white">
<text>考勤管理</text>
<view>
<u-button class="mr-3" size="mini" type="primary" @click="isShow = !isShow">过滤</u-button>
<u-button size="mini" type="primary" @click="isShow = !isShow">导出</u-button>
</view>
</view>
<!-- 列表 -->
<view class="check-work-list">
<u-table class="table-head">
<u-tr>
<u-th>姓名</u-th>
<u-th></u-th>
<u-th></u-th>
<u-th width="30%">审核人</u-th>
</u-tr>
</u-table>
<view v-if="clockInfos.length" v-for="(list, listIndex) in clockInfos" :key="listIndex">
<view class="table-time px-2">{{ list.dateTime }}</view>
<u-table class="table-body">
<u-tr v-if="list.recordList.length" v-for="(item, index) in list.recordList" :key="index">
<u-td>{{ item.memberName }}</u-td>
<u-td>
<template v-if="!item.morningStatus">
<view v-if="item.isMine === 1">
<u-button class="m-0" size="mini" type="primary" @click="showTimeSelect(0, item, list.dateTime)">早打卡</u-button>
</view>
<template v-else>
<view>未打卡</view>
</template>
</template>
<view v-else class="flex justify-center">
<view :class="{'relative': item.morningStatus === 1}">
<text :class="{'font-semibold': item.isMine === 1 || item.isChecker === 1,
'text-green-500': item.isChecker === 1 && (item.morningStatus === 1 || item.morningStatus === 3),
'line-through': item.morningStatus === 2,
'text-gray-400': item.morningStatus === 2 && (item.isMine === 1 || item.isChecker === 1),
'text-cyan': item.morningStatus === 3 && item.isMine === 1}"
@click="showActionPanel(item.morningStatus, 'morning', item.morning, item)"
>
{{ dayjs(+item.morning).format("HH:mm") }}
</text>
<!-- 小红点 -->
<template v-if="item.morningStatus === 1">
<u-badge v-if="item.isMine === 1" :is-dot="true"
type="error" size="mini" style="top: -2px; right: -10px;"></u-badge>
<!-- 小红点 -->
<template v-else-if="item.isChecker === 1">
<u-badge class="animate-ping" :is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;"></u-badge>
<u-badge :is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;"></u-badge>
</template>
<!-- 小红点 -->
<u-badge v-else :is-dot="true"
type="warning" size="mini" style="top: -2px; right: -10px;"></u-badge>
</template>
</view>
</view>
</u-td>
<u-td>
<template v-if="!item.nightStatus">
<view v-if="item.isMine === 1">
<u-button class="m-0" size="mini" type="primary" @click="showTimeSelect(1, item, list.dateTime)">晚打卡</u-button>
</view>
<template v-else>
<view>未打卡</view>
</template>
</template>
<view v-else class="flex justify-center">
<view :class="{'relative': item.nightStatus === 1}">
<text :class="{'font-semibold': item.isMine === 1 || item.isChecker === 1,
'text-green-500': item.isChecker === 1 && (item.nightStatus === 1 || item.nightStatus === 3),
'line-through': item.nightStatus === 2,
'text-gray-400': item.nightStatus === 2 && (item.isMine === 1 || item.isChecker === 1),
'text-cyan': item.nightStatus === 3 && item.isMine === 1}"
@click="showActionPanel(item.nightStatus, 'night', item.night, item)"
>
{{ dayjs(+item.night).format("HH:mm") }}
</text>
<!-- 小红点 -->
<template v-if="item.nightStatus === 1">
<u-badge v-if="item.isMine === 1" :is-dot="true"
type="error" size="mini" style="top: -2px; right: -10px;"></u-badge>
<!-- 小红点 -->
<template v-else-if="item.isChecker === 1">
<u-badge class="animate-ping" :is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;"></u-badge>
<u-badge :is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;"></u-badge>
</template>
<!-- 小红点 -->
<u-badge v-else :is-dot="true"
type="warning" size="mini" style="top: -2px; right: -10px;"></u-badge>
</template>
</view>
</view>
</u-td>
<u-td width="30%">
<view class="flex justify-center">
<view v-if="item.isMine === 1 && (!item.morningStatus && !item.nightStatus)" class="px-2 py-1 flex items-center border border-solid rounded border-gray-200" @click="show = true">
<text class="mr-3 whitespace-nowrap">{{ item.lastCheckerName ? item.lastCheckerName : item.checkerName ? item.checkerName : checkerName }}</text>
<u-icon size="24" name="arrow-down"></u-icon>
</view>
<view v-else>{{ item.lastCheckerName ? item.lastCheckerName : item.checkerName ? item.checkerName : '周勇' }}</view>
</view>
</u-td>
</u-tr>
</u-table>
</view>
</view>
<!-- 审核人列表 -->
<u-select v-model="show" :list="list" @confirm="confirm"></u-select>
<!-- 打卡弹框 -->
<u-modal v-model="punchModal" title="打卡" :showCancelButton="true" @confirm="confirmPunch">
<view class="px-4 text-sm">
<!-- 文字内容 -->
<view class="flex justify-between items-center">
<view>打卡原因</view>
<input class="text-sm text-right" v-model="remark" placeholder="请输入打卡原因" />
</view>
<!-- 时间选择 -->
<view class="my-4 flex justify-between items-center">
<view>打卡时间</view>
<view @click="showChangeTime">{{ dayjs(+selectedTime).format("HH:mm") }}</view>
</view>
</view>
</u-modal>
<!-- 审核操作按钮列表 -->
<u-action-sheet :list="actions" v-model="actionShow" @click="clickAction"></u-action-sheet>
<!-- 时间选择框 -->
<u-picker mode="time" v-model="showModal" :params="params" @confirm="changeTime"></u-picker>
<!-- 筛选框 -->
<SearchPopup v-if="isShow" :members="list" :show="isShow" @closePopup="closePopup" @getClockQuery="getClockQuery" @clockExport="clockExport"></SearchPopup>
</view>
</template>
<script setup>
import { computed, onMounted, ref } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import SearchPopup from '@/components/SearchPopup/SearchPopup.vue';
const props = defineProps({
clockParams: {
type: Object,
default: () => {}
}
})
const store = useStore();
const projectId = uni.$storage.getStorageSync('projectId');
const roleId = uni.$storage.getStorageSync('roleId');
const checkers = uni.$storage.getStorageSync('checkers'); // 检查人列表
const url = uni.$storage.getStorageSync('domain');
let clockInfos = ref([]); // 打卡列表
const show = ref(false); // 是否展示审核人列表
let list = ref([]); // 审核人列表
let actionShow = ref(false); // 审核操作按钮列表
const actions = ref([{ text: '修改' }, { text: '驳回' }, { text: '确认' }]); // 操作按钮列表
let examineParams = ref({}); // 审核参数
let punchModal = ref(false); // 打卡模态框
let remark = ref(null); // 打卡原因
let showModal = ref(false); // 修改时间模态框
const params = ref({year: false, month: false, day: false, hour: true, minute: true, timestamp: true})
let checkerId = ref(null);
let checkerName = ref(null);
let isShow = ref(false); // 是否打开筛选框
let timeType = ref(null); // 时间类型 0 早打卡 1 晚打卡
let actionType = ref(0); // 操作类型:0 打卡 1 审核
let currRecord = ref({}); // 当前点击的打卡记录
let selectedDate = ref(null); // 选中的日期
let selectedTime = ref(null); // 选中的时间
let startTime = dayjs().startOf('day').valueOf();
let endTime = dayjs().endOf('day').valueOf();
let memberIdList = ref([]);
onMounted(() => {
selectedTime.value = dayjs().valueOf();
list.value = [];
let checker_arr = JSON.parse(checkers);
checker_arr.forEach(item => {
list.value.push({
value: item.memberId,
label: item.name
})
if (item.name === '周勇') {
checkerId.value = item.memberId;
checkerName.value = item.name;
}
})
getClockQuery(props.clockParams);
})
/**
* 批量查询打卡信息
*/
async function getClockQuery(data) {
closePopup();
startTime = data ? dayjs(data.startTime).startOf('day').valueOf() : startTime;
endTime = data ? dayjs(data.endTime).endOf('day').valueOf() : endTime;
memberIdList.value = data ? data.memberIdList : memberIdList.value;
try {
const params = { projectId, roleId, memberIdList: memberIdList.value, startTime, endTime }
const data = await uni.$u.api.clockQuery(params, url);
data.forEach(list => {
let arr = [];
list.recordList.forEach(item => {
if (item.isMine === 1) {
arr.push(item);
}
})
list.recordList.forEach(item => {
if (item.isChecker === 1) {
arr.push(item);
}
})
list.recordList.forEach(item => {
if (!item.isMine && !item.isChecker) {
arr.push(item);
}
})
list.recordList = arr;
})
clockInfos.value = data;
} catch (error) {
console.log('error: ', error);
}
}
// 选择审核人结果
function confirm(e) {
checkerId.value = e[0].value;
checkerName.value = e[0].label;
}
// 打卡选择时间
function showTimeSelect(clockType, record, time) {
punchModal.value = true; // 显示打卡模态框
selectedDate.value = time; // 选中的日期
actionType.value = 0; // 打卡还是修改时间 0 打卡 1 审核人修改时间
timeType.value = clockType; // 0 早打卡 1 晚打卡
currRecord.value = record; // 当前打卡记录
}
// 打卡时间选择弹框
function showChangeTime() {
showModal.value = true;
}
// 打卡
async function punch() {
checkerId.value = currRecord.value.lastCheckerId ? currRecord.value.lastCheckerId : currRecord.value.checkerId ? currRecord.value.checkerId : checkerId.value;
try {
const params = {
checkerId: checkerId.value,
id: !currRecord.value.id ? null : currRecord.value.id,
memberId: currRecord.value.memberId,
dateTime: selectedTime.value,
clockType: timeType.value,
remark: remark.value
}
const data = await uni.$u.api.clockPunch(params, url);
getClockQuery(); // 打卡成功重新加载打卡信息
} catch (error) {
console.log('error: ', error);
}
}
// 点击待审核时间打开操作面板
async function showActionPanel(status, type, time, data) {
if (status !== 1) return;
actionShow.value = true;
timeType.value = type;
examineParams.value = { id: data.id, [type]: time, projectId, type: 0 };
}
// 选择的操作下班 0 修改 1 驳回 2 确认
function clickAction(index) {
if (index === 1) { // 驳回
examineParams.value.type = 1;
} else if (index === 2) { // 确认
examineParams.value.type = 2;
}
if (index === 0) {
// 修改
showModal.value = true;
actionType.value = 1;
} else {
handleClockAudit();
}
}
// 时间改变确认事件
function changeTime(e) {
const date = selectedDate.value + " " + e.hour + ":" + e.minute;
selectedTime.value = dayjs(date).valueOf();
examineParams.value[timeType.value] = dayjs(date).valueOf();
if (actionType.value === 1) {
handleClockAudit();
}
}
// 打卡弹框回调函数
function confirmPunch() {
punch();
}
// 审核
async function handleClockAudit() {
try {
const data = await uni.$u.api.clockAudit(examineParams.value, url);
getClockQuery();
} catch (error) {
console.log('error: ', error);
}
}
// 关闭过滤弹框
function closePopup(data) {
isShow.value = data;
}
// 导出
function clockExport() {
closePopup();
}
</script>
<style lang="scss" scoped>
.whitespace-nowrap {
white-space: nowrap;
}
.check-work-title {
height: 50px;
border-bottom: 1px solid #e8e8e8;
}
.check-work-list {
padding-top: 50px;
}
.u-table {
border-left: unset !important;
}
.table-head {
border-top: unset !important;
}
.u-th {
border-right: unset !important;
height: 36px;
background: #fafafa;
}
.u-td {
border-right: unset !important;
height: 40px;
}
.table-time {
height: 30px;
line-height: 30px;
background: #fafafa;
}
.text-cyan {
color: #3B83F6;
}
</style>