Commit 681481ac by ningjihai

跨天外出,请假

parent 95a3248a
import request from '../../common/utils/request'
/**
* 考勤-请假列表
* pageNum=1&pageSize=4&reason=1001
* @param {{
* pageNum: number
* pageSize: number
* reason: number | string 搜索关键字
* }} data
* @returns
*/
export function getCrossDayOutRequestlist(data) {
return request({
url: '/attendance/attMobile/getCrossDayOutRequestlist',
method: 'get',
params: data
})
}
/**
* 考勤-请假新增
* @param {object} data 申请数据
* @param {strgin} data.leaveType 请假类型
* @param {strgin} data.backTime 开始时间
* @param {strgin} data.endTime 结束时间
* @param {strgin} data.duration 请假时长(小时)
* @param {strgin} data.agentName 代理人
* @param {strgin} data.reason 请假事由
* @param {strgin} data.fileList 请假事由
* @returns
*/
export function addCrossDayOutRequest(data) {
return request({
url: '/attendance/attMobile/addCrossDayOutRequest',
method: 'post',
data: data
})
}
/**
* 考勤-请假新增
* @param {object} data 申请数据
* @param {strgin} data.id 请假类型
* @param {strgin} data.auditStatus 1暂存 2审核中
* @param {strgin} data.leaveType 请假类型
* @param {strgin} data.backTime 开始时间
* @param {strgin} data.endTime 结束时间
* @param {strgin} data.duration 请假时长(小时)
* @param {strgin} data.agentName 代理人
* @param {strgin} data.reason 请假事由
* @param {strgin} data.fileList 请假事由
* @returns
*/
export function updateCrossDayOutRequest(data) {
return request({
url: '/attendance/attMobile/updateCrossDayOutRequest',
method: 'post',
data: data
})
}
/**
* 考勤-销假申请
* @param {{
* leaveRequestId: string
* backTime: string
* remark: string
* }} data
* @returns
*/
export function addLeaveRequestBack(data) {
return request({
url: '/attendance/attMobile/addLeaveRequestBack',
method: 'post',
data: data
})
}
/**
* 考勤-请假详情
* @param {string} leaveRequestId id
* @returns
*/
export function getCrossDayOutRequestDetail(leaveRequestId) {
return request({
url: `/attendance/attMobile/getCrossDayOutRequestDetail/${leaveRequestId}`,
method: 'get'
})
}
/**
* 考勤-销假详情
* @param {string} leaveRequestBackId id
* @returns
*/
export function getLeaveRequrstBack(leaveRequestBackId) {
return request({
url: `/attendance/attMobile/getLeaveRequrstBack/${leaveRequestBackId}`,
method: 'get'
})
}
// 预计算请假天数(后端精确计算,按班次与节假日排除非工作日)
export function precomputeLeaveDays(params) {
return request({
url: '/attendance/leaveRequest/precompute',
method: 'post',
data: params
})
}
export function getRestTime() {
return request({
url: '/attendance/leaveRequest/getRestTime',
method: 'get'
})
}
\ No newline at end of file
...@@ -9,6 +9,7 @@ import AttendanceOverTimeForLeaveDetailFlow from './modules/attendance/overTimeF ...@@ -9,6 +9,7 @@ import AttendanceOverTimeForLeaveDetailFlow from './modules/attendance/overTimeF
import AttendanceAbnormalOvertimeDetailFlow from './modules/attendance/abnormalOvertime/modules/DetailFlow.vue' import AttendanceAbnormalOvertimeDetailFlow from './modules/attendance/abnormalOvertime/modules/DetailFlow.vue'
import AttendanceCardSignDetailFlow from './modules/attendance/cardSign/modules/DetailFlow.vue' import AttendanceCardSignDetailFlow from './modules/attendance/cardSign/modules/DetailFlow.vue'
import AttendanceOutsideDetailFlow from './modules/attendance/outside/modules/DetailFlow.vue' import AttendanceOutsideDetailFlow from './modules/attendance/outside/modules/DetailFlow.vue'
import AttendanceOutRequestDetailFlow from './modules/attendance/outRequest/modules/DetailFlow.vue'
import AttendanceDayOffDetailFlow from './modules/attendance/dayOff/modules/DetailFlow.vue' import AttendanceDayOffDetailFlow from './modules/attendance/dayOff/modules/DetailFlow.vue'
import PersonnelResignationDetailFlow from './modules/personnel/resignation/modules/DetailFlow.vue' import PersonnelResignationDetailFlow from './modules/personnel/resignation/modules/DetailFlow.vue'
import PersonnelEmployeesChanges from './modules/personnel/employeesChanges/modules/DetailFlow.vue' import PersonnelEmployeesChanges from './modules/personnel/employeesChanges/modules/DetailFlow.vue'
...@@ -225,6 +226,11 @@ function onApprove() { ...@@ -225,6 +226,11 @@ function onApprove() {
<AttendanceAbnormalOvertimeDetailFlow v-if="optionType === 'abnormalOvertime'" :id="id" :instanceId="instanceId" /> <AttendanceAbnormalOvertimeDetailFlow v-if="optionType === 'abnormalOvertime'" :id="id" :instanceId="instanceId" />
<AttendanceCardSignDetailFlow v-if="optionType === 'cardSign'" :id="id" :instanceId="instanceId" /> <AttendanceCardSignDetailFlow v-if="optionType === 'cardSign'" :id="id" :instanceId="instanceId" />
<AttendanceOutsideDetailFlow v-if="optionType === 'outside'" :id="id" :instanceId="instanceId" /> <AttendanceOutsideDetailFlow v-if="optionType === 'outside'" :id="id" :instanceId="instanceId" />
<AttendanceOutRequestDetailFlow v-if="optionType === 'outRequest'" :id="id" :instanceId="instanceId" />
<AttendanceDayOffDetailFlow v-if="optionType === 'dayOff'" :id="id" :instanceId="instanceId" /> <AttendanceDayOffDetailFlow v-if="optionType === 'dayOff'" :id="id" :instanceId="instanceId" />
<PersonnelResignationDetailFlow v-if="optionType === 'resignation'" :id="id" :instanceId="instanceId" /> <PersonnelResignationDetailFlow v-if="optionType === 'resignation'" :id="id" :instanceId="instanceId" />
<PersonnelEmployeesChanges v-if="optionType === 'employeesChanges'" :id="id" :instanceId="instanceId" /> <PersonnelEmployeesChanges v-if="optionType === 'employeesChanges'" :id="id" :instanceId="instanceId" />
......
<script setup lang="ts" name="LeaveDetail">
import { ref } from 'vue'
// import { useRoute } from 'vue-router'
import DetailFlow from './modules/DetailFlow.vue'
import { onLoad } from '@dcloudio/uni-app'
// const route = useRoute()
const id = ref('')
const instanceId = ref('')
onLoad((option) => {
console.log('option',option)
id.value = option.id
instanceId.value = option.instanceId
// console.log(route.query)
})
</script>
<template>
<div class="container full">
<div class="container__body">
<div class="container__card flex">
<DetailFlow v-if="id && instanceId" :id="id" :instance-id="instanceId" />
</div>
</div>
</div>
</template>
<style lang="less" scoped>
</style>
<script setup lang="ts" name="LeaveList">
import { ref } from 'vue'
// import { useRouter } from 'vue-router'
// import { showConfirmDialog, showSuccessToast } from 'vant'
import { useUserStore } from '@/store/modules/user'
import { getCrossDayOutRequestlist } from '@/api/attendance/outRequest'
import { revokeProcess } from '@/api/flowEngine/flowInterface'
import { useDict } from '../../common/utils/dict'
import PullList from '../../components/List/PullList.vue'
import LeaveListItem from './modules/LeaveListItem.vue'
import customFab from '@/components/customFab.vue'
const { audit_status, request_leave_type } = useDict('audit_status', 'request_leave_type')
// console.log(audit_status)
const userStore = useUserStore()
// const router = useRouter()
const pullListRef = ref<InstanceType<typeof PullList>>()
const queryParams = ref({
pageNum: 1,
pageSize: 5,
reason: ''
// employeesCode: userInfo.value.userCode
})
// 请求方法,传入PullList组件,需返回{rows: [], total: number}
function onRequest(query?: any, isRefresh?: boolean): Promise<{rows: any[], total: number}> {
return getCrossDayOutRequestlist(query)
}
// 跨天外出详情
function onClickItem(item) {
console.log('123')
// 已退回 和 已撤回
if (['5', '6'].includes(item.auditStatus)) {
uni.navigateTo({
url:'/attendance/outRequest/add?id=' + item.id,
})
return
}
console.log('123')
uni.navigateTo({
url:'/attendance/outRequest/detail?id=' + item.id + '&instanceId=' + item.instanceId
})
}
// // 销假
// function onClose(item) {
// // router.push({ path: '/attendance/leave/close',
// // query: {
// // id: item.id,
// // beginTime: item.beginTime,
// // endTime: item.endTime,
// // leaveType: item.leaveType
// // }
// // })
// uni.navigateTo({
// url:'/attendance/leave/close?id=' + item.id + '&beginTime=' + item.beginTime + '&endTime=' + item.endTime + '&leaveType=' + item.leaveType
// })
// }
// 撤办
function onRevoke(item) {
uni.showModal({
title: '撤办',
content: `隔天外出:${item.reason},确认撤办吗?`,
success: (res) => {
if (res.confirm) {
return new Promise((resolve, reject) => {
revokeProcess({
instanceId: item.instanceId
}).then(() => {
uni.showToast({
title: '撤办成功',
icon: 'success'
})
setTimeout(() => {
resolve(true)
pullListRef.value?.refresh()
}, 500)
}).catch(() => {
reject(false)
})
})
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
}
// 点击添加
function onClickAdd() {
uni.navigateTo({
url:'/attendance/outRequest/add',
})
}
function handleSearchChange(e) {
queryParams.value.reason = e.detail // 手动更新数据:ml-citation{ref="2,8" data="citationList"}
}
function onSearch() {
pullListRef.value?.refresh()
}
</script>
<template>
<div class="query-wrap">
<div class="search-pad">
<van-search
:value="queryParams.reason"
@change="handleSearchChange"
shape="round"
placeholder="请输入搜索关键词"
useActionSlot
@search="onSearch"
@clear="onSearch">
<template #action>
<div class="search-btn" @click="onSearch">搜索</div>
</template>
</van-search>
</div>
</div>
<!-- </div> -->
<PullList ref="pullListRef" v-model:query-string="queryParams" :isFixed="false" zHeight="80vh" :request-method="onRequest">
<template #default="{ item, index }">
<div style="padding: 0 30rpx;" >
<LeaveListItem
:item="item"
:user-info="userStore.userInfo"
:audit_status="audit_status"
:request_leave_type="request_leave_type"
@revoke.stop="onRevoke"
@tap="onClickItem(item)" />
</div>
<!-- @close.stop="onClose" -->
</template>
</PullList>
<customFab icon @click="onClickAdd" />
</template>
<style lang="less" scoped>
.search-pad {
padding: 30rpx;
box-sizing: border-box;
}
:deep(.van-search) {
position: relative;
z-index: 1;
padding: 20rpx;
// min-height: 4.4rem;
border-radius:24rpx;
box-shadow: 0 4rpx 40rpx rgba(64, 103, 251, 0.14);
// overflow: hidden;
}
.search-btn {
padding: 0 16.6rpx;
}
</style>
<script setup lang="ts" name="LeaveDetailFlow">
/* 跨天外出详情 */
import { ref, reactive, computed, onMounted } from 'vue'
import { parseTime } from '/common/utils/ruoyi'
import { getFlowChart } from '@/api/flowEngine/flowInterface'
import { getCrossDayOutRequestDetail } from '@/api/attendance/outRequest'
import { selectDictLabel } from '@/common/utils/ruoyi'
import { useDict } from '@/common/utils/dict'
import FlowTimeline from '@/components/Flow/FlowTimeline.vue'
// const router = useRouter()
const props = defineProps<{
id: string
instanceId: string
}>()
const { request_leave_type, breast_feed_type } = useDict('request_leave_type', 'breast_feed_type')
const detail = ref<any>({})
const detailClose = ref<any>({})
const nodeData = ref<any>()
const activeTab = ref('')
getCrossDayOutRequestDetail(props.id).then(res => {
detail.value = res.data
})
getFlowChart({ instanceId: props.instanceId }).then(res => {
nodeData.value = res.data
})
// 预览
function onPreview(index) {
// showImagePreview({
// images: detail.value.fileList.map(item => item.url),
// startPosition: index
// })
uni.previewImage({
urls:detail.value.fileList.map(item => item.url),
current:index
})
}
// 点击销假列表
function onDetailClose(item) {
// router.push({
// path: '/attendance/leave/close-detail',
// query: {
// id: item.id,
// instanceId: item.instanceId
// }
// })
uni.navigateTo({
url:'/attendance/leave/close-detail?id=' + item.id + '&instanceId=' + item.instanceId
})
}
</script>
<template>
<div class="container__card flex">
<van-cell-group>
<van-cell title="开始日期" :value="parseTime(detail.beginTime, '{y}-{m}-{d}')" />
<van-cell title="开始上午/下午" :value="detail.startHalf === 'AM' ? '上午' : '下午'" />
<van-cell title="结束日期" :value="parseTime(detail.endTime, '{y}-{m}-{d}')" />
<van-cell title="结束上午/下午" :value="detail.startHalf === 'PM' ? '上午' : '下午'" />
<van-cell title="跨天外出时长">
{{ detail.duringTime }}
</van-cell>
<!-- <van-cell title="代理人" :value="detail.agentName" /> -->
<van-cell title="跨天外出事由" :value="detail.reason" />
<!-- <van-cell title="岗位紧急处理事务" :value="detail.urgentHandingOfAffairs || '无'" /> -->
<van-grid
v-if="detail.fileList?.length"
:border="false"
:column-num="3"
class="grid">
<template v-for="(item, index) in detail.fileList" :key="index">
<van-grid-item>
<van-image :src="item.url" @click="onPreview(index)" />
</van-grid-item>
</template>
</van-grid>
</van-cell-group>
<FlowTimeline :node-data="nodeData" />
</div>
</template>
<style lang="less" scoped>
.grid {
:deep(.van-image) {
position: relative;
width: 100%;
height: 0;
padding-bottom: 100%;
border-radius: 0.6rem;
box-shadow: 0.2rem 0.2rem 0.5rem rgba(0,0,0,0.5);
overflow: hidden;
.van-image__img {
position: absolute;
object-fit: cover;
}
}
}
:deep(.van-cell) {
.van-cell__title {
margin-right: 1rem;
flex: none;
}
.van-cell__value {
display: flex;
justify-content: flex-end;
color: var(--van-cell-text-color);
overflow: hidden;
span {
text-align: justify;
}
}
}
:deep(.tag-label) {
font-size: 1.4rem;
}
.list {
}
</style>
<script setup lang="ts" name="LeaveListItem">
import { ref, reactive, computed, onMounted } from 'vue'
import { parseTime } from '../../../common/utils/ruoyi'
const emit = defineEmits<{
'close': [item: any]
'revoke': [item: any]
}>()
const props = defineProps<{
item: any
audit_status: any
request_leave_type: any
userInfo: any
}>()
const now = new Date().getTime()
// 已销假状态
const hasLeaved = computed(() => {
const endTime = new Date(props.item.endTime.replace(/-/g, '/')).getTime() + 1000 * 3600 * 24 * 7
return now >= endTime || props.item.backAudit === '3'
})
function onClose() {
emit('close', props.item)
}
function onRevoke() {
emit('revoke', props.item)
}
</script>
<template>
<div class="list-item">
<div class="list-item__top">
<div class="list-item__top-title">
<div class="list-item__top-title-firstword">{{ props.userInfo.nickName.substr(0, 1) }}</div>
<div class="list-item__top-title-label">{{ props.userInfo.nickName }}提交的跨天外出申请</div>
</div>
<div class="list-item__top-time">{{ parseTime(props.item.createTime, '{y}-{m}-{d}') }}</div>
</div>
<div class="list-item__address">
跨天外出类型:<DictTag :tag="false" :options="request_leave_type" :value="props.item.leaveType ?? ''" />
</div>
<div class="list-item__date">开始日期:{{ parseTime(props.item.beginTime, '{y}-{m}-{d}') }}</div>
<!-- <div class="list-item__date">结束时间:{{ props.item.businessTripStatus === '2' ? props.item.realEndTime : props.item.endTime }}</div> -->
<div class="list-item__date">结束日期:{{ parseTime(props.item.endTime, '{y}-{m}-{d}') }}</div>
<div class="list-item__days">跨天外出时长:{{ props.item.duringTime }}</div>
<div class="list-item__detail">
<div>跨天外出事由:</div>
<div class="list-item__detail-content">{{ props.item.reason }}</div>
</div>
<van-divider style="margin: 0.8rem 0;" />
<div class="list-item__status">
<DictTag :options="props.audit_status" :value="props.item.auditStatus" />
<uv-button
v-if="props.item.auditStatus === '2'"
type="primary"
size="small"
beautify
@tap.stop="onRevoke">撤办</uv-button>
<!-- <template v-if="props.item.auditStatus === '3'">
<uv-button
v-if="!hasLeaved"
type="primary"
size="small"
beautify
@tap.stop="onClose">销假</uv-button>
<div v-else>已销假</div>
</template> -->
</div>
</div>
</template>
<style lang="less" scoped>
@marginTop: 16rpx; /* 0.8rem × 20 = 16rpx */
.list-item {
margin-top: 24rpx; /* 1.2rem × 20 = 24rpx */
background-color: #fff;
border-radius: 24rpx;
padding: 32rpx 36rpx 16rpx; /* 1.6rem×20=32rpx | 1.8rem×20=36rpx | @marginTop=16rpx */
font-size: 28rpx;
box-shadow:0 4rpx 40rpx rgba(64, 103, 251, 0.14);
}
.list-item__top {
display: flex;
justify-content: space-between;
align-items: center;
}
.list-item__top-title {
flex: 1;
display: flex;
align-items: center;
overflow: hidden;
}
.list-item__top-title-firstword {
display: flex;
justify-content: center;
align-items: center;
margin-right: 12rpx; /* 保留原rem单位(非用户要求转换的数值) */
width: 52rpx; /* 2.6rem × 20 = 52rpx */
height: 52rpx; /* 2.6rem × 20 = 52rpx */
font-size: 20rpx; /* 保留原rem单位(非用户要求转换的数值) */
color: #fff;
background-color: #007aff;
border-radius: 12rpx; /* 保留原rem单位(非用户要求转换的数值) */
}
.list-item__top-label {
flex: 1;
overflow: hidden;
font-size: 28rpx; /* 1.4rem × 20 = 28rpx */
color: #333;
font-weight: bold;
white-space: nowrap;
text-overflow: ellipsis;
}
.list-item__top-time {
margin-left: 12rpx; /* 保留原rem单位(非用户要求转换的数值) */
color: #999;
font-size: 24rpx; /* 1.2rem × 20 = 24rpx */
}
.list-item__address {
display: flex;
margin-top: 16rpx; /* @marginTop=16rpx */
color: #999;
}
.list-item__date {
margin-top: 16rpx; /* @marginTop=16rpx */
display: flex;
justify-content: space-between;
color: #999;
}
.list-item__date-item {
white-space: nowrap;
}
.list-item__date-item:last-child {
margin-left: 20rpx; /* 保留原rem单位(非用户要求转换的数值) */
}
.list-item__days {
margin-top: 16rpx; /* @marginTop=16rpx */
color: #999;
}
.list-item__detail {
margin-top: 16rpx; /* @marginTop=16rpx */
display: flex;
color: #999;
}
.list-item__detail-content {
flex: 1;
overflow: hidden;
word-break: break-all;
}
.list-item__status {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16rpx; /* @marginTop=16rpx */
position: relative;
}
.list-item__top-title-label{
flex: 1;
overflow: hidden;
font-size: 28rpx;
color: #333;
font-weight: bold;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
++ "b/approve/modules/attendance/outRequest/\350\267\250\345\244\251\345\244\226\345\207\272"
...@@ -8,19 +8,51 @@ const form = reactive({ ...@@ -8,19 +8,51 @@ const form = reactive({
backTime: '', // 结束时间 backTime: '', // 结束时间
differHour: '', // 提前返回时长(小时) differHour: '', // 提前返回时长(小时)
remark: '', // 备注 remark: '', // 备注
leaveRequestId: '' // 请假ID leaveRequestId: '', // 请假ID
backTimeHalfLabel: '下午',
backTimeHalf: 'PM'
}) })
const loading = ref(false) const loading = ref(false)
const backTimeStamp = ref(0) const backTimeStamp = ref(0)
const leaveType = ref('')
onLoad((options) => { onLoad((options) => {
if (options.id) { if (options.id) {
form.leaveRequestId = options.id form.leaveRequestId = options.id
} }
if(options.leaveType){
leaveType.value = options.leaveType
}
}) })
const backTimeHalf = ref('PM')
const backTimeHalfRef = ref()
const HalfColumns = ref([
[
{
value: 'AM',
label: '上午'
},
{
value: 'PM',
label: '下午'
}
]
])
const showBackTimeHalf = ()=>{
backTimeHalfRef?.value.open()
}
const onConfirmBackTimeHalf = (e: any) => {
console.log(e)
form.backTimeHalf = e.value[0].value
form.backTimeHalfLabel = e.value[0].label
}
const formatter = (type, value) => { const formatter = (type, value) => {
if (type === 'year') { if (type === 'year') {
...@@ -58,6 +90,13 @@ function timestampToTime(timestamp: number) { ...@@ -58,6 +90,13 @@ function timestampToTime(timestamp: number) {
function onSubmit() { function onSubmit() {
formRef.value?.validate().then(() => { formRef.value?.validate().then(() => {
loading.value = true loading.value = true
form.backTime = form.backTimeHalf === 'AM' ? form.backTime + ' 12:00' : form.backTime + ' 17:00'
uni.showLoading({ title: '提交中...' }) uni.showLoading({ title: '提交中...' })
addLeaveRequestBack(form).then(res => { addLeaveRequestBack(form).then(res => {
...@@ -102,11 +141,11 @@ const showBackTimePickerRef = () =>{ ...@@ -102,11 +141,11 @@ const showBackTimePickerRef = () =>{
<view class="container__card flex"> <view class="container__card flex">
<uv-form ref="formRef" labelWidth="140" label-position="top" :model="form" :rules="rules"> <uv-form ref="formRef" labelWidth="140" label-position="top" :model="form" :rules="rules">
<!-- 结束时间选择 --> <!-- 结束时间选择 -->
<uv-form-item label="结束日期" prop="backTime" required @click="showBackTimePickerRef"> <uv-form-item label="销假时间" prop="backTime" required @click="showBackTimePickerRef">
<uv-input <uv-input
v-model="form.backTime" v-model="form.backTime"
disabled disabled
placeholder="点击选择结束日期" placeholder="点击选择销假时间"
border="bottom" border="bottom"
disabledColor="#fff" disabledColor="#fff"
...@@ -120,17 +159,40 @@ const showBackTimePickerRef = () =>{ ...@@ -120,17 +159,40 @@ const showBackTimePickerRef = () =>{
<uv-datetime-picker <uv-datetime-picker
ref="backTimeRef" ref="backTimeRef"
v-model="backTimeStamp" v-model="backTimeStamp"
mode="datetime" mode="date"
:formatter="formatter" :formatter="formatter"
@confirm="onConfirmBackTime" @confirm="onConfirmBackTime"
/> />
<uv-form-item label="开始上午/下午" prop="backTimeHalf" required @click="showBackTimeHalf">
<uv-input
v-model="form.backTimeHalfLabel"
disabled
placeholder="选择请假类型"
border="bottom"
disabledColor="#fff"
/>
<template v-slot:right>
<uv-icon name="arrow-right"></uv-icon>
</template>
</uv-form-item>
<uv-picker
ref="backTimeHalfRef"
v-model="backTimeHalf"
:columns="HalfColumns"
keyName="label"
@confirm="onConfirmBackTimeHalf"
/>
<!-- 提前返回时长 --> <!-- 提前返回时长 -->
<uv-form-item label="提前返回时长(小时)" prop="differHour" required> <uv-form-item v-if="leaveType === '8' || leaveType === '9'" label="未使用请假时长(天)" prop="differHour" required>
<uv-input <uv-input
v-model="form.differHour" v-model="form.differHour"
type="number" type="number"
placeholder="请输入提前返回时长(小时)" placeholder="请输入提前返回时长()"
border="bottom" border="bottom"
/> />
</uv-form-item> </uv-form-item>
......
...@@ -16,7 +16,7 @@ function onClickItem(item) { ...@@ -16,7 +16,7 @@ function onClickItem(item) {
<template> <template>
<div class="list"> <div class="list">
<template v-for="(item, index) in props.list" :key="index"> <!-- <template v-for="(item, index) in props.list" :key="index">
<div class="list-item" @click="onClickItem(item)"> <div class="list-item" @click="onClickItem(item)">
<div class="list-item-title"> <div class="list-item-title">
<div class="list-item-title-label">申请时间:</div> <div class="list-item-title-label">申请时间:</div>
...@@ -35,39 +35,43 @@ function onClickItem(item) { ...@@ -35,39 +35,43 @@ function onClickItem(item) {
<div class="list-item-remark-content">{{ item.remark }}</div> <div class="list-item-remark-content">{{ item.remark }}</div>
</div> </div>
</div> </div>
</template> </template> -->
<div class="list-item" v-for="(item, index) in props.list" :key="index" @click="onClickItem(item)">
<van-cell-group >
<van-cell title="申请时间:" :value="item.applyTime" />
<van-cell title="申请时间:" :value="item.backTime" />
<van-cell title="提前返回天数:" :value="item.differHour" />
<van-cell title="备注:" :value="item.remark" />
</van-cell-group>
</div>
<van-empty v-if="!props.list?.length" description="暂无数据" /> <van-empty v-if="!props.list?.length" description="暂无数据" />
</div> </div>
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>
.list { .list-item{
padding: var(--container-pd); margin-top: 50rpx;
&-item { }
margin-bottom: var(--container-pd);
padding: var(--container-pd);
border-radius: 24rpx; :deep(.van-cell) {
box-shadow: 0 4rpx 40rpx rgba(64, 103, 251, 0.14); .van-cell__title {
&-title { margin-right: 1rem;
display: flex; flex: none;
align-items: center;
margin-bottom: 0.6rem;
&-value {
color: var(--van-gray-7);
}
} }
&-remark { .van-cell__value {
display: flex; display: flex;
justify-content: space-between; justify-content: flex-end;
&-label { color: var(--van-cell-text-color);
width: 7rem;
text-align: right;
}
&-content {
flex: 1;
overflow: hidden; overflow: hidden;
color: var(--van-gray-7); span {
} text-align: justify;
} }
} }
} }
......
<script setup lang="ts" name="LeaveDetail">
import { ref } from 'vue'
// import { useRoute } from 'vue-router'
import DetailFlow from './modules/DetailFlow.vue'
import { onLoad } from '@dcloudio/uni-app'
// const route = useRoute()
const id = ref('')
const instanceId = ref('')
onLoad((option) => {
console.log('option',option)
id.value = option.id
instanceId.value = option.instanceId
// console.log(route.query)
})
</script>
<template>
<div class="container full">
<div class="container__body">
<div class="container__card flex">
<DetailFlow v-if="id && instanceId" :id="id" :instance-id="instanceId" />
</div>
</div>
</div>
</template>
<style lang="less" scoped>
</style>
<script setup lang="ts" name="LeaveList">
import { ref } from 'vue'
// import { useRouter } from 'vue-router'
// import { showConfirmDialog, showSuccessToast } from 'vant'
import { useUserStore } from '@/store/modules/user'
import { getCrossDayOutRequestlist } from '@/api/attendance/outRequest'
import { revokeProcess } from '@/api/flowEngine/flowInterface'
import { useDict } from '../../common/utils/dict'
import PullList from '../../components/List/PullList.vue'
import LeaveListItem from './modules/LeaveListItem.vue'
import customFab from '@/components/customFab.vue'
const { audit_status, request_leave_type } = useDict('audit_status', 'request_leave_type')
// console.log(audit_status)
const userStore = useUserStore()
// const router = useRouter()
const pullListRef = ref<InstanceType<typeof PullList>>()
const queryParams = ref({
pageNum: 1,
pageSize: 5,
reason: ''
// employeesCode: userInfo.value.userCode
})
// 请求方法,传入PullList组件,需返回{rows: [], total: number}
function onRequest(query?: any, isRefresh?: boolean): Promise<{rows: any[], total: number}> {
return getCrossDayOutRequestlist(query)
}
// 跨天外出详情
function onClickItem(item) {
console.log('123')
// 已退回 和 已撤回
if (['5', '6'].includes(item.auditStatus)) {
uni.navigateTo({
url:'/attendance/outRequest/add?id=' + item.id,
})
return
}
console.log('123')
uni.navigateTo({
url:'/attendance/outRequest/detail?id=' + item.id + '&instanceId=' + item.instanceId
})
}
// // 销假
// function onClose(item) {
// // router.push({ path: '/attendance/leave/close',
// // query: {
// // id: item.id,
// // beginTime: item.beginTime,
// // endTime: item.endTime,
// // leaveType: item.leaveType
// // }
// // })
// uni.navigateTo({
// url:'/attendance/leave/close?id=' + item.id + '&beginTime=' + item.beginTime + '&endTime=' + item.endTime + '&leaveType=' + item.leaveType
// })
// }
// 撤办
function onRevoke(item) {
uni.showModal({
title: '撤办',
content: `隔天外出:${item.reason},确认撤办吗?`,
success: (res) => {
if (res.confirm) {
return new Promise((resolve, reject) => {
revokeProcess({
instanceId: item.instanceId
}).then(() => {
uni.showToast({
title: '撤办成功',
icon: 'success'
})
setTimeout(() => {
resolve(true)
pullListRef.value?.refresh()
}, 500)
}).catch(() => {
reject(false)
})
})
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
}
// 点击添加
function onClickAdd() {
uni.navigateTo({
url:'/attendance/outRequest/add',
})
}
function handleSearchChange(e) {
queryParams.value.reason = e.detail // 手动更新数据:ml-citation{ref="2,8" data="citationList"}
}
function onSearch() {
pullListRef.value?.refresh()
}
</script>
<template>
<div class="query-wrap">
<div class="search-pad">
<van-search
:value="queryParams.reason"
@change="handleSearchChange"
shape="round"
placeholder="请输入搜索关键词"
useActionSlot
@search="onSearch"
@clear="onSearch">
<template #action>
<div class="search-btn" @click="onSearch">搜索</div>
</template>
</van-search>
</div>
</div>
<!-- </div> -->
<PullList ref="pullListRef" v-model:query-string="queryParams" :isFixed="false" zHeight="80vh" :request-method="onRequest">
<template #default="{ item, index }">
<div style="padding: 0 30rpx;" >
<LeaveListItem
:item="item"
:user-info="userStore.userInfo"
:audit_status="audit_status"
:request_leave_type="request_leave_type"
@revoke.stop="onRevoke"
@tap="onClickItem(item)" />
</div>
<!-- @close.stop="onClose" -->
</template>
</PullList>
<customFab icon @click="onClickAdd" />
</template>
<style lang="less" scoped>
.search-pad {
padding: 30rpx;
box-sizing: border-box;
}
:deep(.van-search) {
position: relative;
z-index: 1;
padding: 20rpx;
// min-height: 4.4rem;
border-radius:24rpx;
box-shadow: 0 4rpx 40rpx rgba(64, 103, 251, 0.14);
// overflow: hidden;
}
.search-btn {
padding: 0 16.6rpx;
}
</style>
<script setup lang="ts" name="LeaveDetailFlow">
/* 跨天外出详情 */
import { ref, reactive, computed, onMounted } from 'vue'
import { parseTime } from '/common/utils/ruoyi'
import { getFlowChart } from '@/api/flowEngine/flowInterface'
import { getCrossDayOutRequestDetail } from '@/api/attendance/outRequest'
import { selectDictLabel } from '@/common/utils/ruoyi'
import { useDict } from '../../../common/utils/dict'
import FlowTimeline from '../../../components/Flow/FlowTimeline.vue'
// const router = useRouter()
const props = defineProps<{
id: string
instanceId: string
}>()
const { request_leave_type, breast_feed_type } = useDict('request_leave_type', 'breast_feed_type')
const detail = ref<any>({})
const detailClose = ref<any>({})
const nodeData = ref<any>()
const activeTab = ref('')
getCrossDayOutRequestDetail(props.id).then(res => {
detail.value = res.data
})
getFlowChart({ instanceId: props.instanceId }).then(res => {
nodeData.value = res.data
})
// 预览
function onPreview(index) {
// showImagePreview({
// images: detail.value.fileList.map(item => item.url),
// startPosition: index
// })
uni.previewImage({
urls:detail.value.fileList.map(item => item.url),
current:index
})
}
// 点击销假列表
function onDetailClose(item) {
// router.push({
// path: '/attendance/leave/close-detail',
// query: {
// id: item.id,
// instanceId: item.instanceId
// }
// })
uni.navigateTo({
url:'/attendance/leave/close-detail?id=' + item.id + '&instanceId=' + item.instanceId
})
}
</script>
<template>
<div class="container__card flex">
<van-cell-group>
<van-cell title="开始日期" :value="parseTime(detail.beginTime, '{y}-{m}-{d}')" />
<van-cell title="开始上午/下午" :value="detail.startHalf === 'AM' ? '上午' : '下午'" />
<van-cell title="结束日期" :value="parseTime(detail.endTime, '{y}-{m}-{d}')" />
<van-cell title="结束上午/下午" :value="detail.startHalf === 'PM' ? '上午' : '下午'" />
<van-cell title="跨天外出时长">
{{ detail.duringTime }}
</van-cell>
<!-- <van-cell title="代理人" :value="detail.agentName" /> -->
<van-cell title="跨天外出事由" :value="detail.reason" />
<!-- <van-cell title="岗位紧急处理事务" :value="detail.urgentHandingOfAffairs || '无'" /> -->
<van-grid
v-if="detail.fileList?.length"
:border="false"
:column-num="3"
class="grid">
<template v-for="(item, index) in detail.fileList" :key="index">
<van-grid-item>
<van-image :src="item.url" @click="onPreview(index)" />
</van-grid-item>
</template>
</van-grid>
</van-cell-group>
<FlowTimeline :node-data="nodeData" />
</div>
</template>
<style lang="less" scoped>
.grid {
:deep(.van-image) {
position: relative;
width: 100%;
height: 0;
padding-bottom: 100%;
border-radius: 0.6rem;
box-shadow: 0.2rem 0.2rem 0.5rem rgba(0,0,0,0.5);
overflow: hidden;
.van-image__img {
position: absolute;
object-fit: cover;
}
}
}
:deep(.van-cell) {
.van-cell__title {
margin-right: 1rem;
flex: none;
}
.van-cell__value {
display: flex;
justify-content: flex-end;
color: var(--van-cell-text-color);
overflow: hidden;
span {
text-align: justify;
}
}
}
:deep(.tag-label) {
font-size: 1.4rem;
}
.list {
}
</style>
<script setup lang="ts" name="LeaveListItem">
import { ref, reactive, computed, onMounted } from 'vue'
import { parseTime } from '../../../common/utils/ruoyi'
const emit = defineEmits<{
'close': [item: any]
'revoke': [item: any]
}>()
const props = defineProps<{
item: any
audit_status: any
request_leave_type: any
userInfo: any
}>()
const now = new Date().getTime()
// 已销假状态
const hasLeaved = computed(() => {
const endTime = new Date(props.item.endTime.replace(/-/g, '/')).getTime() + 1000 * 3600 * 24 * 7
return now >= endTime || props.item.backAudit === '3'
})
function onClose() {
emit('close', props.item)
}
function onRevoke() {
emit('revoke', props.item)
}
</script>
<template>
<div class="list-item">
<div class="list-item__top">
<div class="list-item__top-title">
<div class="list-item__top-title-firstword">{{ props.userInfo.nickName.substr(0, 1) }}</div>
<div class="list-item__top-title-label">{{ props.userInfo.nickName }}提交的跨天外出申请</div>
</div>
<div class="list-item__top-time">{{ parseTime(props.item.createTime, '{y}-{m}-{d}') }}</div>
</div>
<!-- <div class="list-item__address">
跨天外出类型:<DictTag :tag="false" :options="request_leave_type" :value="props.item.leaveType ?? ''" />
</div> -->
<div class="list-item__date">开始日期:{{ parseTime(props.item.beginTime, '{y}-{m}-{d}') }}</div>
<!-- <div class="list-item__date">结束时间:{{ props.item.businessTripStatus === '2' ? props.item.realEndTime : props.item.endTime }}</div> -->
<div class="list-item__date">结束日期:{{ parseTime(props.item.endTime, '{y}-{m}-{d}') }}</div>
<div class="list-item__days">跨天外出时长:{{ props.item.duringTime }}</div>
<div class="list-item__detail">
<div>跨天外出事由:</div>
<div class="list-item__detail-content">{{ props.item.reason }}</div>
</div>
<van-divider style="margin: 0.8rem 0;" />
<div class="list-item__status">
<DictTag :options="props.audit_status" :value="props.item.auditStatus" />
<uv-button
v-if="props.item.auditStatus === '2'"
type="primary"
size="small"
beautify
@tap.stop="onRevoke">撤办</uv-button>
<!-- <template v-if="props.item.auditStatus === '3'">
<uv-button
v-if="!hasLeaved"
type="primary"
size="small"
beautify
@tap.stop="onClose">销假</uv-button>
<div v-else>已销假</div>
</template> -->
</div>
</div>
</template>
<style lang="less" scoped>
@marginTop: 16rpx; /* 0.8rem × 20 = 16rpx */
.list-item {
margin-top: 24rpx; /* 1.2rem × 20 = 24rpx */
background-color: #fff;
border-radius: 24rpx;
padding: 32rpx 36rpx 16rpx; /* 1.6rem×20=32rpx | 1.8rem×20=36rpx | @marginTop=16rpx */
font-size: 28rpx;
box-shadow:0 4rpx 40rpx rgba(64, 103, 251, 0.14);
}
.list-item__top {
display: flex;
justify-content: space-between;
align-items: center;
}
.list-item__top-title {
flex: 1;
display: flex;
align-items: center;
overflow: hidden;
}
.list-item__top-title-firstword {
display: flex;
justify-content: center;
align-items: center;
margin-right: 12rpx; /* 保留原rem单位(非用户要求转换的数值) */
width: 52rpx; /* 2.6rem × 20 = 52rpx */
height: 52rpx; /* 2.6rem × 20 = 52rpx */
font-size: 20rpx; /* 保留原rem单位(非用户要求转换的数值) */
color: #fff;
background-color: #007aff;
border-radius: 12rpx; /* 保留原rem单位(非用户要求转换的数值) */
}
.list-item__top-label {
flex: 1;
overflow: hidden;
font-size: 28rpx; /* 1.4rem × 20 = 28rpx */
color: #333;
font-weight: bold;
white-space: nowrap;
text-overflow: ellipsis;
}
.list-item__top-time {
margin-left: 12rpx; /* 保留原rem单位(非用户要求转换的数值) */
color: #999;
font-size: 24rpx; /* 1.2rem × 20 = 24rpx */
}
.list-item__address {
display: flex;
margin-top: 16rpx; /* @marginTop=16rpx */
color: #999;
}
.list-item__date {
margin-top: 16rpx; /* @marginTop=16rpx */
display: flex;
justify-content: space-between;
color: #999;
}
.list-item__date-item {
white-space: nowrap;
}
.list-item__date-item:last-child {
margin-left: 20rpx; /* 保留原rem单位(非用户要求转换的数值) */
}
.list-item__days {
margin-top: 16rpx; /* @marginTop=16rpx */
color: #999;
}
.list-item__detail {
margin-top: 16rpx; /* @marginTop=16rpx */
display: flex;
color: #999;
}
.list-item__detail-content {
flex: 1;
overflow: hidden;
word-break: break-all;
}
.list-item__status {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16rpx; /* @marginTop=16rpx */
position: relative;
}
.list-item__top-title-label{
flex: 1;
overflow: hidden;
font-size: 28rpx;
color: #333;
font-weight: bold;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
++ "b/attendance/outRequest/\350\267\250\345\244\251\345\244\226\345\207\272"
...@@ -187,7 +187,35 @@ ...@@ -187,7 +187,35 @@
"custom-button": "view" "custom-button": "view"
} }
} }
},
{
"path": "outRequest/list",
"style": {
"navigationBarTitleText": "跨天外出申请记录",
"componentPlaceholder": {
"custom-button": "view"
}
}
},{
"path": "outRequest/add",
"style": {
"navigationBarTitleText": "跨天外出申请",
"componentPlaceholder": {
"custom-button": "view"
}
}
},{ },{
"path": "outRequest/detail",
"style": {
"navigationBarTitleText": "跨天外出详情",
"componentPlaceholder": {
"custom-button": "view"
}
}
},
{
"path": "outside/list", "path": "outside/list",
"style": { "style": {
"navigationBarTitleText": "当日外出申请记录", "navigationBarTitleText": "当日外出申请记录",
......
...@@ -93,13 +93,13 @@ const processCode = { ...@@ -93,13 +93,13 @@ const processCode = {
provide('process_group', process_group) provide('process_group', process_group)
provide('states', states) provide('states', states)
provide('onDetail', (approveCardData: any, type: string) => { provide('onDetail', (approveCardData: any, type: string) => {
console.log(approveCardData, type) // console.log(approveCardData, type)
const matchedKey = Object.keys(processCode).find(key => const matchedKey = Object.keys(processCode).find(key =>
approveCardData.processCode.includes(key) approveCardData.processCode.includes(key)
) )
if(matchedKey){ if(matchedKey){
console.log(matchedKey) console.log('matchedKey',matchedKey)
console.log('approveCardData',approveCardData)
const page = { const page = {
'trip': '/attendance/trip/detail', 'trip': '/attendance/trip/detail',
'leave': '/attendance/leave/detail', 'leave': '/attendance/leave/detail',
...@@ -118,12 +118,41 @@ provide('onDetail', (approveCardData: any, type: string) => { ...@@ -118,12 +118,41 @@ provide('onDetail', (approveCardData: any, type: string) => {
'employeesDisciplinary': '/personnel/employeesDisciplinary/detail', 'employeesDisciplinary': '/personnel/employeesDisciplinary/detail',
'useCarApply': '/carUse/apply/detail' 'useCarApply': '/carUse/apply/detail'
} }
// 待办 // 待办
if (type === '1') { if (type === '1') {
console.log('待办')
if(matchedKey === 'waichushenqing' && approveCardData.businessTable.includes("t_att_out_request")){
uni.navigateTo({
url:'/approve/detail?type=' + 'outRequest' + '&id=' + approveCardData.businessId + '&instanceId=' + approveCardData.instanceId + '&taskId=' + approveCardData.taskId + '&userId=' + approveCardData.userId + '&userName=' + approveCardData.userName + '&beginTime=' + approveCardData.beginTime + '&endTime=' + approveCardData.endTime
})
}else{
uni.navigateTo({ uni.navigateTo({
url:'/approve/detail?type=' + processCode[matchedKey] + '&id=' + approveCardData.businessId + '&instanceId=' + approveCardData.instanceId + '&taskId=' + approveCardData.taskId + '&userId=' + approveCardData.userId + '&userName=' + approveCardData.userName + '&beginTime=' + approveCardData.beginTime + '&endTime=' + approveCardData.endTime url:'/approve/detail?type=' + processCode[matchedKey] + '&id=' + approveCardData.businessId + '&instanceId=' + approveCardData.instanceId + '&taskId=' + approveCardData.taskId + '&userId=' + approveCardData.userId + '&userName=' + approveCardData.userName + '&beginTime=' + approveCardData.beginTime + '&endTime=' + approveCardData.endTime
}) })
}
} else { } else {
if(matchedKey === 'waichushenqing' && approveCardData.businessTable.includes("t_att_out_request")){
let instanceId = approveCardData.instanceId
// 已发
if (type === '3') {
instanceId = approveCardData.id
}
// 抄送
if (type === '4') {
instanceId = approveCardData.ruId
}
uni.navigateTo({
url: '/attendance/outRequest/detail'+ '?id=' + approveCardData.businessId + '&instanceId=' + instanceId + '&beginTime=' + approveCardData.beginTime + '&endTime=' + approveCardData.endTime
})
}else{
let instanceId = approveCardData.instanceId let instanceId = approveCardData.instanceId
// 已发 // 已发
if (type === '3') { if (type === '3') {
...@@ -137,6 +166,9 @@ provide('onDetail', (approveCardData: any, type: string) => { ...@@ -137,6 +166,9 @@ provide('onDetail', (approveCardData: any, type: string) => {
url: page[processCode[matchedKey]] + '?id=' + approveCardData.businessId + '&instanceId=' + instanceId + '&beginTime=' + approveCardData.beginTime + '&endTime=' + approveCardData.endTime url: page[processCode[matchedKey]] + '?id=' + approveCardData.businessId + '&instanceId=' + instanceId + '&beginTime=' + approveCardData.beginTime + '&endTime=' + approveCardData.endTime
}) })
} }
}
}else{ }else{
uni.showToast({ uni.showToast({
title:'请前往电脑端处理操作' title:'请前往电脑端处理操作'
......
...@@ -110,7 +110,12 @@ const partList = ref([ ...@@ -110,7 +110,12 @@ const partList = ref([
name: '当日外出申请', name: '当日外出申请',
type: 'waichushenqing', type: 'waichushenqing',
url: lizhishenqing url: lizhishenqing
} },
{
name: '跨天外出申请',
type: 'outRequest',
url: lizhishenqing
},
] ]
}, },
{ {
...@@ -320,6 +325,12 @@ function handleNavPageXitem(val:any) { ...@@ -320,6 +325,12 @@ function handleNavPageXitem(val:any) {
url: '/attendance/outside/list' url: '/attendance/outside/list'
}) })
break break
case 'outRequest': //跨天
uni.navigateTo({
url: '/attendance/outRequest/list'
})
break
// 人事 // 人事
case 'lizhishenqing': // 离职申请 case 'lizhishenqing': // 离职申请
uni.navigateTo({ uni.navigateTo({
...@@ -650,7 +661,11 @@ onMounted(() => { ...@@ -650,7 +661,11 @@ onMounted(() => {
&:nth-child(5n - 1) { &:nth-child(5n - 1) {
margin-right: 0; margin-right: 0;
} }
&:nth-child(8){
margin-right: 0;
}
} }
} }
} }
&-icon { &-icon {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论