|
|
@ -1,87 +1,164 @@ |
|
|
|
<template> |
|
|
|
<div class="mt-5"> |
|
|
|
<div class="d-flex flex-nowrap table-head"> |
|
|
|
<div style="width: 10%"></div> |
|
|
|
<div class="table-head-item">姓名</div> |
|
|
|
<div class="table-head-item" style="width: 16%">姓名</div> |
|
|
|
<div class="table-head-item">早</div> |
|
|
|
<div class="table-head-item">晚</div> |
|
|
|
<div class="table-head-item">审核人</div> |
|
|
|
<div class="table-head-item" style="width: 30%">审核人</div> |
|
|
|
</div> |
|
|
|
<div v-if="clockInfos && clockInfos.length"> |
|
|
|
<div v-for="(list, listIndex) in clockInfos" :key="listIndex" class="teble-box"> |
|
|
|
<div class="table-time px-2">{{ list.dateTime }}</div> |
|
|
|
<a-table :pagination="false" :show-header="false" :columns="columns" :data-source="list.recordList"> |
|
|
|
<template slot="actions" slot-scope="text, record, index"> |
|
|
|
<img @click="showMenu(index)" src="https://www.tall.wiki/staticrec/drag.svg" /> |
|
|
|
</template> |
|
|
|
<!-- 早 --> |
|
|
|
<template slot="morning" slot-scope="text, record, index"> |
|
|
|
<div v-if="record.morningStatus"> |
|
|
|
{{ $moment(record.morning - 0).format('HH:mm') }} |
|
|
|
<div v-if="!record.isMine && !record.isChecker"> |
|
|
|
<span v-if="record.morningStatus">{{ $moment(record.morning - 0).format('HH:mm') }}</span> |
|
|
|
<span v-else>未打卡</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div v-else> |
|
|
|
<div v-if="record.isMine"> |
|
|
|
<span v-if="record.morningStatus" class="baseColor font-bold"> |
|
|
|
{{ $moment(record.morning - 0).format('HH:mm') }} |
|
|
|
</span> |
|
|
|
<a-button |
|
|
|
v-else |
|
|
|
type="primary" |
|
|
|
size="small" |
|
|
|
@click="checkTime(listIndex, index, 0, record.id, record.memberId, record.checkerId)" |
|
|
|
> |
|
|
|
打卡 |
|
|
|
</a-button> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div v-if="record.isChecker"> |
|
|
|
<div v-if="record.morningStatus && !showMorningTime"> |
|
|
|
<a-popconfirm |
|
|
|
ok-text="取消" |
|
|
|
cancel-text="修改" |
|
|
|
ok-type="" |
|
|
|
@confirm="cancel" |
|
|
|
:visible="morningVisible" |
|
|
|
@cancel="changeStatus(record.id, 0, 'morning')" |
|
|
|
> |
|
|
|
<a-icon slot="icon" type="" /> |
|
|
|
<a-button slot="title" size="small" @click="rejectStatus(record.id, 1, 'morning', record.morning)">驳回</a-button> |
|
|
|
<span |
|
|
|
class="font-bold" |
|
|
|
:class="record.morningStatus === 2 ? 'line-through red--text' : 'green--text'" |
|
|
|
@click="changeVisible(record.morningStatus, 'morningVisible')" |
|
|
|
> |
|
|
|
{{ $moment(record.morning - 0).format('HH:mm') }} |
|
|
|
</span> |
|
|
|
</a-popconfirm> |
|
|
|
</div> |
|
|
|
<div v-if="!record.morningStatus && !showMorningTime">未打卡</div> |
|
|
|
<!-- 修改时间 --> |
|
|
|
<a-time-picker |
|
|
|
placeholder="请选择" |
|
|
|
style="width: 100%" |
|
|
|
v-if="showMorningTime" |
|
|
|
format="HH:mm" |
|
|
|
class="px-2" |
|
|
|
@change="timeChange" |
|
|
|
@openChange="openChange($event, 'morning')" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<a-button |
|
|
|
v-else |
|
|
|
type="primary" |
|
|
|
size="small" |
|
|
|
@click="checkTime(listIndex, index, 0, record.id, record.memberId, record.checkerId)" |
|
|
|
> |
|
|
|
打卡 |
|
|
|
</a-button> |
|
|
|
</template> |
|
|
|
<!-- 晚 --> |
|
|
|
<template slot="night" slot-scope="text, record, index"> |
|
|
|
<div v-if="record.nightStatus"> |
|
|
|
{{ $moment(record.night - 0).format('HH:mm') }} |
|
|
|
<div v-if="!record.isMine && !record.isChecker"> |
|
|
|
<span v-if="record.nightStatus">{{ $moment(record.night - 0).format('HH:mm') }}</span> |
|
|
|
<span v-else>未打卡</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div v-else> |
|
|
|
<div v-if="record.isMine"> |
|
|
|
<span v-if="record.nightStatus" class="baseColor font-bold"> |
|
|
|
{{ $moment(record.night - 0).format('HH:mm') }} |
|
|
|
</span> |
|
|
|
<a-button |
|
|
|
v-else |
|
|
|
type="primary" |
|
|
|
size="small" |
|
|
|
@click="checkTime(listIndex, index, 1, record.id, record.memberId, record.checkerId)" |
|
|
|
> |
|
|
|
打卡 |
|
|
|
</a-button> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div v-if="record.isChecker"> |
|
|
|
<div v-if="record.nightStatus && !showNightTime"> |
|
|
|
<a-popconfirm |
|
|
|
ok-text="取消" |
|
|
|
cancel-text="修改" |
|
|
|
ok-type="" |
|
|
|
@confirm="cancel" |
|
|
|
:visible="visible" |
|
|
|
@cancel="changeStatus(record.id, 0, 'night')" |
|
|
|
> |
|
|
|
<a-icon slot="icon" type="" /> |
|
|
|
<a-button slot="title" size="small" @click="rejectStatus(record.id, 1, 'night', record.night)">驳回</a-button> |
|
|
|
<span |
|
|
|
class="font-bold" |
|
|
|
:class="record.nightStatus === 2 ? 'line-through red--text' : 'green--text'" |
|
|
|
@click="changeVisible(record.nightStatus, 'visible')" |
|
|
|
> |
|
|
|
{{ $moment(record.night - 0).format('HH:mm') }} |
|
|
|
</span> |
|
|
|
</a-popconfirm> |
|
|
|
</div> |
|
|
|
<div v-if="!record.nightStatus && !showNightTime">未打卡</div> |
|
|
|
<!-- 修改时间 --> |
|
|
|
<a-time-picker |
|
|
|
placeholder="请选择" |
|
|
|
style="width: 100%" |
|
|
|
v-if="showNightTime" |
|
|
|
format="HH:mm" |
|
|
|
class="px-2" |
|
|
|
@change="timeChange" |
|
|
|
@openChange="openChange($event, 'night')" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<a-button |
|
|
|
v-else |
|
|
|
type="primary" |
|
|
|
size="small" |
|
|
|
@click="checkTime(listIndex, index, 1, record.id, record.memberId, record.checkerId)" |
|
|
|
> |
|
|
|
打卡 |
|
|
|
</a-button> |
|
|
|
</template> |
|
|
|
<!-- 审核人 --> |
|
|
|
<template slot="checkerName" slot-scope="text, record"> |
|
|
|
<a-select :default-value="record.checkerId || checkerId" style="width: 80px" placeholder="选择" @change="chooseChecker"> |
|
|
|
<a-select-option :value="member.memberId" v-for="member in members" :key="member.memberId"> |
|
|
|
{{ member.name }} |
|
|
|
</a-select-option> |
|
|
|
</a-select> |
|
|
|
<div class="px-2"> |
|
|
|
<div v-if="!record.isMine || (record.isMine && record.morningStatus && record.nightStatus)"> |
|
|
|
{{ record.checkerName || members[0].name }} |
|
|
|
</div> |
|
|
|
<a-select v-else :default-value="record.checkerId || members[0].memberId" style="width: 100%" @change="chooseChecker"> |
|
|
|
<a-select-option :value="member.memberId" v-for="member in members" :key="member.memberId"> |
|
|
|
{{ member.name }} |
|
|
|
</a-select-option> |
|
|
|
</a-select> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</a-table> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<a-empty class="mt-8 mb-8" description="暂无打卡信息" v-else /> |
|
|
|
<a-drawer |
|
|
|
placement="bottom" |
|
|
|
:closable="false" |
|
|
|
:mask-closable="false" |
|
|
|
:visible="visible" |
|
|
|
:after-visible-change="afterVisibleChange" |
|
|
|
class="actions-box" |
|
|
|
> |
|
|
|
<p class="mb-1">修改</p> |
|
|
|
<p class="mb-4">驳回</p> |
|
|
|
<p class="mb-0" @click="onClose">取消</p> |
|
|
|
</a-drawer> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { mapState } from 'vuex'; |
|
|
|
import { clockQuery, clockPunch } from '@/config/api'; |
|
|
|
import { clockQuery, clockPunch, clockAudit } from '@/config/api'; |
|
|
|
|
|
|
|
const columns = [ |
|
|
|
{ title: ' ', dataIndex: 'actions', key: 'actions', scopedSlots: { customRender: 'actions' }, align: 'center', width: '10%' }, |
|
|
|
{ title: '姓名', dataIndex: 'memberName', key: 'memberName', align: 'center', width: '22.5%' }, |
|
|
|
{ title: '早', dataIndex: 'morning', key: 'morning', scopedSlots: { customRender: 'morning' }, align: 'center', width: '22.5%' }, |
|
|
|
{ title: '晚', dataIndex: 'night', key: 'night', scopedSlots: { customRender: 'night' }, align: 'center', width: '22.5%' }, |
|
|
|
{ title: '姓名', dataIndex: 'memberName', key: 'memberName', align: 'center', width: '16%' }, |
|
|
|
{ title: '早', dataIndex: 'morning', key: 'morning', scopedSlots: { customRender: 'morning' }, align: 'center', width: '27%' }, |
|
|
|
{ title: '晚', dataIndex: 'night', key: 'night', scopedSlots: { customRender: 'night' }, align: 'center', width: '27%' }, |
|
|
|
{ |
|
|
|
title: '审核人', |
|
|
|
dataIndex: 'checkerName', |
|
|
|
key: 'checkerName', |
|
|
|
scopedSlots: { customRender: 'checkerName' }, |
|
|
|
align: 'center', |
|
|
|
width: '22.5%', |
|
|
|
width: '30%', |
|
|
|
}, |
|
|
|
]; |
|
|
|
|
|
|
@ -93,15 +170,26 @@ export default { |
|
|
|
memberIdList: [], |
|
|
|
options: null, |
|
|
|
checkerId: undefined, |
|
|
|
morningVisible: false, |
|
|
|
visible: false, |
|
|
|
placement: 'left', |
|
|
|
timer: null, |
|
|
|
showNightTime: false, |
|
|
|
showMorningTime: false, |
|
|
|
chooseTime: '', |
|
|
|
auditOptions: null, |
|
|
|
}; |
|
|
|
}, |
|
|
|
|
|
|
|
computed: mapState('home', ['projectId', 'members']), |
|
|
|
|
|
|
|
mounted() { |
|
|
|
this.setParams(); |
|
|
|
this.timer = setInterval(() => { |
|
|
|
if (this.projectId) { |
|
|
|
clearInterval(this.timer); |
|
|
|
this.setParams(); |
|
|
|
} |
|
|
|
}, 300); |
|
|
|
}, |
|
|
|
|
|
|
|
methods: { |
|
|
@ -152,7 +240,7 @@ export default { |
|
|
|
this.clockInfos[listIndex].recordList[index].night = selectTime; |
|
|
|
} |
|
|
|
const dateTime = this.$moment(time).format('x') - 0; |
|
|
|
const params = { param: { checkerId: this.checkerId || checkerId, clockType, dateTime, id, memberId } }; |
|
|
|
const params = { param: { checkerId: this.checkerId || checkerId || this.members[0].memberId, clockType, dateTime, id, memberId } }; |
|
|
|
this.handleClockPunch(params); |
|
|
|
}, |
|
|
|
|
|
|
@ -172,25 +260,90 @@ export default { |
|
|
|
this.$message.success('打卡成功'); |
|
|
|
this.setParams(); |
|
|
|
} else { |
|
|
|
this.$message.error(msg || '获取失败'); |
|
|
|
this.$message.error(msg || '打卡失败'); |
|
|
|
throw msg; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
throw error || '获取失败'; |
|
|
|
throw error || '打卡失败'; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
showMenu(index) { |
|
|
|
console.log('展开菜单', index); |
|
|
|
this.visible = true; |
|
|
|
changeVisible(status, type) { |
|
|
|
if (status !== 2) { |
|
|
|
this[type] = true; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 修改 |
|
|
|
changeStatus(id, type, timeType) { |
|
|
|
if (timeType === 'morning') { |
|
|
|
this.morningVisible = false; |
|
|
|
this.showMorningTime = true; |
|
|
|
} else { |
|
|
|
this.visible = false; |
|
|
|
this.showNightTime = true; |
|
|
|
} |
|
|
|
this.auditOptions = { id, type }; |
|
|
|
}, |
|
|
|
// 选择修改时间 |
|
|
|
timeChange(time) { |
|
|
|
this.chooseTime = this.$moment(time).format('x'); |
|
|
|
}, |
|
|
|
async openChange(open, timeType) { |
|
|
|
if (!open && this.chooseTime) { |
|
|
|
this.auditOptions[timeType] = this.chooseTime; |
|
|
|
this.auditOptions.projectId = this.projectId; |
|
|
|
const params = { param: this.auditOptions }; |
|
|
|
await this.handleClockAudit(params); |
|
|
|
} |
|
|
|
if (!open && !this.chooseTime) { |
|
|
|
this.showNightTime = false; |
|
|
|
this.showMorningTime = false; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
afterVisibleChange(val) { |
|
|
|
console.log('visible', val); |
|
|
|
// 驳回 |
|
|
|
async rejectStatus(id, type, timeType, time) { |
|
|
|
if (timeType === 'morning') { |
|
|
|
this.morningVisible = false; |
|
|
|
} else { |
|
|
|
this.visible = false; |
|
|
|
} |
|
|
|
const params = { param: { id, type, [timeType]: time, projectId: this.projectId } }; |
|
|
|
await this.handleClockAudit(params); |
|
|
|
}, |
|
|
|
|
|
|
|
onClose() { |
|
|
|
// 取消 |
|
|
|
cancel() { |
|
|
|
this.visible = false; |
|
|
|
this.morningVisible = false; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 审核打卡 |
|
|
|
* @param {string} id 打卡记录id |
|
|
|
* @param {string} morning 早打卡时间 |
|
|
|
* @param {string} night 晚打卡时间 |
|
|
|
* @param {string} projectId 项目id |
|
|
|
* @param {string} type 审批类型(0-修改,1-驳回) |
|
|
|
*/ |
|
|
|
async handleClockAudit(params) { |
|
|
|
try { |
|
|
|
const res = await clockAudit(params); |
|
|
|
const { code, msg } = res.data; |
|
|
|
if (code === 200) { |
|
|
|
this.$message.success(params.param.type === 0 ? '修改成功' : '驳回成功'); |
|
|
|
this.showNightTime = false; |
|
|
|
this.showMorningTime = false; |
|
|
|
this.auditOptions = null; |
|
|
|
this.setParams(); |
|
|
|
} else { |
|
|
|
this.$message.error(msg || '审核失败'); |
|
|
|
throw msg; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
throw error || '审核失败'; |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
}; |
|
|
@ -200,19 +353,19 @@ export default { |
|
|
|
.table-head { |
|
|
|
border-bottom: 1px solid #e8e8e8; |
|
|
|
background: #fafafa; |
|
|
|
height: 40px; |
|
|
|
line-height: 40px; |
|
|
|
height: 36px; |
|
|
|
line-height: 36px; |
|
|
|
font-weight: bold; |
|
|
|
} |
|
|
|
|
|
|
|
.table-time { |
|
|
|
height: 40px; |
|
|
|
line-height: 40px; |
|
|
|
height: 36px; |
|
|
|
line-height: 36px; |
|
|
|
border-bottom: 1px solid #e8e8e8; |
|
|
|
} |
|
|
|
|
|
|
|
.table-head-item { |
|
|
|
width: 22.5%; |
|
|
|
width: 27%; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
@ -222,7 +375,11 @@ img { |
|
|
|
} |
|
|
|
|
|
|
|
.teble-box >>> .ant-table-row-cell-break-word { |
|
|
|
padding: 8px 0 !important; |
|
|
|
padding: 4px 0 !important; |
|
|
|
} |
|
|
|
|
|
|
|
.teble-box >>> .ant-table-row { |
|
|
|
height: 41px !important; |
|
|
|
} |
|
|
|
|
|
|
|
.actions-box >>> .ant-drawer-content-wrapper { |
|
|
@ -238,9 +395,21 @@ img { |
|
|
|
} |
|
|
|
|
|
|
|
.actions-box >>> .ant-drawer-body p { |
|
|
|
height: 40px; |
|
|
|
line-height: 40px; |
|
|
|
height: 36px; |
|
|
|
line-height: 36px; |
|
|
|
background: #fcfcfc; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
</style> |
|
|
|
|
|
|
|
<style> |
|
|
|
.ant-popover-inner { |
|
|
|
width: 200px; |
|
|
|
} |
|
|
|
|
|
|
|
.ant-popover-message { |
|
|
|
position: absolute !important; |
|
|
|
left: 2px; |
|
|
|
top: 8px; |
|
|
|
} |
|
|
|
</style> |
|
|
|