fix(工作报告): 修复工作报告存在的若干问题。
feat(加班申请): 支持批量审批。
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { fetchGetOvertimeApplicationDetail } from '@/service/api';
|
||||
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
|
||||
import { formatOvertimeDate, formatOvertimeDateTime } from './overtime-application-shared';
|
||||
import IconMdiCheckCircleOutline from '~icons/mdi/check-circle-outline';
|
||||
import IconMdiCloseCircleOutline from '~icons/mdi/close-circle-outline';
|
||||
import IconMdiChevronLeft from '~icons/mdi/chevron-left';
|
||||
import IconMdiChevronRight from '~icons/mdi/chevron-right';
|
||||
|
||||
defineOptions({ name: 'OvertimeApplicationBatchDetailDialog' });
|
||||
|
||||
interface Props {
|
||||
/** 选中的加班申请 id 列表(原始 id) */
|
||||
selectedIds: string[];
|
||||
/** 全部加班申请行数据,用于通过 id 查找 */
|
||||
rows: Api.OvertimeApplication.OvertimeApplication[];
|
||||
actionLoading?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
actionLoading: false
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
approve: [];
|
||||
reject: [];
|
||||
}>();
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const currentIndex = ref(0);
|
||||
const detailData = ref<Api.OvertimeApplication.OvertimeApplication | null>(null);
|
||||
const detailLoading = ref(false);
|
||||
|
||||
const currentId = computed(() => props.selectedIds[currentIndex.value] ?? null);
|
||||
|
||||
const total = computed(() => props.selectedIds.length);
|
||||
|
||||
const canGoPrev = computed(() => currentIndex.value > 0);
|
||||
const canGoNext = computed(() => currentIndex.value < props.selectedIds.length - 1);
|
||||
|
||||
async function loadDetail() {
|
||||
const id = currentId.value;
|
||||
if (!id) {
|
||||
detailData.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const row = props.rows.find(r => r.id === id);
|
||||
if (!row) {
|
||||
detailData.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
detailLoading.value = true;
|
||||
const { error, data } = await fetchGetOvertimeApplicationDetail(id);
|
||||
detailLoading.value = false;
|
||||
|
||||
detailData.value = error || !data ? row : data;
|
||||
}
|
||||
|
||||
function goPrev() {
|
||||
if (!canGoPrev.value) return;
|
||||
currentIndex.value -= 1;
|
||||
loadDetail();
|
||||
}
|
||||
|
||||
function goNext() {
|
||||
if (!canGoNext.value) return;
|
||||
currentIndex.value += 1;
|
||||
loadDetail();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => visible.value,
|
||||
value => {
|
||||
if (value) {
|
||||
currentIndex.value = 0;
|
||||
loadDetail();
|
||||
} else {
|
||||
detailData.value = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BusinessFormDialog v-model="visible" title="批量审批" preset="md" :loading="detailLoading" :show-footer="true">
|
||||
<!-- 左右导航 -->
|
||||
<div class="batch-detail__nav">
|
||||
<button type="button" class="batch-detail__nav-btn" :disabled="!canGoPrev" @click.stop="goPrev">
|
||||
<IconMdiChevronLeft class="text-20px" />
|
||||
</button>
|
||||
<span class="batch-detail__nav-counter">{{ currentIndex + 1 }} / {{ total }}</span>
|
||||
<button type="button" class="batch-detail__nav-btn" :disabled="!canGoNext" @click.stop="goNext">
|
||||
<IconMdiChevronRight class="text-20px" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ElDescriptions v-if="detailData" class="overtime-application-detail-dialog__descriptions" :column="2" border>
|
||||
<ElDescriptionsItem label="申请人" label-class-name="overtime-application-detail-dialog__label">
|
||||
{{ detailData.applicantName }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="加班日期" label-class-name="overtime-application-detail-dialog__label--compact">
|
||||
{{ formatOvertimeDate(detailData.overtimeDate) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="加班时长" label-class-name="overtime-application-detail-dialog__label">
|
||||
{{ detailData.overtimeDuration }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="提交时间" label-class-name="overtime-application-detail-dialog__label--compact">
|
||||
{{ formatOvertimeDateTime(detailData.submitTime) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="加班原因" :span="2" label-class-name="overtime-application-detail-dialog__label">
|
||||
{{ detailData.overtimeReason }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="加班内容" :span="2" label-class-name="overtime-application-detail-dialog__label">
|
||||
{{ detailData.overtimeContent }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
<ElEmpty v-else description="未获取到加班申请详情" />
|
||||
|
||||
<template #footer>
|
||||
<div class="batch-detail__footer">
|
||||
<span class="batch-detail__footer-hint">将对全部 {{ total }} 项统一执行操作</span>
|
||||
<div class="batch-detail__footer-actions">
|
||||
<ElButton
|
||||
class="batch-detail__approve-btn"
|
||||
type="success"
|
||||
:loading="props.actionLoading"
|
||||
:disabled="props.actionLoading || !detailData"
|
||||
@click="emit('approve')"
|
||||
>
|
||||
<template #icon>
|
||||
<IconMdiCheckCircleOutline />
|
||||
</template>
|
||||
通过
|
||||
</ElButton>
|
||||
<ElButton type="danger" plain :disabled="props.actionLoading || !detailData" @click="emit('reject')">
|
||||
<template #icon>
|
||||
<IconMdiCloseCircleOutline />
|
||||
</template>
|
||||
退回
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BusinessFormDialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.batch-detail__nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.batch-detail__nav-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 1px solid rgb(226 232 240 / 90%);
|
||||
border-radius: 8px;
|
||||
background-color: rgb(255 255 255 / 98%);
|
||||
color: rgb(71 85 105 / 94%);
|
||||
cursor: pointer;
|
||||
transition: all 160ms ease;
|
||||
}
|
||||
|
||||
.batch-detail__nav-btn:hover:not(:disabled) {
|
||||
border-color: rgb(14 116 144 / 60%);
|
||||
color: rgb(14 116 144 / 96%);
|
||||
}
|
||||
|
||||
.batch-detail__nav-btn:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.batch-detail__nav-counter {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: rgb(15 23 42 / 96%);
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.batch-detail__footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.batch-detail__footer-hint {
|
||||
font-size: 13px;
|
||||
color: rgb(100 116 139 / 92%);
|
||||
}
|
||||
|
||||
.batch-detail__footer-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.batch-detail__approve-btn {
|
||||
--el-button-bg-color: #0f766e;
|
||||
--el-button-border-color: #0f766e;
|
||||
--el-button-hover-bg-color: #115e59;
|
||||
--el-button-hover-border-color: #115e59;
|
||||
--el-button-active-bg-color: #134e4a;
|
||||
--el-button-active-border-color: #134e4a;
|
||||
}
|
||||
|
||||
:deep(.overtime-application-detail-dialog__descriptions .el-descriptions__cell) {
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
:deep(.overtime-application-detail-dialog__label),
|
||||
:deep(.overtime-application-detail-dialog__label--compact) {
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
:deep(.overtime-application-detail-dialog__label) {
|
||||
width: 96px;
|
||||
min-width: 96px;
|
||||
}
|
||||
|
||||
:deep(.overtime-application-detail-dialog__label--compact) {
|
||||
width: 86px;
|
||||
min-width: 86px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { RDMS_OVERTIME_DURATION_DICT_CODE } from '@/constants/dict';
|
||||
import {
|
||||
fetchCreateOvertimeApplication,
|
||||
@@ -85,8 +86,8 @@ const rules = computed(
|
||||
|
||||
function createDefaultModel(): Api.OvertimeApplication.SaveOvertimeApplicationParams {
|
||||
return {
|
||||
overtimeDate: '',
|
||||
overtimeDuration: '',
|
||||
overtimeDate: dayjs().format('YYYY-MM-DD'),
|
||||
overtimeDuration: '0.5',
|
||||
overtimeReason: '',
|
||||
overtimeContent: '',
|
||||
approverId: ''
|
||||
|
||||
Reference in New Issue
Block a user