Merge remote-tracking branch 'origin/main'

pull/1/head
liutao 1 month ago
commit 250c873c11

@ -3,11 +3,28 @@ import request from '@/config/axios'
// 报工 VO // 报工 VO
export interface BaogongRecordVO { export interface BaogongRecordVO {
id: number // id id: number // id
planId: number // 关联计划id planId: number // 关联计划 id
num: number // 派工数量 num: number // 派工数量
baogongTime: Date // 派工时间 baogongTime: Date // 派工时间
} }
// 报工统计 VO
export interface BaogongRecordStatVO {
id: number // ID
taskCode: string // 任务单编码
planCode: string // 计划单编码
employeeId: string // 员工 ID
employeeName: string // 员工姓名
productName: string // 产品名称
productCode: string // 产品编码
baogongNum: number // 报工数量
passNum: number // 合格数量
noPassNum: number // 不合格数量
passRate: number // 合格率 (%)
reason: string // 原因
baogongTime: Date // 报工时间
}
// 报工 API // 报工 API
export const BaogongRecordApi = { export const BaogongRecordApi = {
// 查询报工分页 // 查询报工分页
@ -15,6 +32,11 @@ export const BaogongRecordApi = {
return await request.get({ url: `/mes/baogong-record/page`, params }) return await request.get({ url: `/mes/baogong-record/page`, params })
}, },
// 查询报工统计分页
getBaogongRecordStatPage: async (params: any) => {
return await request.get({ url: `/mes/baogong-record/stat-page`, params })
},
// 查询报工详情 // 查询报工详情
getBaogongRecord: async (id: number) => { getBaogongRecord: async (id: number) => {
return await request.get({ url: `/mes/baogong-record/get?id=` + id }) return await request.get({ url: `/mes/baogong-record/get?id=` + id })
@ -40,4 +62,13 @@ export const BaogongRecordApi = {
return await request.download({ url: `/mes/baogong-record/export-excel`, params }) return await request.download({ url: `/mes/baogong-record/export-excel`, params })
}, },
// 导出报工统计 Excel
exportBaogongRecordStat: async (params) => {
return await request.download({ url: `/mes/baogong-record/export-stat-excel`, params })
},
// 导出报工统计 Excel (新)
statExportExcel: async (params) => {
return await request.download({ url: `/mes/baogong-record/stat-export-excel`, params })
},
} }

@ -3398,6 +3398,10 @@ export default {
tabAllLabel: 'All', tabAllLabel: 'All',
tabIssuedLabel: 'Issued', tabIssuedLabel: 'Issued',
tabPartialSchedulingLabel: 'Partial Scheduling',
tabPendingProductionLabel: 'Pending Production',
tabInProductionLabel: 'In Production',
tabCompletedLabel: 'Completed',
tabPlanLabel: 'Planned', tabPlanLabel: 'Planned',
tabStartLabel: 'Started', tabStartLabel: 'Started',
tabFinishedLabel: 'Finished', tabFinishedLabel: 'Finished',
@ -3408,6 +3412,7 @@ export default {
tableDeliveryDateColumn: 'Delivery Date', tableDeliveryDateColumn: 'Delivery Date',
tableStatusColumn: 'Status', tableStatusColumn: 'Status',
tableScheduleCompletedColumn: 'Schedule Completed', tableScheduleCompletedColumn: 'Schedule Completed',
tableProductionProgressColumn: 'Production Progress',
tableRemarkColumn: 'Remark', tableRemarkColumn: 'Remark',
tableOperateColumn: 'Operate', tableOperateColumn: 'Operate',
@ -3831,6 +3836,33 @@ export default {
actionDeleteLabel: 'Delete', actionDeleteLabel: 'Delete',
exportFilename: 'FormingRecord.xls' exportFilename: 'FormingRecord.xls'
},
WorkReportRecord: {
moduleName: 'Work Report Record',
searchBaogongTimeLabel: 'Report Date',
searchBaogongTimeStartPlaceholder: 'Start Date',
searchBaogongTimeEndPlaceholder: 'End Date',
buttonSearchText: 'Search',
buttonResetText: 'Reset',
buttonExportText: 'Export',
tableTaskCodeColumn: 'Task Code',
tablePlanCodeColumn: 'Plan Code',
tableEmployeeIdColumn: 'Employee ID',
tableEmployeeNameColumn: 'Employee Name',
tableProductNameColumn: 'Product Name',
tableProductCodeColumn: 'Product Code',
tableBaogongNumColumn: 'Report Quantity',
tablePassNumColumn: 'Pass Quantity',
tableNoPassNumColumn: 'No-Pass Quantity',
tablePassRateColumn: 'Pass Rate(%)',
tableReasonColumn: 'Reason',
tableBaogongTimeColumn: 'Report Time',
exportFilename: 'Employee Work Report Statistics.xls'
} }
}, },
DataCollection: { DataCollection: {

@ -3244,6 +3244,10 @@ export default {
tabAllLabel: '所有', tabAllLabel: '所有',
tabIssuedLabel: '下达', tabIssuedLabel: '下达',
tabPartialSchedulingLabel: '部分排产',
tabPendingProductionLabel: '待生产',
tabInProductionLabel: '生产中',
tabCompletedLabel: '已完成',
tabPlanLabel: '计划', tabPlanLabel: '计划',
tabStartLabel: '开工', tabStartLabel: '开工',
tabFinishedLabel: '完工', tabFinishedLabel: '完工',
@ -3254,6 +3258,7 @@ export default {
tableDeliveryDateColumn: '交货日期', tableDeliveryDateColumn: '交货日期',
tableStatusColumn: '状态', tableStatusColumn: '状态',
tableScheduleCompletedColumn: '是否完成排产', tableScheduleCompletedColumn: '是否完成排产',
tableProductionProgressColumn: '生产进度',
tableRemarkColumn: '备注', tableRemarkColumn: '备注',
tableOperateColumn: '操作', tableOperateColumn: '操作',
@ -3677,6 +3682,32 @@ export default {
actionDeleteLabel: '删除', actionDeleteLabel: '删除',
exportFilename: '成型记录.xls' exportFilename: '成型记录.xls'
},
WorkReportRecord: {
moduleName: '报工记录',
searchBaogongTimeLabel: '报工日期',
searchBaogongTimeStartPlaceholder: '开始日期',
searchBaogongTimeEndPlaceholder: '结束日期',
buttonSearchText: '搜索',
buttonResetText: '重置',
buttonExportText: '导出',
tableTaskCodeColumn: '任务单编码',
tablePlanCodeColumn: '计划单编码',
tableEmployeeIdColumn: '员工 ID',
tableEmployeeNameColumn: '员工姓名',
tableProductNameColumn: '产品名称',
tableProductCodeColumn: '产品编码',
tableBaogongNumColumn: '报工数量',
tablePassNumColumn: '合格数量',
tableNoPassNumColumn: '不合格数量',
tablePassRateColumn: '合格率 (%)',
tableReasonColumn: '原因',
tableBaogongTimeColumn: '报工时间',
exportFilename: '员工报工统计.xls'
} }
}, },
DataCollection: { DataCollection: {

@ -3,31 +3,35 @@
<div ref="ganttContainerRef" class="schedule-gantt-container" :style="{ height }"></div> <div ref="ganttContainerRef" class="schedule-gantt-container" :style="{ height }"></div>
<div class="schedule-detail-panel"> <div class="schedule-detail-panel">
<div class="schedule-detail-title">计划信息</div> <div class="schedule-detail-title">计划信息</div>
<el-descriptions :column="1" border size="small" v-if="activePreviewDevice"> <template v-if="activePreviewDevice">
<el-descriptions-item label="设备名称">{{ activePreviewDevice.deviceName }}</el-descriptions-item> <el-descriptions :column="1" border size="small">
<el-descriptions-item label="设备ID">{{ activePreviewDevice.deviceId }}</el-descriptions-item> <el-descriptions-item label="设备名称">{{ activePreviewDevice.deviceName }}</el-descriptions-item>
<el-descriptions-item label="产能">{{ activePreviewDevice.ratedCapacity ?? '-' }}</el-descriptions-item> <el-descriptions-item label="设备ID">{{ activePreviewDevice.deviceId }}</el-descriptions-item>
<el-descriptions-item label="计划条数">{{ activePreviewDevice.plans?.length ?? 0 }}</el-descriptions-item> <el-descriptions-item label="产能">{{ activePreviewDevice.ratedCapacity ?? '-' }}</el-descriptions-item>
</el-descriptions> <el-descriptions-item v-if="'dailyAverageValue' in activePreviewDevice" label="每日报工平均值">{{ activePreviewDevice.dailyAverageValue ?? '-' }}</el-descriptions-item>
<el-empty v-else description="暂无计划信息" :image-size="80" /> <el-descriptions-item v-if="'dataCollectionCapacity' in activePreviewDevice" label="数据采集产能">{{ activePreviewDevice.dataCollectionCapacity ?? '-' }}</el-descriptions-item>
<div class="schedule-plan-list-title">计划明细</div> <el-descriptions-item label="计划条数">{{ activePreviewDevice.plans?.length ?? 0 }}</el-descriptions-item>
<div class="schedule-plan-list"> </el-descriptions>
<div <div class="schedule-plan-list-title">计划明细</div>
v-for="(plan, index) in activePreviewDevice?.plans ?? []" <div class="schedule-plan-list">
:key="`${activePreviewDevice?.deviceId}-${plan.taskDetailId}-${index}`" <div
:class="['schedule-plan-item', { 'schedule-plan-item-active': plan.sourceType === 'CURRENT' }]" v-for="(plan, index) in activePreviewTask ? [activePreviewTask] : (activePreviewDevice?.plans ?? [])"
> :key="`${activePreviewDevice?.deviceId}-${plan.taskDetailId}-${index}`"
<div class="schedule-plan-item-head"> :class="['schedule-plan-item', { 'schedule-plan-item-active': plan.sourceType === 'CURRENT' }]"
<span class="schedule-plan-item-title">{{ plan.productCode ?? '-' }} / {{ plan.productName ?? '-' }}</span> >
<div class="schedule-plan-item-head">
<span class="schedule-plan-item-title">{{ plan.productCode ?? '-' }} / {{ plan.productName ?? '-' }}</span>
</div>
<div>计划编码{{ plan.taskCode ?? '-' }}</div>
<div>计划数量{{ plan.planNumber ?? '-' }}</div>
<div>交货日期{{ plan.deliveryDateStr ?? '-' }}</div>
<div>开始{{ plan.planStartTimeStr || '-' }}</div>
<div>结束{{ plan.planEndTimeStr || '-' }}</div>
<div>最晚开工{{ plan.latestStartTimeStr || '-' }}</div>
</div> </div>
<div>计划编码{{ plan.taskCode ?? '-' }}</div>
<div>计划数量{{ plan.planNumber ?? '-' }}</div>
<div>交货日期{{ plan.deliveryDateStr ?? '-' }}</div>
<div>开始{{ plan.planStartTimeStr || '-' }}</div>
<div>结束{{ plan.planEndTimeStr || '-' }}</div>
<div>最晚开工{{ plan.latestStartTimeStr || '-' }}</div>
</div> </div>
</div> </template>
<el-empty v-else description="暂无计划信息" :image-size="80" />
</div> </div>
</div> </div>
@ -56,6 +60,24 @@
<el-button type="primary" @click="handleTaskAdjustSubmit"></el-button> <el-button type="primary" @click="handleTaskAdjustSubmit"></el-button>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog v-if="props.editable" v-model="startDateEditorVisible" title="修改开始时间" width="360px" append-to-body>
<el-form label-width="80px">
<el-form-item label="开始时间">
<el-date-picker
v-model="startDateEditorValue"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择开始时间"
class="!w-full"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="startDateEditorVisible = false">取消</el-button>
<el-button type="primary" @click="handleStartDateEditorSubmit"></el-button>
</template>
</el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -80,6 +102,11 @@ const props = withDefaults(
const message = useMessage() const message = useMessage()
const ganttContainerRef = ref<HTMLDivElement>() const ganttContainerRef = ref<HTMLDivElement>()
const activePreviewDevice = ref<any>() const activePreviewDevice = ref<any>()
const activePreviewTask = ref<any>()
const startDateEditorVisible = ref(false)
const startDateEditorTaskId = ref<string | number | null>(null)
const startDateEditorValue = ref('')
const tooltipCleanupFns = ref<(() => void)[]>([])
const ganttEventIds = ref<string[]>([]) const ganttEventIds = ref<string[]>([])
const ganttSyncing = ref(false) const ganttSyncing = ref(false)
const taskAdjustDialogVisible = ref(false) const taskAdjustDialogVisible = ref(false)
@ -209,7 +236,28 @@ const buildPreviewGanttData = (scheduleList: any[]) => {
return { data: tasks, links } return { data: tasks, links }
} }
const cleanupTaskTooltips = () => {
const tooltipsExt = (gantt.ext as any)?.tooltips
const tooltip = tooltipsExt?.tooltip
if (tooltipsExt?.detach) {
tooltipsExt.detach('.gantt_task_line')
tooltipsExt.detach('.gantt_task_content')
}
if (tooltip?.hide) {
tooltip.hide()
}
const node = tooltip?.getNode?.()
if (node) {
node.style.display = 'none'
node.style.visibility = 'hidden'
node.style.opacity = '0'
}
tooltipCleanupFns.value.forEach((fn) => fn())
tooltipCleanupFns.value = []
}
const destroyGantt = () => { const destroyGantt = () => {
cleanupTaskTooltips()
ganttEventIds.value.forEach((eventId) => gantt.detachEvent(eventId)) ganttEventIds.value.forEach((eventId) => gantt.detachEvent(eventId))
ganttEventIds.value = [] ganttEventIds.value = []
gantt.clearAll() gantt.clearAll()
@ -296,7 +344,28 @@ const initTaskTooltips = () => {
const tooltipsExt = (gantt.ext as any)?.tooltips const tooltipsExt = (gantt.ext as any)?.tooltips
const tooltip = tooltipsExt?.tooltip const tooltip = tooltipsExt?.tooltip
if (!tooltipsExt || !tooltip || !ganttContainerRef.value) return if (!tooltipsExt || !tooltip || !ganttContainerRef.value) return
let tooltipHideTimer: ReturnType<typeof setTimeout> | null = null
const forceHideTooltip = () => {
if (tooltipHideTimer) {
clearTimeout(tooltipHideTimer)
tooltipHideTimer = null
}
tooltip.hide()
const node = tooltip.getNode?.()
if (node) {
node.style.display = 'none'
node.style.visibility = 'hidden'
node.style.opacity = '0'
}
}
const showTooltip = (event: MouseEvent, html: string) => { const showTooltip = (event: MouseEvent, html: string) => {
if (tooltipHideTimer) {
clearTimeout(tooltipHideTimer)
tooltipHideTimer = null
}
tooltip.setContent(html) tooltip.setContent(html)
tooltip.show(event) tooltip.show(event)
const node = tooltip.getNode?.() const node = tooltip.getNode?.()
@ -308,6 +377,7 @@ const initTaskTooltips = () => {
node.style.pointerEvents = 'none' node.style.pointerEvents = 'none'
} }
} }
tooltipsExt.detach('.gantt_task_line') tooltipsExt.detach('.gantt_task_line')
tooltipsExt.detach('.gantt_task_content') tooltipsExt.detach('.gantt_task_content')
tooltipsExt.attach({ tooltipsExt.attach({
@ -323,7 +393,38 @@ const initTaskTooltips = () => {
showTooltip(event, buildTaskTooltipHtml(task)) showTooltip(event, buildTaskTooltipHtml(task))
}, },
onmouseleave: () => { onmouseleave: () => {
tooltip.hide() forceHideTooltip()
}
})
const containerEl = ganttContainerRef.value
const containerMouseLeaveHandler = () => {
forceHideTooltip()
}
containerEl.addEventListener('mouseleave', containerMouseLeaveHandler)
const documentMouseMoveHandler = (e: MouseEvent) => {
const target = e.target as HTMLElement
if (!target) return
const isOverTask = target.closest('.gantt_task_line') || target.closest('.gantt_task_content')
if (!isOverTask) {
if (tooltipHideTimer) clearTimeout(tooltipHideTimer)
tooltipHideTimer = setTimeout(forceHideTooltip, 150)
} else {
if (tooltipHideTimer) {
clearTimeout(tooltipHideTimer)
tooltipHideTimer = null
}
}
}
document.addEventListener('mousemove', documentMouseMoveHandler)
tooltipCleanupFns.value.push(() => {
containerEl.removeEventListener('mouseleave', containerMouseLeaveHandler)
document.removeEventListener('mousemove', documentMouseMoveHandler)
if (tooltipHideTimer) {
clearTimeout(tooltipHideTimer)
tooltipHideTimer = null
} }
}) })
} }
@ -340,6 +441,9 @@ const syncPlanTimeFromTask = (task: any) => {
task._planData.planEndTimeStr = end.format('YYYY-MM-DD HH:mm:ss') task._planData.planEndTimeStr = end.format('YYYY-MM-DD HH:mm:ss')
task._planData.scheduleDays = duration task._planData.scheduleDays = duration
activePreviewDevice.value = task._deviceData activePreviewDevice.value = task._deviceData
if (activePreviewTask.value && String(activePreviewTask.value.taskDetailId) === String(task._planData.taskDetailId)) {
activePreviewTask.value = task._planData
}
} }
const syncDeviceTaskRangeFromChildren = (task: any) => { const syncDeviceTaskRangeFromChildren = (task: any) => {
@ -496,6 +600,39 @@ const handleTaskAdjustSubmit = () => {
taskAdjustDialogVisible.value = false taskAdjustDialogVisible.value = false
} }
const handleStartDateEditorSubmit = () => {
if (!startDateEditorTaskId.value || !startDateEditorValue.value) {
startDateEditorVisible.value = false
return
}
const task = gantt.getTask(startDateEditorTaskId.value)
if (!task?._planData) {
startDateEditorVisible.value = false
return
}
const newStart = dayjs(startDateEditorValue.value)
if (!newStart.isValid()) {
message.warning('请选择有效的时间')
return
}
const duration = Math.max(Number(task.duration) || 1, 1)
const newEnd = newStart.add(duration - 1, 'day').endOf('day')
ganttSyncing.value = true
try {
task.start_date = newStart.toDate()
task.end_date = newEnd.toDate()
task.duration = duration
syncPlanTimeFromTask(task)
normalizeDeviceChildren(task.parent)
refreshPlanLinksByRowOrder()
refreshTimelineRangeByTasks()
gantt.updateTask(task.id)
} finally {
ganttSyncing.value = false
}
startDateEditorVisible.value = false
}
const refreshTimelineRangeByTasks = () => { const refreshTimelineRangeByTasks = () => {
let minStart = Number.POSITIVE_INFINITY let minStart = Number.POSITIVE_INFINITY
let maxEnd = Number.NEGATIVE_INFINITY let maxEnd = Number.NEGATIVE_INFINITY
@ -527,10 +664,11 @@ const initGanttPreview = () => {
destroyGantt() destroyGantt()
if (!previewScheduleList.value.length) { if (!previewScheduleList.value.length) {
activePreviewDevice.value = undefined activePreviewDevice.value = undefined
activePreviewTask.value = undefined
return return
} }
gantt.plugins({ tooltip: true }) gantt.plugins({ tooltip: true, inline_edit: true,undo: true })
gantt.config.readonly = !props.editable gantt.config.readonly = !props.editable
gantt.config.drag_move = !!props.editable gantt.config.drag_move = !!props.editable
gantt.config.drag_links = false gantt.config.drag_links = false
@ -560,12 +698,8 @@ const initGanttPreview = () => {
width: 210, width: 210,
template: (task: any) => template: (task: any) =>
props.editable && task?._planData && String(task?._planData?.sourceType ?? '').toUpperCase() === 'CURRENT' props.editable && task?._planData && String(task?._planData?.sourceType ?? '').toUpperCase() === 'CURRENT'
? `<span class="gantt-inline-editor-trigger" data-field="start_date">${formatGridDateText(task.start_date)}</span>` ? `<span class="gantt-inline-editor-trigger" data-field="start_date" data-task-id="${task.id}">${formatGridDateText(task.start_date)}</span>`
: formatGridDateText(task.start_date), : formatGridDateText(task.start_date)
editor: {
type: 'date',
map_to: 'start_date'
}
}, },
{ {
name: 'duration', name: 'duration',
@ -598,6 +732,7 @@ const initGanttPreview = () => {
const globalRange = getGlobalDateRange(previewScheduleList.value) const globalRange = getGlobalDateRange(previewScheduleList.value)
gantt.config.start_date = dayjs(globalRange.start).startOf('day').toDate() gantt.config.start_date = dayjs(globalRange.start).startOf('day').toDate()
gantt.config.end_date = dayjs(globalRange.end).endOf('day').toDate() gantt.config.end_date = dayjs(globalRange.end).endOf('day').toDate()
gantt.init(ganttContainerRef.value) gantt.init(ganttContainerRef.value)
const ganttData = buildPreviewGanttData(previewScheduleList.value) const ganttData = buildPreviewGanttData(previewScheduleList.value)
@ -606,18 +741,39 @@ const initGanttPreview = () => {
if (ganttData.data.length) { if (ganttData.data.length) {
activePreviewDevice.value = ganttData.data[0]._deviceData activePreviewDevice.value = ganttData.data[0]._deviceData
gantt.showDate(gantt.config.start_date) const today = dayjs().startOf('day').toDate()
const pos = gantt.posFromDate(today)
const scrollState = gantt.getScrollState()
if (scrollState && pos >= 0) {
const halfWidth = scrollState.inner_width / 3
gantt.scrollTo(Math.max(0, pos - halfWidth), 0)
} else {
gantt.showDate(today)
}
} }
const clickEventId = gantt.attachEvent('onTaskClick', (id, event: MouseEvent) => { const clickEventId = gantt.attachEvent('onTaskClick', (id, event: MouseEvent) => {
const task = gantt.getTask(id) const task = gantt.getTask(id)
activePreviewDevice.value = task?._deviceData if (task?._planData) {
activePreviewTask.value = task._planData
activePreviewDevice.value = task._deviceData
} else {
activePreviewTask.value = undefined
activePreviewDevice.value = task?._deviceData
}
if (!props.editable) return true if (!props.editable) return true
const target = event?.target as HTMLElement | null const target = event?.target as HTMLElement | null
const editableNode = target?.closest('.gantt-inline-editor-trigger') as HTMLElement | null const editableNode = target?.closest('.gantt-inline-editor-trigger') as HTMLElement | null
const field = editableNode?.dataset?.field const field = editableNode?.dataset?.field
if (!field || !task?._planData || String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return true
if (field === 'start_date') {
startDateEditorTaskId.value = id
startDateEditorValue.value = dayjs(task.start_date).format('YYYY-MM-DD HH:mm:ss')
startDateEditorVisible.value = true
return false
}
const inlineEditors = (gantt.ext as any)?.inlineEditors const inlineEditors = (gantt.ext as any)?.inlineEditors
if (field && task?._planData && String(task?._planData?.sourceType ?? '').toUpperCase() === 'CURRENT' && inlineEditors?.startEdit) { if (inlineEditors?.startEdit) {
inlineEditors.startEdit(id, field) inlineEditors.startEdit(id, field)
return false return false
} }
@ -625,6 +781,28 @@ const initGanttPreview = () => {
}) })
ganttEventIds.value.push(clickEventId) ganttEventIds.value.push(clickEventId)
if (props.editable && ganttContainerRef.value) {
const gridClickHandler = (e: MouseEvent) => {
const target = e.target as HTMLElement
const trigger = target.closest('.gantt-inline-editor-trigger[data-field="start_date"]') as HTMLElement | null
if (!trigger) return
const taskId = trigger.getAttribute('data-task-id')
if (!taskId) return
try {
const task = gantt.getTask(taskId)
if (!task?._planData || String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return
e.stopPropagation()
startDateEditorTaskId.value = taskId
startDateEditorValue.value = dayjs(task.start_date).format('YYYY-MM-DD HH:mm:ss')
startDateEditorVisible.value = true
} catch {}
}
ganttContainerRef.value.addEventListener('click', gridClickHandler, true)
tooltipCleanupFns.value.push(() => {
ganttContainerRef.value?.removeEventListener('click', gridClickHandler, true)
})
}
if (!props.editable) return if (!props.editable) return
const beforeDragEventId = gantt.attachEvent('onBeforeTaskDrag', (id) => { const beforeDragEventId = gantt.attachEvent('onBeforeTaskDrag', (id) => {

@ -99,24 +99,13 @@
<el-form-item :label="t('ProductionPlan.Plan.dialogPlanNumberLabel')" prop="planNumber"> <el-form-item :label="t('ProductionPlan.Plan.dialogPlanNumberLabel')" prop="planNumber">
<el-input v-model="formData.planNumber" /> <el-input v-model="formData.planNumber" />
</el-form-item> </el-form-item>
<el-form-item :label="t('ProductionPlan.Plan.dialogPreProductionLabel')" prop="isPreProduction">
<el-radio-group v-model="formData.isPreProduction">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.MES_PRE_PRODUCTION)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="热压数量" prop="reyaNumber"> <!-- <el-form-item label="热压数量" prop="reyaNumber">
<el-input v-model="formData.reyaNumber" /> <el-input v-model="formData.reyaNumber" />
</el-form-item> --> </el-form-item> -->
<el-form-item :label="t('ProductionPlan.Plan.dialogPlanStartLabel')" prop="planStartTime"> <el-form-item :label="t('ProductionPlan.Plan.dialogPlanStartLabel')" prop="planStartTime">
<el-date-picker <el-date-picker
v-model="formData.planStartTime" v-model="formData.planStartTime"
type="date" type="datetime"
value-format="x" value-format="x"
:placeholder="t('ProductionPlan.Plan.dialogPlanStartPlaceholder')" :placeholder="t('ProductionPlan.Plan.dialogPlanStartPlaceholder')"
class="!w-full" class="!w-full"
@ -126,7 +115,7 @@
<el-form-item :label="t('ProductionPlan.Plan.dialogPlanEndLabel')" prop="planEndTime"> <el-form-item :label="t('ProductionPlan.Plan.dialogPlanEndLabel')" prop="planEndTime">
<el-date-picker <el-date-picker
v-model="formData.planEndTime" v-model="formData.planEndTime"
type="date" type="datetime"
value-format="x" value-format="x"
:placeholder="t('ProductionPlan.Plan.dialogPlanEndPlaceholder')" :placeholder="t('ProductionPlan.Plan.dialogPlanEndPlaceholder')"
class="!w-full" class="!w-full"
@ -136,7 +125,7 @@
<el-form-item label="最晚开工时间" prop="latestStartTime"> <el-form-item label="最晚开工时间" prop="latestStartTime">
<el-date-picker <el-date-picker
v-model="formData.latestStartTime" v-model="formData.latestStartTime"
type="date" type="datetime"
value-format="x" value-format="x"
:disabled-date="latestStartDisabledDate" :disabled-date="latestStartDisabledDate"
placeholder="请选择最晚开工时间" placeholder="请选择最晚开工时间"
@ -180,7 +169,6 @@ import { PlanApi, PlanVO } from '@/api/mes/plan'
import { TaskApi, TaskDetailVO, TaskVO } from '@/api/mes/task' import { TaskApi, TaskDetailVO, TaskVO } from '@/api/mes/task'
import { DeviceLedgerApi, DeviceLedgerVO } from '@/api/mes/deviceledger' import { DeviceLedgerApi, DeviceLedgerVO } from '@/api/mes/deviceledger'
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue' import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
import { DICT_TYPE, getIntDictOptions } from "@/utils/dict";
/** 生产计划 表单 */ /** 生产计划 表单 */
defineOptions({ name: 'PlanForm' }) defineOptions({ name: 'PlanForm' })
@ -188,6 +176,7 @@ const taskList = ref<TaskVO[]>([]) // 用户列表
const taskDetailList = ref<TaskDetailVO[]>([]) // const taskDetailList = ref<TaskDetailVO[]>([]) //
const deviceSelectDialogRef = ref() const deviceSelectDialogRef = ref()
const selectedDeviceRows = ref<DeviceLedgerVO[]>([]) const selectedDeviceRows = ref<DeviceLedgerVO[]>([])
const maxUnplannedNumber = ref<number | undefined>(undefined)
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -217,7 +206,6 @@ const formData = ref({
deviceId: undefined, deviceId: undefined,
deviceName: undefined, deviceName: undefined,
workerId: undefined, workerId: undefined,
isPreProduction: undefined,
isCode: undefined, isCode: undefined,
latestStartTime: undefined, latestStartTime: undefined,
finishDate: undefined, finishDate: undefined,
@ -306,10 +294,45 @@ const validatePlanEndTime = (_rule: any, value: any, callback: (error?: Error) =
} }
callback() callback()
} }
const getSelectedTaskDetailUnplanned = () => {
const detail = taskDetailList.value.find(
(item: any) => Number(item.id) === Number(formData.value.taskDetailId)
) as any
if (!detail) return undefined
const total = Number(detail.number)
const planned = Number(detail.planNumber)
if (!Number.isFinite(total) || !Number.isFinite(planned)) return undefined
return Math.max(total - planned, 0)
}
const getPlanNumberLimit = () => {
const detailUnplanned = getSelectedTaskDetailUnplanned()
if (detailUnplanned !== undefined) return detailUnplanned
return maxUnplannedNumber.value
}
const validatePlanNumber = (_rule: any, value: any, callback: (error?: Error) => void) => {
if (value === undefined || value === null || value === '') {
callback()
return
}
const currentPlanNumber = Number(value)
if (!Number.isFinite(currentPlanNumber)) {
callback(new Error('计划数量必须为数字'))
return
}
const planNumberLimit = getPlanNumberLimit()
if (planNumberLimit !== undefined && currentPlanNumber > planNumberLimit) {
callback(new Error(`计划数量不能大于未计划数量(${planNumberLimit})`))
return
}
callback()
}
const formRules = reactive({ const formRules = reactive({
taskDetailId: [{ required: true, message: t('ProductionPlan.Plan.validatorTaskDetailRequired'), trigger: 'blur' }], taskDetailId: [{ required: true, message: t('ProductionPlan.Plan.validatorTaskDetailRequired'), trigger: 'blur' }],
taskId: [{ required: true, message: t('ProductionPlan.Plan.validatorTaskRequired'), trigger: 'blur' }], taskId: [{ required: true, message: t('ProductionPlan.Plan.validatorTaskRequired'), trigger: 'blur' }],
planNumber: [{ required: true, message: t('ProductionPlan.Plan.validatorPlanNumberRequired'), trigger: 'blur' }], planNumber: [
{ required: true, message: t('ProductionPlan.Plan.validatorPlanNumberRequired'), trigger: 'blur' },
{ validator: validatePlanNumber, trigger: ['blur', 'change'] }
],
// reyaNumber: [{ required: true, message: t('ProductionPlan.Plan.validatorReyaNumberRequired'), trigger: 'blur' }], // reyaNumber: [{ required: true, message: t('ProductionPlan.Plan.validatorReyaNumberRequired'), trigger: 'blur' }],
planStartTime: [ planStartTime: [
{ required: true, message: t('ProductionPlan.Plan.validatorPlanStartRequired'), trigger: 'blur' }, { required: true, message: t('ProductionPlan.Plan.validatorPlanStartRequired'), trigger: 'blur' },
@ -320,7 +343,6 @@ const formRules = reactive({
{ validator: validatePlanEndTime, trigger: 'change' } { validator: validatePlanEndTime, trigger: 'change' }
], ],
deviceId: [{ required: true, message: '请选择设备', trigger: 'change' }], deviceId: [{ required: true, message: '请选择设备', trigger: 'change' }],
isPreProduction: [{ required: true, message: t('ProductionPlan.Plan.validatorPreProductionRequired'), trigger: 'blur' }],
latestStartTime: [ latestStartTime: [
{ required: true, message: '请选择最晚开工时间', trigger: 'blur' }, { required: true, message: '请选择最晚开工时间', trigger: 'blur' },
{ validator: validateLatestStartTime, trigger: 'change' } { validator: validateLatestStartTime, trigger: 'change' }
@ -374,6 +396,7 @@ const open = async (type: string, id?: number,
dialogTitle.value = t('action.' + type) dialogTitle.value = t('action.' + type)
formType.value = type formType.value = type
resetForm() resetForm()
maxUnplannedNumber.value = number !== undefined ? Math.max(Number(number), 0) : undefined
formData.value.deliveryDate = taskDeliveryDate || undefined formData.value.deliveryDate = taskDeliveryDate || undefined
if(taskId) { if(taskId) {
formData.value.taskId = taskId formData.value.taskId = taskId
@ -465,6 +488,7 @@ const resetForm = () => {
finishDate: undefined, finishDate: undefined,
deliveryDate: undefined deliveryDate: undefined
} }
maxUnplannedNumber.value = undefined
selectedDeviceRows.value = [] selectedDeviceRows.value = []
formRef.value?.resetFields() formRef.value?.resetFields()
} }
@ -474,10 +498,12 @@ const handleTaskChange = async() => {
taskDetailList.value =await TaskApi.getTaskDetailListByTaskId(formData.value.taskId) taskDetailList.value =await TaskApi.getTaskDetailListByTaskId(formData.value.taskId)
formData.value.taskDetailId = undefined formData.value.taskDetailId = undefined
formData.value.finishDate = undefined formData.value.finishDate = undefined
formRef.value?.validateField('planNumber')
} }
/** 明细变化 */ /** 明细变化 */
const handleTaskDetailChange = async() => { const handleTaskDetailChange = async() => {
syncFinishDateByTaskDetail() syncFinishDateByTaskDetail()
formRef.value?.validateField('planNumber')
} }
const handlePlanStartTimeChange = () => { const handlePlanStartTimeChange = () => {
formRef.value?.validateField(['planStartTime', 'planEndTime']) formRef.value?.validateField(['planStartTime', 'planEndTime'])

@ -55,7 +55,7 @@
<el-form-item :label="t('ProductionPlan.Plan.dialogPlanNumberLabel')" prop="planNumber"> <el-form-item :label="t('ProductionPlan.Plan.dialogPlanNumberLabel')" prop="planNumber">
<el-input disabled v-model="formData.planNumber" /> <el-input disabled v-model="formData.planNumber" />
</el-form-item> </el-form-item>
<el-form-item :label="t('ProductionPlan.Plan.dialogPreProductionLabel')" prop="isPreProduction"> <!-- <el-form-item :label="t('ProductionPlan.Plan.dialogPreProductionLabel')" prop="isPreProduction">
<el-radio-group disabled v-model="formData.isPreProduction"> <el-radio-group disabled v-model="formData.isPreProduction">
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.MES_PRE_PRODUCTION)" v-for="dict in getIntDictOptions(DICT_TYPE.MES_PRE_PRODUCTION)"
@ -65,7 +65,7 @@
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item> -->
<el-form-item :label="t('ProductionPlan.Plan.dialogPlanStartLabel')" prop="planStartTime"> <el-form-item :label="t('ProductionPlan.Plan.dialogPlanStartLabel')" prop="planStartTime">
<el-date-picker <el-date-picker
disabled disabled

@ -39,6 +39,7 @@
v-model="formData.deliveryDate" v-model="formData.deliveryDate"
type="date" type="date"
value-format="x" value-format="x"
:disabled-date="disablePastDate"
:placeholder="t('ProductionPlan.Task.dialogDeliveryDatePlaceholder')" :placeholder="t('ProductionPlan.Task.dialogDeliveryDatePlaceholder')"
class="!w-full" class="!w-full"
/> />
@ -107,8 +108,27 @@ const formData = ref({
isEnable: undefined, isEnable: undefined,
isCode: undefined isCode: undefined
}) })
const disablePastDate = (date: Date) => {
return date.getTime() < new Date(new Date().setHours(0, 0, 0, 0)).getTime()
}
const validateDeliveryDate = (_rule: any, value: any, callback: (error?: Error) => void) => {
if (!value) {
callback()
return
}
const selected = Number(value)
const todayStart = new Date(new Date().setHours(0, 0, 0, 0)).getTime()
if (Number.isFinite(selected) && selected < todayStart) {
callback(new Error('交货日期不能早于今天'))
return
}
callback()
}
const formRules = reactive({ const formRules = reactive({
deliveryDate: [{ required: true, message: t('ProductionPlan.Task.validatorDeliveryDateRequired'), trigger: 'blur' }], deliveryDate: [
{ required: true, message: t('ProductionPlan.Task.validatorDeliveryDateRequired'), trigger: 'blur' },
{ validator: validateDeliveryDate, trigger: ['change', 'blur'] }
],
taskType: [{ required: true, message: t('ProductionPlan.Task.validatorTaskTypeRequired'), trigger: 'blur' }], taskType: [{ required: true, message: t('ProductionPlan.Task.validatorTaskTypeRequired'), trigger: 'blur' }],
isUrgent: [{ required: true, message: t('ProductionPlan.Task.validatorIsUrgentRequired'), trigger: 'change' }] isUrgent: [{ required: true, message: t('ProductionPlan.Task.validatorIsUrgentRequired'), trigger: 'change' }]
}) })

@ -89,6 +89,7 @@
v-model="formData.finishDate" v-model="formData.finishDate"
type="date" type="date"
value-format="x" value-format="x"
:disabled-date="disablePastDate"
:placeholder="t('ProductionPlan.Task.detailFormDeliveryDatePlaceholder')" :placeholder="t('ProductionPlan.Task.detailFormDeliveryDatePlaceholder')"
/> />
</el-form-item> </el-form-item>
@ -158,12 +159,29 @@ const formData = ref({
boxingDate: undefined, boxingDate: undefined,
arriveDate: undefined, arriveDate: undefined,
}) })
const disablePastDate = (date: Date) => {
return date.getTime() < new Date(new Date().setHours(0, 0, 0, 0)).getTime()
}
const validateFinishDate = (_rule: any, value: any, callback: (error?: Error) => void) => {
if (!value) {
callback()
return
}
const selected = Number(value)
const todayStart = new Date(new Date().setHours(0, 0, 0, 0)).getTime()
if (Number.isFinite(selected) && selected < todayStart) {
callback(new Error('交货日期不能早于今天'))
return
}
callback()
}
const formRules = reactive({ const formRules = reactive({
productId: [{ required: true, message: t('ProductionPlan.Task.validatorDetailProductIdRequired'), trigger: 'blur' }], productId: [{ required: true, message: t('ProductionPlan.Task.validatorDetailProductIdRequired'), trigger: 'blur' }],
unitId: [{ required: true, message: t('ProductionPlan.Task.validatorDetailUnitIdRequired'), trigger: 'blur' }], unitId: [{ required: true, message: t('ProductionPlan.Task.validatorDetailUnitIdRequired'), trigger: 'blur' }],
taskId: [{ required: true, message: t('ProductionPlan.Task.validatorDetailTaskIdRequired'), trigger: 'blur' }], taskId: [{ required: true, message: t('ProductionPlan.Task.validatorDetailTaskIdRequired'), trigger: 'blur' }],
number: [{ required: true, message: t('ProductionPlan.Task.validatorDetailNumberRequired'), trigger: 'blur' }], number: [{ required: true, message: t('ProductionPlan.Task.validatorDetailNumberRequired'), trigger: 'blur' }],
packageSize: [{ required: true, message: t('ProductionPlan.Task.validatorDetailPackageSizeRequired'), trigger: 'blur' }] packageSize: [{ required: true, message: t('ProductionPlan.Task.validatorDetailPackageSizeRequired'), trigger: 'blur' }],
finishDate: [{ validator: validateFinishDate, trigger: ['change', 'blur'] }]
}) })
const formRef = ref() // Ref const formRef = ref() // Ref

@ -94,10 +94,10 @@
<el-tab-pane :label="t('ProductionPlan.Task.tabDraftLabel')" name="0" /> <el-tab-pane :label="t('ProductionPlan.Task.tabDraftLabel')" name="0" />
<el-tab-pane :label="t('ProductionPlan.Task.tabSubmitLabel')" name="1" /> <el-tab-pane :label="t('ProductionPlan.Task.tabSubmitLabel')" name="1" />
<el-tab-pane :label="t('ProductionPlan.Task.tabIssuedLabel')" name="2" /> <el-tab-pane :label="t('ProductionPlan.Task.tabIssuedLabel')" name="2" />
<el-tab-pane :label="t('ProductionPlan.Task.tabPlanLabel')" name="3" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabPartialSchedulingLabel')" name="7" />
<el-tab-pane :label="t('ProductionPlan.Task.tabStartLabel')" name="4" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabPendingProductionLabel')" name="8" />
<el-tab-pane :label="t('ProductionPlan.Task.tabFinishedLabel')" name="5" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabInProductionLabel')" name="9" />
<el-tab-pane :label="t('ProductionPlan.Task.tabStoredLabel')" name="6" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabCompletedLabel')" name="10" />
</el-tabs> </el-tabs>
<el-table <el-table
v-loading="loading" v-loading="loading"

@ -58,10 +58,10 @@ v-model="queryParams.createTime" @change="handleQuery" value-format="YYYY-MM-DD
<el-tabs v-model="activeName" @tab-click="handleTabClick"> <el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane :label="t('ProductionPlan.TaskSummary.tabAllLabel')" name="" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabAllLabel')" name="" />
<el-tab-pane :label="t('ProductionPlan.TaskSummary.tabIssuedLabel')" name="2" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabIssuedLabel')" name="2" />
<el-tab-pane label="部分排产" name="7" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabPartialSchedulingLabel')" name="7" />
<el-tab-pane label="待生产" name="8" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabPendingProductionLabel')" name="8" />
<el-tab-pane label="生产中" name="9" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabInProductionLabel')" name="9" />
<el-tab-pane label="已完成" name="10" /> <el-tab-pane :label="t('ProductionPlan.TaskSummary.tabCompletedLabel')" name="10" />
</el-tabs> </el-tabs>
<el-table <el-table
v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" highlight-current-row v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" highlight-current-row
@ -85,6 +85,19 @@ v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" hi
<el-tag :type="scope.row.isScheduled ? 'success' : 'info'">{{ scope.row.isScheduled ? '是' : '否' }}</el-tag> <el-tag :type="scope.row.isScheduled ? 'success' : 'info'">{{ scope.row.isScheduled ? '是' : '否' }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="t('ProductionPlan.TaskSummary.tableProductionProgressColumn')" align="center" min-width="180px">
<template #default="scope">
<div class="production-progress-cell">
<el-progress
:percentage="getProductionProgressPercent(scope.row)"
:show-text="false"
:stroke-width="12"
class="production-progress-bar"
/>
<span class="production-progress-text">{{ getProductionProgressText(scope.row) }}</span>
</div>
</template>
</el-table-column>
<el-table-column :label="t('ProductionPlan.TaskSummary.tableRemarkColumn')" align="center" prop="remark" /> <el-table-column :label="t('ProductionPlan.TaskSummary.tableRemarkColumn')" align="center" prop="remark" />
<el-table-column :label="t('ProductionPlan.TaskSummary.tableOperateColumn')" align="center" min-width="200px"> <el-table-column :label="t('ProductionPlan.TaskSummary.tableOperateColumn')" align="center" min-width="200px">
@ -162,6 +175,19 @@ const deliveryDateFormatter = (_row: any, _column: any, value: any) => {
if (value) return dateFormatter2(_row, _column, value) if (value) return dateFormatter2(_row, _column, value)
return dateFormatter2(_row, _column, _row?.finishDate) return dateFormatter2(_row, _column, _row?.finishDate)
} }
const getProductionProgressPercent = (row: any) => {
const storedPlanNumber = Number(row?.storedPlanNumber ?? 0)
const totalNumber = Number(row?.totalNumber ?? row?.number ?? 0)
if (!Number.isFinite(storedPlanNumber) || !Number.isFinite(totalNumber) || totalNumber <= 0) return 0
const rawPercent = (storedPlanNumber / totalNumber) * 100
return Math.max(0, Math.min(100, Number(rawPercent.toFixed(2))))
}
const getProductionProgressText = (row: any) => {
const storedPlanNumber = Number(row?.storedPlanNumber ?? 0)
const totalNumber = Number(row?.totalNumber ?? row?.number ?? 0)
if (!Number.isFinite(storedPlanNumber) || !Number.isFinite(totalNumber) || totalNumber <= 0) return '0/0'
return `${storedPlanNumber}/${totalNumber}`
}
/** 查询列表 */ /** 查询列表 */
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
@ -241,3 +267,23 @@ const handleTabClick = (tab: TabsPaneContext) => {
handleQuery() handleQuery()
} }
</script> </script>
<style scoped>
.production-progress-cell {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.production-progress-bar {
width: 180px;
}
.production-progress-text {
min-width: 92px;
text-align: left;
font-variant-numeric: tabular-nums;
color: var(--el-text-color-regular);
}
</style>

@ -0,0 +1,224 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item :label="t('ProductionPlan.WorkReportRecord.searchBaogongTimeLabel')" prop="baogongTime">
<el-date-picker
v-model="queryParams.baogongTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
:start-placeholder="t('ProductionPlan.WorkReportRecord.searchBaogongTimeStartPlaceholder')"
:end-placeholder="t('ProductionPlan.WorkReportRecord.searchBaogongTimeEndPlaceholder')"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
:shortcuts="dateShortcuts"
class="!w-240px"
@change="handleDateChange"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> {{ t('ProductionPlan.WorkReportRecord.buttonSearchText') }}</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> {{ t('ProductionPlan.WorkReportRecord.buttonResetText') }}</el-button>
<el-button
type="success"
plain
@click="handleStatExport"
:loading="statExportLoading"
v-hasPermi="['mes:baogong-record:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tableTaskCodeColumn')" align="center" prop="taskCode" min-width="120px" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tablePlanCodeColumn')" align="center" prop="planCode" min-width="120px" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tableEmployeeIdColumn')" align="center" prop="employeeId" min-width="100px" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tableEmployeeNameColumn')" align="center" prop="employeeName" min-width="100px" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tableProductNameColumn')" align="center" prop="productName" min-width="120px" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tableProductCodeColumn')" align="center" prop="productCode" min-width="100px" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tableBaogongNumColumn')" align="center" prop="baogongNum" min-width="100px" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tablePassNumColumn')" align="center" prop="passNum" min-width="100px" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tableNoPassNumColumn')" align="center" prop="noPassNum" min-width="100px" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tablePassRateColumn')" align="center" prop="passRate" min-width="100px" :formatter="formatPassRate" sortable />
<el-table-column :label="t('ProductionPlan.WorkReportRecord.tableReasonColumn')" align="center" prop="reason" min-width="150px" show-overflow-tooltip />
<el-table-column
:label="t('ProductionPlan.WorkReportRecord.tableBaogongTimeColumn')"
align="center"
prop="baogongTime"
:formatter="dateFormatter"
width="180px"
sortable
/>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { BaogongRecordApi, BaogongRecordStatVO } from '@/api/mes/baogongrecord'
/** 报工记录统计 列表 */
defineOptions({ name: 'WorkReportRecord' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<BaogongRecordStatVO[]>([]) //
const total = ref(0) //
const selectedIds = ref<(string | number)[]>([])
//
const dateShortcuts = [
{
text: '今日',
value: () => {
const start = new Date()
start.setHours(0, 0, 0, 0)
const end = new Date()
end.setHours(23, 59, 59, 999)
return [start, end]
}
},
{
text: '近七日',
value: () => {
const end = new Date()
end.setHours(23, 59, 59, 999)
const start = new Date()
start.setDate(start.getDate() - 6)
start.setHours(0, 0, 0, 0)
return [start, end]
}
},
{
text: '近三十日',
value: () => {
const end = new Date()
end.setHours(23, 59, 59, 999)
const start = new Date()
start.setDate(start.getDate() - 29)
start.setHours(0, 0, 0, 0)
return [start, end]
}
}
]
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
beginBaogongTime: undefined,
endBaogongTime: undefined,
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
const statExportLoading = ref(false)
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await BaogongRecordApi.getBaogongRecordStatPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
queryParams.beginBaogongTime = undefined
queryParams.endBaogongTime = undefined
handleQuery()
}
/** 日期选择变化处理 */
const handleDateChange = (value: any) => {
if (value && Array.isArray(value) && value.length === 2) {
queryParams.beginBaogongTime = value[0]
queryParams.endBaogongTime = value[1]
} else {
queryParams.beginBaogongTime = undefined
queryParams.endBaogongTime = undefined
}
handleQuery()
}
/** 合格率格式化 */
const formatPassRate = (row: BaogongRecordStatVO) => {
if (row.passRate === null || row.passRate === undefined) return ''
return row.passRate + '%'
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
await message.exportConfirm()
exportLoading.value = true
const data = await BaogongRecordApi.exportBaogongRecordStat(queryParams)
download.excel(data, t('ProductionPlan.WorkReportRecord.exportFilename'))
} catch {
} finally {
exportLoading.value = false
}
}
/** 多选变化 */
const handleSelectionChange = (selection: BaogongRecordStatVO[]) => {
selectedIds.value = selection
.map((item) => item.id)
.filter((id): id is string | number => id !== undefined && id !== null && id !== '')
}
/** 统计导出按钮操作 */
const handleStatExport = async () => {
try {
await message.exportConfirm()
statExportLoading.value = true
const params: any = { ...queryParams }
if (selectedIds.value.length > 0) {
params.ids = selectedIds.value.join(',')
}
const data = await BaogongRecordApi.statExportExcel(params)
download.excel(data, t('ProductionPlan.WorkReportRecord.exportFilename'))
} catch {
} finally {
statExportLoading.value = false
}
}
//
onMounted(async () => {
await getList()
})
</script>
<style lang="scss" scoped>
</style>
Loading…
Cancel
Save