style:生产报表页面适配中英文

pull/1/head
黄伟杰 4 weeks ago
parent b7883659b1
commit 1cd7cbf82a

@ -4269,5 +4269,142 @@ export default {
messageDeviceNoParams: 'No parameters under this device', messageDeviceNoParams: 'No parameters under this device',
messageFetchChartFailed: 'Failed to load chart data' messageFetchChartFailed: 'Failed to load chart data'
} }
},
ProductionReport: {
Index: {
searchCodeLabel: 'Task Code',
searchCodePlaceholder: 'Please enter task code',
searchOrderDateLabel: 'Order Date',
searchDeliveryDateLabel: 'Delivery Date',
searchRemarkLabel: 'Remark',
searchRemarkPlaceholder: 'Please enter remark',
searchCreateTimeLabel: 'Create Time',
startDatePlaceholder: 'Start Date',
endDatePlaceholder: 'End Date',
buttonSearch: 'Search',
buttonReset: 'Reset',
buttonExport: 'Export',
tabAll: 'All',
tabIssued: 'Issued',
tabPartialScheduled: 'Partial Scheduled',
tabPendingProduction: 'Pending',
tabInProduction: 'In Production',
tabCompleted: 'Completed',
tableCode: 'Task Code',
tableOrderDate: 'Order Date',
tableDeliveryDate: 'Delivery Date',
tableStatus: 'Status',
tableIsScheduled: 'Scheduled',
tableProductionProgress: 'Progress',
tableRemark: 'Remark',
yes: 'Yes',
no: 'No',
detailTabBasicInfo: 'Basic Info',
detailTabRelatedPlan: 'Related Plans',
detailTabQualityInfo: 'Quality Info',
detailTabBaogongInfo: 'Work Report',
exportFilename: 'ProductionReport'
},
BasicInfo: {
buttonRefresh: 'Refresh',
tableTaskCode: 'Task Code',
tableProductCode: 'Product Code',
tableProductName: 'Product Name',
tableTotalNumber: 'Total Qty',
tablePlanNumber: 'Planned Qty',
tableUnplannedNumber: 'Unplanned Qty',
tableOperate: 'Operate',
buttonViewProductInfo: 'View Product'
},
ProductInfo: {
dialogTitle: 'Product Info',
labelProductCode: 'Product Code',
labelProductName: 'Product Name',
labelNumber: 'Quantity',
labelUnit: 'Unit',
labelSpecification: 'Specification',
tableMaterialCode: 'Material Code',
tableMaterialName: 'Material Name',
tableRequiredQuantity: 'Required Qty',
tableUnit: 'Unit'
},
BaogongInfo: {
dateRangeSeparator: 'To',
startDatePlaceholder: 'Start Date',
endDatePlaceholder: 'End Date',
buttonToday: 'Today',
buttonLast7Days: 'Last 7 Days',
buttonLast30Days: 'Last 30 Days',
buttonReset: 'Reset',
buttonRefresh: 'Refresh',
tableTaskCode: 'Task Code',
tablePlanCode: 'Plan Code',
tableEmployeeId: 'Employee ID',
tableEmployeeName: 'Employee Name',
tableProductName: 'Product Name',
tableProductCode: 'Product Code',
tableBaogongNumber: 'Report Qty',
tablePassNumber: 'Pass Qty',
tableNoPassNumber: 'Fail Qty',
tablePassRate: 'Pass Rate%',
tableNoPassReason: 'Fail Reason',
tableBaogongTime: 'Report Time'
},
QualityInfo: {
buttonRefresh: 'Refresh',
expandTableName: 'Inspection Item',
expandTableMethod: 'Method',
expandTableStandard: 'Standard',
expandTableUnit: 'Unit',
expandTableUpperLimit: 'Upper Limit',
expandTableLowerLimit: 'Lower Limit',
expandTableInputValue: 'Input Value',
expandTableImage: 'Image',
expandTablePreview: 'Preview',
expandTableResult: 'Result',
expandTableRemark: 'Remark',
tableCode: 'Inspection Code',
tableCategory: 'Category',
tableStatus: 'Status',
tableTicketCode: 'Ticket Code',
tableProcess: 'Process',
tableSchema: 'Inspection Schema',
tableManager: 'Manager',
tableExecutor: 'Executor',
tableExecuteTime: 'Execute Time',
tableRemark: 'Remark',
tableResult: 'Result',
resultPass: 'Pass',
resultFail: 'Fail',
inspectionPending: 'Pending',
inspectionPass: 'Passed',
inspectionFail: 'Failed',
unknown: 'Unknown'
},
RelatedPlan: {
buttonRefresh: 'Refresh',
emptyDescription: 'No related plans'
},
PlanCard: {
labelProductName: 'Product Name',
labelDeviceName: 'Device Name',
labelPlanStartTime: 'Plan Start Time',
labelPlanEndTime: 'Plan End Time',
labelLatestStartTime: 'Latest Start Time',
statPlanNumber: 'Planned Qty',
statWangongNumber: 'Completed Qty',
statPassNumber: 'Pass Qty',
statNoPassNumber: 'Fail Qty',
statPassRate: 'Pass Rate',
emptyDescription: 'No data'
},
Timeline: {
unknown: 'Unknown',
statusScheduled: 'Scheduled',
statusPaused: 'Paused',
statusPendingStorage: 'Pending Storage',
statusStored: 'Stored',
statusStarted: 'Started'
}
} }
} }

@ -4486,6 +4486,143 @@ export default {
} }
}
},
ProductionReport: {
Index: {
searchCodeLabel: '任务单编码',
searchCodePlaceholder: '请输入任务单编码',
searchOrderDateLabel: '下达日期',
searchDeliveryDateLabel: '交货日期',
searchRemarkLabel: '备注',
searchRemarkPlaceholder: '请输入备注',
searchCreateTimeLabel: '创建时间',
startDatePlaceholder: '开始日期',
endDatePlaceholder: '结束日期',
buttonSearch: '搜索',
buttonReset: '重置',
buttonExport: '导出',
tabAll: '全部',
tabIssued: '已下达',
tabPartialScheduled: '部分排产',
tabPendingProduction: '待生产',
tabInProduction: '生产中',
tabCompleted: '已完成',
tableCode: '任务单编码',
tableOrderDate: '下达日期',
tableDeliveryDate: '交货日期',
tableStatus: '状态',
tableIsScheduled: '是否排产',
tableProductionProgress: '生产进度',
tableRemark: '备注',
yes: '是',
no: '否',
detailTabBasicInfo: '基础信息',
detailTabRelatedPlan: '关联计划',
detailTabQualityInfo: '质检信息',
detailTabBaogongInfo: '报工信息',
exportFilename: '生产报表'
},
BasicInfo: {
buttonRefresh: '刷新',
tableTaskCode: '任务单编码',
tableProductCode: '产品编码',
tableProductName: '产品名称',
tableTotalNumber: '总数量',
tablePlanNumber: '已计划数量',
tableUnplannedNumber: '未计划数量',
tableOperate: '操作',
buttonViewProductInfo: '查看产品信息'
},
ProductInfo: {
dialogTitle: '产品信息',
labelProductCode: '产品编码',
labelProductName: '产品名称',
labelNumber: '数量',
labelUnit: '单位',
labelSpecification: '规格',
tableMaterialCode: '原料编码',
tableMaterialName: '原料名称',
tableRequiredQuantity: '需求量',
tableUnit: '单位'
},
BaogongInfo: {
dateRangeSeparator: '至',
startDatePlaceholder: '开始日期',
endDatePlaceholder: '结束日期',
buttonToday: '今日',
buttonLast7Days: '近七天',
buttonLast30Days: '近30日',
buttonReset: '重置',
buttonRefresh: '刷新',
tableTaskCode: '任务单编码',
tablePlanCode: '计划编码',
tableEmployeeId: '员工ID',
tableEmployeeName: '员工名称',
tableProductName: '产品名称',
tableProductCode: '产品编码',
tableBaogongNumber: '报工数量',
tablePassNumber: '合格数量',
tableNoPassNumber: '不合格数量',
tablePassRate: '合格率%',
tableNoPassReason: '不合格原因',
tableBaogongTime: '报工时间'
},
QualityInfo: {
buttonRefresh: '刷新',
expandTableName: '检验项名称',
expandTableMethod: '检验方式',
expandTableStandard: '判定基准',
expandTableUnit: '单位',
expandTableUpperLimit: '上限值',
expandTableLowerLimit: '下限值',
expandTableInputValue: '输入值',
expandTableImage: '图片',
expandTablePreview: '预览',
expandTableResult: '检验结果',
expandTableRemark: '备注',
tableCode: '质检编码',
tableCategory: '质检分类',
tableStatus: '状态',
tableTicketCode: '工单编码',
tableProcess: '工序',
tableSchema: '检验方案',
tableManager: '负责人',
tableExecutor: '执行人',
tableExecuteTime: '执行时间',
tableRemark: '备注',
tableResult: '结果',
resultPass: '通过',
resultFail: '不通过',
inspectionPending: '待检测',
inspectionPass: '检测通过',
inspectionFail: '检测不通过',
unknown: '未知'
},
RelatedPlan: {
buttonRefresh: '刷新',
emptyDescription: '暂无关联计划'
},
PlanCard: {
labelProductName: '产品名称',
labelDeviceName: '设备名称',
labelPlanStartTime: '计划开始时间',
labelPlanEndTime: '计划结束时间',
labelLatestStartTime: '最晚开始时间',
statPlanNumber: '计划数量',
statWangongNumber: '完工数量',
statPassNumber: '合格数量',
statNoPassNumber: '不合格数量',
statPassRate: '合格率',
emptyDescription: '暂无数据'
},
Timeline: {
unknown: '未知',
statusScheduled: '已排产',
statusPaused: '暂停',
statusPendingStorage: '待入库',
statusStored: '已入库',
statusStarted: '已开工'
} }
} }
} }

@ -1,26 +1,29 @@
<template> <template>
<el-dialog v-model="dialogVisible" title="产品信息" width="900px" @close="handleClose"> <el-dialog v-model="dialogVisible" :title="t('ProductionReport.ProductInfo.dialogTitle')" width="900px" @close="handleClose">
<el-descriptions :column="5" border class="mb-20px" size="small"> <el-descriptions :column="5" border class="mb-20px" size="small">
<el-descriptions-item label="产品编码">{{ productInfo.barCode }}</el-descriptions-item> <el-descriptions-item :label="t('ProductionReport.ProductInfo.labelProductCode')">{{ productInfo.barCode }}</el-descriptions-item>
<el-descriptions-item label="产品名称">{{ productInfo.productName }}</el-descriptions-item> <el-descriptions-item :label="t('ProductionReport.ProductInfo.labelProductName')">{{ productInfo.productName }}</el-descriptions-item>
<el-descriptions-item label="数量">{{ productInfo.number }}</el-descriptions-item> <el-descriptions-item :label="t('ProductionReport.ProductInfo.labelNumber')">{{ productInfo.number }}</el-descriptions-item>
<el-descriptions-item label="单位">{{ productInfo.unitName }}</el-descriptions-item> <el-descriptions-item :label="t('ProductionReport.ProductInfo.labelUnit')">{{ productInfo.unitName }}</el-descriptions-item>
<el-descriptions-item label="规格">{{ productInfo.standard }}</el-descriptions-item> <el-descriptions-item :label="t('ProductionReport.ProductInfo.labelSpecification')">{{ productInfo.standard }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<el-table v-loading="loading" :data="materialList" :stripe="true" :show-overflow-tooltip="true"> <el-table v-loading="loading" :data="materialList" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="原料编码" align="center" prop="productCode" min-width="130" /> <el-table-column :label="t('ProductionReport.ProductInfo.tableMaterialCode')" align="center" prop="productCode" min-width="130" />
<el-table-column label="原料名称" align="center" prop="productName" min-width="150" /> <el-table-column :label="t('ProductionReport.ProductInfo.tableMaterialName')" align="center" prop="productName" min-width="150" />
<el-table-column label="需求量" align="center" prop="usageNumber" width="100" /> <el-table-column :label="t('ProductionReport.ProductInfo.tableRequiredQuantity')" align="center" prop="usageNumber" width="100" />
<el-table-column label="单位" align="center" prop="unitName" width="80" /> <el-table-column :label="t('ProductionReport.ProductInfo.tableUnit')" align="center" prop="unitName" width="80" />
</el-table> </el-table>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { BomApi } from '@/api/mes/bom' import { BomApi } from '@/api/mes/bom'
import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'ProductInfoDialog' }) defineOptions({ name: 'ProductInfoDialog' })
const { t } = useI18n()
const dialogVisible = ref(false) const dialogVisible = ref(false)
const loading = ref(false) const loading = ref(false)
const materialList = ref<any[]>([]) const materialList = ref<any[]>([])

@ -1,30 +1,29 @@
<template> <template>
<div class="card"> <div class="card">
<!-- 卡片头部基本信息 -->
<div class="card-header"> <div class="card-header">
<div class="header-left"> <div class="header-left">
<div class="plan-id">{{ plan.code || `PLAN${plan.id}` }}</div> <div class="plan-id">{{ plan.code || `PLAN${plan.id}` }}</div>
<div class="plan-basic"> <div class="plan-basic">
<span class="basic-item"> <span class="basic-item">
<span class="label">产品名称:</span> <span class="label">{{ t('ProductionReport.PlanCard.labelProductName') }}:</span>
<span class="value">{{ plan.productName || '-' }}</span> <span class="value">{{ plan.productName || '-' }}</span>
</span> </span>
<span class="basic-item"> <span class="basic-item">
<span class="label">设备名称:</span> <span class="label">{{ t('ProductionReport.PlanCard.labelDeviceName') }}:</span>
<span class="value">{{ plan.deviceName || '-' }}</span> <span class="value">{{ plan.deviceName || '-' }}</span>
</span> </span>
</div> </div>
<div class="plan-basic"> <div class="plan-basic">
<span class="basic-item"> <span class="basic-item">
<span class="label">计划开始时间:</span> <span class="label">{{ t('ProductionReport.PlanCard.labelPlanStartTime') }}:</span>
<span class="value">{{ formatDateTime(plan.planStartTime) }}</span> <span class="value">{{ formatDateTime(plan.planStartTime) }}</span>
</span> </span>
<span class="basic-item"> <span class="basic-item">
<span class="label">计划结束时间:</span> <span class="label">{{ t('ProductionReport.PlanCard.labelPlanEndTime') }}:</span>
<span class="value">{{ formatDateTime(plan.planEndTime) }}</span> <span class="value">{{ formatDateTime(plan.planEndTime) }}</span>
</span> </span>
<span class="basic-item"> <span class="basic-item">
<span class="label">最晚开始时间:</span> <span class="label">{{ t('ProductionReport.PlanCard.labelLatestStartTime') }}:</span>
<span class="value">{{ formatDateTime(plan.latestStartTime) }}</span> <span class="value">{{ formatDateTime(plan.latestStartTime) }}</span>
</span> </span>
</div> </div>
@ -33,34 +32,33 @@
<div class="header-right"> <div class="header-right">
<div class="stat-group"> <div class="stat-group">
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">计划数量</span> <span class="stat-label">{{ t('ProductionReport.PlanCard.statPlanNumber') }}</span>
<span class="stat-value">{{ plan.planNumber || 0 }}</span> <span class="stat-value">{{ plan.planNumber || 0 }}</span>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">完工数量</span> <span class="stat-label">{{ t('ProductionReport.PlanCard.statWangongNumber') }}</span>
<span class="stat-value">{{ plan.wangongNumber || 0 }}</span> <span class="stat-value">{{ plan.wangongNumber || 0 }}</span>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">合格数量</span> <span class="stat-label">{{ t('ProductionReport.PlanCard.statPassNumber') }}</span>
<span class="stat-value">{{ plan.passNumber || 0 }}</span> <span class="stat-value">{{ plan.passNumber || 0 }}</span>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">不合格数量</span> <span class="stat-label">{{ t('ProductionReport.PlanCard.statNoPassNumber') }}</span>
<span class="stat-value">{{ plan.noPassNumber || 0 }}</span> <span class="stat-value">{{ plan.noPassNumber || 0 }}</span>
</div> </div>
</div> </div>
<div class="pass-rate"> <div class="pass-rate">
<span class="rate-label">合格率</span> <span class="rate-label">{{ t('ProductionReport.PlanCard.statPassRate') }}</span>
<span class="rate-value">{{ plan.passRate }}%</span> <span class="rate-value">{{ plan.passRate }}%</span>
</div> </div>
</div> </div>
</div> </div>
<!-- 卡片中部时间线 -->
<div class="card-timeline"> <div class="card-timeline">
<ProductionPlanTimeline v-if="plan.planRecordList && plan.planRecordList.length" :plan-record-list="plan.planRecordList" :items-per-row="8" /> <ProductionPlanTimeline v-if="plan.planRecordList && plan.planRecordList.length" :plan-record-list="plan.planRecordList" :items-per-row="8" />
<el-empty v-else description="暂无数据" :image-size="60" /> <el-empty v-else :description="t('ProductionReport.PlanCard.emptyDescription')" :image-size="60" />
</div> </div>
</div> </div>
</template> </template>
@ -69,16 +67,18 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import type { PlanVO } from '@/api/mes/plan' import type { PlanVO } from '@/api/mes/plan'
import ProductionPlanTimeline from './ProductionPlanTimeline.vue' import ProductionPlanTimeline from './ProductionPlanTimeline.vue'
import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'ProductionPlanCard' }) defineOptions({ name: 'ProductionPlanCard' })
const { t } = useI18n()
interface Props { interface Props {
plan: PlanVO plan: PlanVO
} }
defineProps<Props>() defineProps<Props>()
//
const formatDateTime = (value: string | number | Date | null | undefined): string => { const formatDateTime = (value: string | number | Date | null | undefined): string => {
if (!value) return '-' if (!value) return '-'
return dayjs(value).format('YYYY-MM-DD HH:mm:ss') return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
@ -104,7 +104,6 @@ export default {}
} }
} }
//
.card-header { .card-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -204,14 +203,12 @@ export default {}
} }
} }
// 线
.card-timeline { .card-timeline {
padding: 16px 20px; padding: 16px 20px;
background: #fafafa; background: #fafafa;
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
} }
//
@media (max-width: 1400px) { @media (max-width: 1400px) {
.header-right { .header-right {
flex-wrap: wrap; flex-wrap: wrap;

@ -1,6 +1,5 @@
<template> <template>
<div class="timeline-wrapper" ref="wrapperRef"> <div class="timeline-wrapper" ref="wrapperRef">
<!-- 时间线节点 -->
<div class="timeline-container"> <div class="timeline-container">
<div <div
v-for="(record, index) in sortedRecords" v-for="(record, index) in sortedRecords"
@ -8,19 +7,16 @@
class="timeline-node" class="timeline-node"
:style="getNodeStyle(index)" :style="getNodeStyle(index)"
> >
<!-- 节点圆圈 -->
<div class="node-dot" :class="`status-${record.operateStatus}`"> <div class="node-dot" :class="`status-${record.operateStatus}`">
<div class="node-number">{{ index + 1 }}</div> <div class="node-number">{{ index + 1 }}</div>
</div> </div>
<!-- 节点标签 -->
<div class="node-label"> <div class="node-label">
<div class="status-badge">{{ getStatusLabel(record.operateStatus) }}</div> <div class="status-badge">{{ getStatusLabel(record.operateStatus) }}</div>
<div class="node-time">{{ formatTime(record.operateTime) }}</div> <div class="node-time">{{ formatTime(record.operateTime) }}</div>
</div> </div>
</div> </div>
<!-- SVG 连接线 -->
<svg <svg
v-if="sortedRecords.length > 1" v-if="sortedRecords.length > 1"
class="timeline-svg" class="timeline-svg"
@ -45,13 +41,15 @@
<script setup lang="ts"> <script setup lang="ts">
import dayjs from 'dayjs' import dayjs from 'dayjs'
import type { PlanRecordVO } from '@/api/mes/plan' import type { PlanRecordVO } from '@/api/mes/plan'
import { OPERATE_STATUS_MAP } from '@/api/mes/plan' import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'ProductionPlanTimeline' }) defineOptions({ name: 'ProductionPlanTimeline' })
const { t } = useI18n()
interface Props { interface Props {
planRecordList?: PlanRecordVO[] planRecordList?: PlanRecordVO[]
itemsPerRow?: number // 6 itemsPerRow?: number
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@ -61,14 +59,20 @@ const props = withDefaults(defineProps<Props>(), {
const wrapperRef = ref<HTMLElement>() const wrapperRef = ref<HTMLElement>()
// const NODE_SIZE = 50
const NODE_SIZE = 50 // const NODE_SPACING_X = 140
const NODE_SPACING_X = 140 // const NODE_SPACING_Y = 240
const NODE_SPACING_Y = 240 // const PADDING = 20
const PADDING = 20 // const LABEL_HEIGHT = 60
const LABEL_HEIGHT = 60 //
const OPERATE_STATUS_I18N_MAP: Record<string, string> = {
'1': 'ProductionReport.Timeline.statusScheduled',
'3': 'ProductionReport.Timeline.statusPaused',
'4': 'ProductionReport.Timeline.statusPendingStorage',
'5': 'ProductionReport.Timeline.statusStored',
'8': 'ProductionReport.Timeline.statusStarted'
}
//
const sortedRecords = computed(() => { const sortedRecords = computed(() => {
if (!props.planRecordList || props.planRecordList.length === 0) { if (!props.planRecordList || props.planRecordList.length === 0) {
return [] return []
@ -76,13 +80,11 @@ const sortedRecords = computed(() => {
return [...props.planRecordList].sort((a, b) => a.operateTime - b.operateTime) return [...props.planRecordList].sort((a, b) => a.operateTime - b.operateTime)
}) })
//
const rows = computed(() => { const rows = computed(() => {
if (sortedRecords.value.length === 0) return 1 if (sortedRecords.value.length === 0) return 1
return Math.ceil(sortedRecords.value.length / props.itemsPerRow) return Math.ceil(sortedRecords.value.length / props.itemsPerRow)
}) })
// SVG viewBox
const containerWidth = computed(() => { const containerWidth = computed(() => {
const nodesInRow = Math.min(sortedRecords.value.length, props.itemsPerRow) const nodesInRow = Math.min(sortedRecords.value.length, props.itemsPerRow)
return nodesInRow * NODE_SPACING_X + PADDING * 2 return nodesInRow * NODE_SPACING_X + PADDING * 2
@ -92,7 +94,6 @@ const containerHeight = computed(() => {
return rows.value * NODE_SPACING_Y + PADDING * 2 + LABEL_HEIGHT return rows.value * NODE_SPACING_Y + PADDING * 2 + LABEL_HEIGHT
}) })
//
const getNodePosition = (index: number): { x: number; y: number } => { const getNodePosition = (index: number): { x: number; y: number } => {
const row = Math.floor(index / props.itemsPerRow) const row = Math.floor(index / props.itemsPerRow)
const col = index % props.itemsPerRow const col = index % props.itemsPerRow
@ -102,7 +103,6 @@ const getNodePosition = (index: number): { x: number; y: number } => {
} }
} }
//
const getNodeStyle = (index: number): Record<string, string> => { const getNodeStyle = (index: number): Record<string, string> => {
const virtualPos = getNodePosition(index) const virtualPos = getNodePosition(index)
const percentX = (virtualPos.x / containerWidth.value) * 100 const percentX = (virtualPos.x / containerWidth.value) * 100
@ -117,10 +117,9 @@ const getNodeStyle = (index: number): Record<string, string> => {
} }
} }
// 线SVG path 使线
const pathLines = computed(() => { const pathLines = computed(() => {
const paths: string[] = [] const paths: string[] = []
const radius = 30 // const radius = 30
for (let i = 0; i < sortedRecords.value.length - 1; i++) { for (let i = 0; i < sortedRecords.value.length - 1; i++) {
const currentPos = getNodePosition(i) const currentPos = getNodePosition(i)
@ -129,14 +128,12 @@ const pathLines = computed(() => {
const nextRow = Math.floor((i + 1) / props.itemsPerRow) const nextRow = Math.floor((i + 1) / props.itemsPerRow)
if (currentRow === nextRow) { if (currentRow === nextRow) {
//
const startX = currentPos.x + NODE_SIZE / 2 const startX = currentPos.x + NODE_SIZE / 2
const startY = currentPos.y const startY = currentPos.y
const endX = nextPos.x - NODE_SIZE / 2 const endX = nextPos.x - NODE_SIZE / 2
const endY = nextPos.y const endY = nextPos.y
paths.push(`M ${startX} ${startY} L ${endX} ${endY}`) paths.push(`M ${startX} ${startY} L ${endX} ${endY}`)
} else { } else {
// 使线
const startX = currentPos.x + NODE_SIZE / 2 const startX = currentPos.x + NODE_SIZE / 2
const startY = currentPos.y const startY = currentPos.y
@ -148,11 +145,7 @@ const pathLines = computed(() => {
const isLastCol = currentCol === props.itemsPerRow - 1 const isLastCol = currentCol === props.itemsPerRow - 1
if (isLastCol) { if (isLastCol) {
// 使线
const rightX = currentPos.x + 80 const rightX = currentPos.x + 80
// M:
// L: 线
// Q: 线
paths.push( paths.push(
`M ${startX} ${startY} ` + `M ${startX} ${startY} ` +
`L ${rightX - radius} ${startY} ` + `L ${rightX - radius} ${startY} ` +
@ -165,7 +158,6 @@ const pathLines = computed(() => {
` `
) )
} else { } else {
//
paths.push( paths.push(
`M ${startX} ${startY} ` + `M ${startX} ${startY} ` +
`L ${startX + radius} ${startY} ` + `L ${startX + radius} ${startY} ` +
@ -182,21 +174,18 @@ const pathLines = computed(() => {
return paths return paths
}) })
//
const getStatusLabel = (status: string): string => { const getStatusLabel = (status: string): string => {
return OPERATE_STATUS_MAP[status] || '未知' const i18nKey = OPERATE_STATUS_I18N_MAP[status]
return i18nKey ? t(i18nKey) : t('ProductionReport.Timeline.unknown')
} }
//
const formatTime = (timestamp: number): string => { const formatTime = (timestamp: number): string => {
if (!timestamp) return '-' if (!timestamp) return '-'
return dayjs(timestamp).format('YYYY-MM-DD\nHH:mm:ss') return dayjs(timestamp).format('YYYY-MM-DD\nHH:mm:ss')
} }
//
onMounted(() => { onMounted(() => {
if (wrapperRef.value) { if (wrapperRef.value) {
//
} }
}) })
</script> </script>
@ -221,7 +210,6 @@ export default {}
min-height: v-bind('`${containerHeight}px`'); min-height: v-bind('`${containerHeight}px`');
} }
// SVG
.timeline-svg { .timeline-svg {
position: absolute; position: absolute;
top: 0; top: 0;
@ -231,7 +219,6 @@ export default {}
pointer-events: none; pointer-events: none;
} }
// 线
.timeline-node { .timeline-node {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -239,7 +226,6 @@ export default {}
gap: 8px; gap: 8px;
} }
//
.node-dot { .node-dot {
width: 50px; width: 50px;
height: 50px; height: 50px;
@ -257,23 +243,23 @@ export default {}
z-index: 2; z-index: 2;
&.status-1 { &.status-1 {
background: #409eff; // - background: #409eff;
} }
&.status-3 { &.status-3 {
background: #e6a23c; // - background: #e6a23c;
} }
&.status-4 { &.status-4 {
background: #f56c6c; // - background: #f56c6c;
} }
&.status-5 { &.status-5 {
background: #67c23a; // - 绿 background: #67c23a;
} }
&.status-8 { &.status-8 {
background: #409eff; // - background: #409eff;
} }
} }
@ -282,7 +268,6 @@ export default {}
line-height: 1; line-height: 1;
} }
//
.node-label { .node-label {
text-align: center; text-align: center;
width: 130px; width: 130px;
@ -308,7 +293,6 @@ export default {}
white-space: pre-line; white-space: pre-line;
} }
//
@media (max-width: 1200px) { @media (max-width: 1200px) {
.timeline-wrapper { .timeline-wrapper {
overflow-x: auto; overflow-x: auto;

@ -5,48 +5,47 @@
<el-date-picker <el-date-picker
v-model="baogongDateRange" v-model="baogongDateRange"
type="daterange" type="daterange"
range-separator="" :range-separator="t('ProductionReport.BaogongInfo.dateRangeSeparator')"
start-placeholder="开始日期" :start-placeholder="t('ProductionReport.BaogongInfo.startDatePlaceholder')"
end-placeholder="结束日期" :end-placeholder="t('ProductionReport.BaogongInfo.endDatePlaceholder')"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
:default-time="[new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]" :default-time="[new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]"
@change="handleDateChange" @change="handleDateChange"
class="!w-280px" class="!w-280px"
/> />
<el-button :type="activeShortcut === 'today' ? 'primary' : ''" @click="setShortcut('today')"></el-button> <el-button :type="activeShortcut === 'today' ? 'primary' : ''" @click="setShortcut('today')">{{ t('ProductionReport.BaogongInfo.buttonToday') }}</el-button>
<el-button :type="activeShortcut === '7d' ? 'primary' : ''" @click="setShortcut('7d')"></el-button> <el-button :type="activeShortcut === '7d' ? 'primary' : ''" @click="setShortcut('7d')">{{ t('ProductionReport.BaogongInfo.buttonLast7Days') }}</el-button>
<el-button :type="activeShortcut === '30d' ? 'primary' : ''" @click="setShortcut('30d')">30</el-button> <el-button :type="activeShortcut === '30d' ? 'primary' : ''" @click="setShortcut('30d')">{{ t('ProductionReport.BaogongInfo.buttonLast30Days') }}</el-button>
<el-button @click="handleReset"> <el-button @click="handleReset">
<Icon icon="ep:refresh-left" class="mr-5px" /> 重置 <Icon icon="ep:refresh-left" class="mr-5px" /> {{ t('ProductionReport.BaogongInfo.buttonReset') }}
</el-button> </el-button>
</div> </div>
<el-button @click="handleRefresh" :loading="loading"> <el-button @click="handleRefresh" :loading="loading">
<Icon icon="ep:refresh" class="mr-5px" /> 刷新 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('ProductionReport.BaogongInfo.buttonRefresh') }}
</el-button> </el-button>
</div> </div>
<el-table v-loading="loading" :data="baogongList" :stripe="true" :show-overflow-tooltip="true"> <el-table v-loading="loading" :data="baogongList" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="任务单编码" align="center" prop="taskCode" width="150px" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tableTaskCode')" align="center" prop="taskCode" width="150px" />
<el-table-column label="计划编码" align="center" prop="planCode" width="150px" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tablePlanCode')" align="center" prop="planCode" width="150px" />
<el-table-column label="员工ID" align="center" prop="employeeId" width="120px" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tableEmployeeId')" align="center" prop="employeeId" width="120px" />
<el-table-column label="员工名称" align="center" prop="employeeName" width="120px" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tableEmployeeName')" align="center" prop="employeeName" width="120px" />
<el-table-column label="产品名称" align="center" prop="productName" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tableProductName')" align="center" prop="productName" />
<el-table-column label="产品编码" align="center" prop="productCode" width="120px" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tableProductCode')" align="center" prop="productCode" width="120px" />
<el-table-column label="报工数量" align="center" prop="baogongNum" width="100px" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tableBaogongNumber')" align="center" prop="baogongNum" width="100px" />
<el-table-column label="合格数量" align="center" prop="passNum" width="100px" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tablePassNumber')" align="center" prop="passNum" width="100px" />
<el-table-column label="不合格数量" align="center" prop="noPassNum" width="100px" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tableNoPassNumber')" align="center" prop="noPassNum" width="100px" />
<el-table-column label="合格率%" align="center" width="100px"> <el-table-column :label="t('ProductionReport.BaogongInfo.tablePassRate')" align="center" width="100px">
<template #default="scope"> <template #default="scope">
{{ scope.row.passRate }} {{ scope.row.passRate }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="不合格原因" align="center" prop="reason" /> <el-table-column :label="t('ProductionReport.BaogongInfo.tableNoPassReason')" align="center" prop="reason" />
<el-table-column label="报工时间" align="center" prop="baogongTime" width="180px"> <el-table-column :label="t('ProductionReport.BaogongInfo.tableBaogongTime')" align="center" prop="baogongTime" width="180px">
<template #default="scope"> <template #default="scope">
{{ formatDateTime(scope.row.baogongTime) }} {{ formatDateTime(scope.row.baogongTime) }}
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页 -->
<Pagination <Pagination
:total="total" :total="total"
v-model:page="queryParams.pageNo" v-model:page="queryParams.pageNo"
@ -59,9 +58,12 @@
<script setup lang="ts"> <script setup lang="ts">
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { BaogongRecordApi } from '@/api/mes/baogongrecord' import { BaogongRecordApi } from '@/api/mes/baogongrecord'
import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'ProductionReportBaogongInfo' }) defineOptions({ name: 'ProductionReportBaogongInfo' })
const { t } = useI18n()
const props = defineProps<{ const props = defineProps<{
taskId?: number taskId?: number
}>() }>()
@ -95,7 +97,6 @@ const getList = async () => {
} }
} }
/** 监听任务ID变化 */
watch( watch(
() => props.taskId, () => props.taskId,
(val: number | undefined) => { (val: number | undefined) => {
@ -158,7 +159,6 @@ const handleReset = () => {
handleRefresh() handleRefresh()
} }
/** 格式化日期时间 */
const formatDateTime = (value: string | Date | null) => { const formatDateTime = (value: string | Date | null) => {
if (!value) return '-' if (!value) return '-'
return dayjs(value).format('YYYY-MM-DD HH:mm:ss') return dayjs(value).format('YYYY-MM-DD HH:mm:ss')

@ -2,33 +2,31 @@
<div class="basic-info-container"> <div class="basic-info-container">
<div class="action-bar"> <div class="action-bar">
<el-button @click="handleRefresh" :loading="loading"> <el-button @click="handleRefresh" :loading="loading">
<Icon icon="ep:refresh" class="mr-5px" /> 刷新 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('ProductionReport.BasicInfo.buttonRefresh') }}
</el-button> </el-button>
</div> </div>
<!-- 任务单信息 -->
<el-table v-loading="loading" :data="taskDetailList" :stripe="true" :show-overflow-tooltip="true"> <el-table v-loading="loading" :data="taskDetailList" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="任务单编码" align="center" prop="taskCode" sortable /> <el-table-column :label="t('ProductionReport.BasicInfo.tableTaskCode')" align="center" prop="taskCode" sortable />
<el-table-column label="产品编码" align="center" prop="barCode" sortable /> <el-table-column :label="t('ProductionReport.BasicInfo.tableProductCode')" align="center" prop="barCode" sortable />
<el-table-column label="产品名称" align="center" prop="productName" sortable /> <el-table-column :label="t('ProductionReport.BasicInfo.tableProductName')" align="center" prop="productName" sortable />
<el-table-column label="总数量" align="center" prop="number" /> <el-table-column :label="t('ProductionReport.BasicInfo.tableTotalNumber')" align="center" prop="number" />
<el-table-column label="已计划数量" align="center" prop="planNumber" /> <el-table-column :label="t('ProductionReport.BasicInfo.tablePlanNumber')" align="center" prop="planNumber" />
<el-table-column label="未计划数量" align="center"> <el-table-column :label="t('ProductionReport.BasicInfo.tableUnplannedNumber')" align="center">
<template #default="scope"> <template #default="scope">
<span style="color: #e66126"> <span style="color: #e66126">
{{ scope.row.number - scope.row.planNumber > 0 ? scope.row.number - scope.row.planNumber : 0 }} {{ scope.row.number - scope.row.planNumber > 0 ? scope.row.number - scope.row.planNumber : 0 }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="200px"> <el-table-column :label="t('ProductionReport.BasicInfo.tableOperate')" align="center" width="200px">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="openProductInfo(scope.row)"> <el-button link type="primary" @click="openProductInfo(scope.row)">
查看产品信息 {{ t('ProductionReport.BasicInfo.buttonViewProductInfo') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 产品信息弹框 -->
<ProductInfoDialog ref="productInfoDialogRef" /> <ProductInfoDialog ref="productInfoDialogRef" />
</div> </div>
</template> </template>
@ -36,11 +34,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { TaskApi } from '@/api/mes/task' import { TaskApi } from '@/api/mes/task'
import ProductInfoDialog from './ProductInfoDialog.vue' import ProductInfoDialog from './ProductInfoDialog.vue'
import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'ProductionReportBasicInfo' }) defineOptions({ name: 'ProductionReportBasicInfo' })
const { t } = useI18n()
const props = defineProps<{ const props = defineProps<{
taskId?: number // task ID taskId?: number
taskDeliveryDate?: string | number | Date taskDeliveryDate?: string | number | Date
}>() }>()
@ -48,7 +49,6 @@ const productInfoDialogRef = ref()
const loading = ref(false) const loading = ref(false)
const taskDetailList = ref<any[]>([]) const taskDetailList = ref<any[]>([])
/** 查询任务单清单 */
const getTaskDetailList = async (taskId: number) => { const getTaskDetailList = async (taskId: number) => {
loading.value = true loading.value = true
try { try {
@ -62,7 +62,6 @@ const getTaskDetailList = async (taskId: number) => {
} }
} }
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch( watch(
() => props.taskId, () => props.taskId,
(val: number) => { (val: number) => {
@ -75,7 +74,6 @@ watch(
{ immediate: true, deep: true } { immediate: true, deep: true }
) )
/** 打开产品信息对话框 */
const openProductInfo = (row: any) => { const openProductInfo = (row: any) => {
productInfoDialogRef.value?.open(row) productInfoDialogRef.value?.open(row)
} }

@ -2,7 +2,7 @@
<div class="quality-info-container"> <div class="quality-info-container">
<div class="action-bar"> <div class="action-bar">
<el-button @click="handleRefresh" :loading="loading"> <el-button @click="handleRefresh" :loading="loading">
<Icon icon="ep:refresh" class="mr-5px" /> 刷新 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('ProductionReport.QualityInfo.buttonRefresh') }}
</el-button> </el-button>
</div> </div>
<el-table v-loading="loading" :data="qualityList" :stripe="true" :show-overflow-tooltip="true" @expand-change="handleExpandChange"> <el-table v-loading="loading" :data="qualityList" :stripe="true" :show-overflow-tooltip="true" @expand-change="handleExpandChange">
@ -10,66 +10,65 @@
<template #default="scope"> <template #default="scope">
<div class="expand-content"> <div class="expand-content">
<el-table :data="scope.row.results" size="small" :show-header="true"> <el-table :data="scope.row.results" size="small" :show-header="true">
<el-table-column label="检验项名称" align="center" prop="name" min-width="150px" /> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableName')" align="center" prop="name" min-width="150px" />
<el-table-column label="检验方式" align="center" prop="tool" min-width="100px" /> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableMethod')" align="center" prop="tool" min-width="100px" />
<el-table-column label="判定基准" align="center" prop="zjTypeName" min-width="100px" /> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableStandard')" align="center" prop="zjTypeName" min-width="100px" />
<el-table-column label="单位" align="center" prop="unitName" min-width="80px" /> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableUnit')" align="center" prop="unitName" min-width="80px" />
<el-table-column label="上限值" align="center" prop="upperVal" min-width="80px" /> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableUpperLimit')" align="center" prop="upperVal" min-width="80px" />
<el-table-column label="下限值" align="center" prop="lowerVal" min-width="80px" /> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableLowerLimit')" align="center" prop="lowerVal" min-width="80px" />
<el-table-column label="输入值" align="center" prop="textInput" min-width="100px" /> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableInputValue')" align="center" prop="textInput" min-width="100px" />
<el-table-column label="图片" align="center" prop="images" min-width="80px"> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableImage')" align="center" prop="images" min-width="80px">
<template #default="scope"> <template #default="scope">
<el-button v-if="scope.row.images" link type="primary" @click="handlePreviewImage(scope.row.images)"></el-button> <el-button v-if="scope.row.images" link type="primary" @click="handlePreviewImage(scope.row.images)">{{ t('ProductionReport.QualityInfo.expandTablePreview') }}</el-button>
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="检验结果" align="center" prop="zjResult" min-width="100px"> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableResult')" align="center" prop="zjResult" min-width="100px">
<template #default="scope"> <template #default="scope">
<el-tag :type="scope.row.zjResult === 1 ? 'success' : scope.row.zjResult === 2 ? 'danger' : 'info'"> <el-tag :type="scope.row.zjResult === 1 ? 'success' : scope.row.zjResult === 2 ? 'danger' : 'info'">
{{ getResultLabel(scope.row.zjResult) }} {{ getResultLabel(scope.row.zjResult) }}
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column :label="t('ProductionReport.QualityInfo.expandTableRemark')" align="center" prop="remark" />
</el-table> </el-table>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="质检编码" align="center" prop="code" min-width="140px" /> <el-table-column :label="t('ProductionReport.QualityInfo.tableCode')" align="center" prop="code" min-width="140px" />
<el-table-column label="质检分类" align="center" prop="type" min-width="100px"> <el-table-column :label="t('ProductionReport.QualityInfo.tableCategory')" align="center" prop="type" min-width="100px">
<template #default="scope"> <template #default="scope">
<DictTag type="mes_zj_task_type" :value="scope.row.type" /> <DictTag type="mes_zj_task_type" :value="scope.row.type" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="状态" align="center" prop="status" min-width="80px"> <el-table-column :label="t('ProductionReport.QualityInfo.tableStatus')" align="center" prop="status" min-width="80px">
<template #default="scope"> <template #default="scope">
<DictTag :type="'job_status'" :value="scope.row.status" /> <DictTag :type="'job_status'" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="工单编码" align="center" prop="ticketCode" min-width="200px" /> <el-table-column :label="t('ProductionReport.QualityInfo.tableTicketCode')" align="center" prop="ticketCode" min-width="200px" />
<el-table-column label="工序" align="center" prop="orgType" min-width="80px"> <el-table-column :label="t('ProductionReport.QualityInfo.tableProcess')" align="center" prop="orgType" min-width="80px">
<template #default="scope"> <template #default="scope">
<DictTag :type="DICT_TYPE.MES_ORG_TYPE" :value="scope.row.orgType" /> <DictTag :type="DICT_TYPE.MES_ORG_TYPE" :value="scope.row.orgType" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="检验方案" align="center" prop="schemaName" min-width="140px" /> <el-table-column :label="t('ProductionReport.QualityInfo.tableSchema')" align="center" prop="schemaName" min-width="140px" />
<el-table-column label="负责人" align="center" prop="managerName" min-width="100px" /> <el-table-column :label="t('ProductionReport.QualityInfo.tableManager')" align="center" prop="managerName" min-width="100px" />
<el-table-column label="执行人" align="center" prop="executorName" min-width="100px" /> <el-table-column :label="t('ProductionReport.QualityInfo.tableExecutor')" align="center" prop="executorName" min-width="100px" />
<el-table-column label="执行时间" align="center" prop="executeTime" min-width="180px"> <el-table-column :label="t('ProductionReport.QualityInfo.tableExecuteTime')" align="center" prop="executeTime" min-width="180px">
<template #default="scope"> <template #default="scope">
{{ formatDateTime(scope.row.executeTime) }} {{ formatDateTime(scope.row.executeTime) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" min-width="150px" /> <el-table-column :label="t('ProductionReport.QualityInfo.tableRemark')" align="center" prop="remark" min-width="150px" />
<el-table-column label="结果" align="center" prop="result" min-width="100px"> <el-table-column :label="t('ProductionReport.QualityInfo.tableResult')" align="center" prop="result" min-width="100px">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.result === '1' || scope.row.result === 1" type="success"></el-tag> <el-tag v-if="scope.row.result === '1' || scope.row.result === 1" type="success">{{ t('ProductionReport.QualityInfo.resultPass') }}</el-tag>
<el-tag v-else-if="scope.row.result === '2' || scope.row.result === 2" type="danger">不通过</el-tag> <el-tag v-else-if="scope.row.result === '2' || scope.row.result === 2" type="danger">{{ t('ProductionReport.QualityInfo.resultFail') }}</el-tag>
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页 -->
<Pagination <Pagination
:total="total" :total="total"
v-model:page="queryParams.pageNo" v-model:page="queryParams.pageNo"
@ -84,9 +83,12 @@ import dayjs from 'dayjs'
import { ZjTaskApi } from '@/api/mes/zjtask' import { ZjTaskApi } from '@/api/mes/zjtask'
import { DictTag } from '@/components/DictTag' import { DictTag } from '@/components/DictTag'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'ProductionReportQualityInfo' }) defineOptions({ name: 'ProductionReportQualityInfo' })
const { t } = useI18n()
interface ZjTaskResult { interface ZjTaskResult {
id: number id: number
zjType: number zjType: number
@ -99,7 +101,7 @@ interface ZjTaskResult {
unitName?: string unitName?: string
upperVal?: number upperVal?: number
lowerVal?: number lowerVal?: number
zjResult: number // 0- 1- 2- zjResult: number
images?: string images?: string
zjTime?: string zjTime?: string
taskId: number taskId: number
@ -162,7 +164,6 @@ const getList = async () => {
} }
} }
/** 监听任务ID变化 */
watch( watch(
() => props.taskId, () => props.taskId,
(val: number | undefined) => { (val: number | undefined) => {
@ -182,26 +183,22 @@ const handleRefresh = () => {
getList() getList()
} }
const handleExpandChange = (row: ZjTaskWithResults, expandedRows: any[]) => { const handleExpandChange = (_row: ZjTaskWithResults, _expandedRows: any[]) => {
//
} }
const handlePreviewImage = (imagePath: string) => { const handlePreviewImage = (imagePath: string) => {
//
window.open(imagePath, '_blank') window.open(imagePath, '_blank')
} }
/** 获取检验结果标签 */
const getResultLabel = (result: number): string => { const getResultLabel = (result: number): string => {
const labelMap: Record<number, string> = { const labelMap: Record<number, string> = {
0: '待检测', 0: t('ProductionReport.QualityInfo.inspectionPending'),
1: '检测通过', 1: t('ProductionReport.QualityInfo.inspectionPass'),
2: '检测不通过' 2: t('ProductionReport.QualityInfo.inspectionFail')
} }
return labelMap[result] || '未知' return labelMap[result] || t('ProductionReport.QualityInfo.unknown')
} }
/** 格式化日期时间 */
const formatDateTime = (value: string | Date | null) => { const formatDateTime = (value: string | Date | null) => {
if (!value) return '-' if (!value) return '-'
return dayjs(value).format('YYYY-MM-DD HH:mm:ss') return dayjs(value).format('YYYY-MM-DD HH:mm:ss')

@ -2,19 +2,16 @@
<div class="related-plan-container"> <div class="related-plan-container">
<div class="action-bar"> <div class="action-bar">
<el-button @click="handleRefresh" :loading="loading"> <el-button @click="handleRefresh" :loading="loading">
<Icon icon="ep:refresh" class="mr-5px" /> 刷新 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('ProductionReport.RelatedPlan.buttonRefresh') }}
</el-button> </el-button>
</div> </div>
<!-- 加载中状态 -->
<div v-if="loading" class="loading-wrapper"> <div v-if="loading" class="loading-wrapper">
<el-skeleton :rows="3" animated /> <el-skeleton :rows="3" animated />
</div> </div>
<!-- 空状态 --> <el-empty v-else-if="planList.length === 0" :description="t('ProductionReport.RelatedPlan.emptyDescription')" />
<el-empty v-else-if="planList.length === 0" description="暂无关联计划" />
<!-- 卡片列表 -->
<div v-else class="card-list"> <div v-else class="card-list">
<ProductionPlanCard <ProductionPlanCard
v-for="plan in planList" v-for="plan in planList"
@ -23,7 +20,6 @@
/> />
</div> </div>
<!-- 分页 -->
<Pagination <Pagination
v-if="total > 0" v-if="total > 0"
:total="total" :total="total"
@ -37,9 +33,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { PlanApi, type PlanVO } from '@/api/mes/plan' import { PlanApi, type PlanVO } from '@/api/mes/plan'
import ProductionPlanCard from './ProductionPlanCard.vue' import ProductionPlanCard from './ProductionPlanCard.vue'
import { useI18n } from '@/hooks/web/useI18n'
defineOptions({ name: 'ProductionReportRelatedPlan' }) defineOptions({ name: 'ProductionReportRelatedPlan' })
const { t } = useI18n()
const props = defineProps<{ const props = defineProps<{
taskId?: number taskId?: number
}>() }>()
@ -53,7 +52,6 @@ const queryParams = reactive({
pageSize: 10 pageSize: 10
}) })
//
const getList = async () => { const getList = async () => {
if (!queryParams.taskId) return if (!queryParams.taskId) return
loading.value = true loading.value = true
@ -74,7 +72,6 @@ const getList = async () => {
} }
} }
// ID
watch( watch(
() => props.taskId, () => props.taskId,
(val: number | undefined) => { (val: number | undefined) => {

@ -1,6 +1,5 @@
<template> <template>
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 -->
<el-form <el-form
class="-mb-15px" class="-mb-15px"
:model="queryParams" :model="queryParams"
@ -9,84 +8,82 @@
label-width="auto" label-width="auto"
label-position="left" label-position="left"
> >
<el-form-item label="任务单编码" prop="code"> <el-form-item :label="t('ProductionReport.Index.searchCodeLabel')" prop="code">
<el-input <el-input
v-model="queryParams.code" v-model="queryParams.code"
placeholder="请输入任务单编码" :placeholder="t('ProductionReport.Index.searchCodePlaceholder')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="下达日期" prop="orderDate"> <el-form-item :label="t('ProductionReport.Index.searchOrderDateLabel')" prop="orderDate">
<el-date-picker <el-date-picker
v-model="queryParams.orderDate" v-model="queryParams.orderDate"
@change="handleQuery" @change="handleQuery"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
type="daterange" type="daterange"
start-placeholder="开始日期" :start-placeholder="t('ProductionReport.Index.startDatePlaceholder')"
end-placeholder="结束日期" :end-placeholder="t('ProductionReport.Index.endDatePlaceholder')"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="交货日期" prop="deliveryDate"> <el-form-item :label="t('ProductionReport.Index.searchDeliveryDateLabel')" prop="deliveryDate">
<el-date-picker <el-date-picker
v-model="queryParams.deliveryDate" v-model="queryParams.deliveryDate"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
@change="handleQuery" @change="handleQuery"
type="daterange" type="daterange"
start-placeholder="开始日期" :start-placeholder="t('ProductionReport.Index.startDatePlaceholder')"
end-placeholder="结束日期" :end-placeholder="t('ProductionReport.Index.endDatePlaceholder')"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item :label="t('ProductionReport.Index.searchRemarkLabel')" prop="remark">
<el-input <el-input
v-model="queryParams.remark" v-model="queryParams.remark"
placeholder="请输入备注" :placeholder="t('ProductionReport.Index.searchRemarkPlaceholder')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="创建时间" prop="createTime"> <el-form-item :label="t('ProductionReport.Index.searchCreateTimeLabel')" prop="createTime">
<el-date-picker <el-date-picker
v-model="queryParams.createTime" v-model="queryParams.createTime"
@change="handleQuery" @change="handleQuery"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
type="daterange" type="daterange"
start-placeholder="开始日期" :start-placeholder="t('ProductionReport.Index.startDatePlaceholder')"
end-placeholder="结束日期" :end-placeholder="t('ProductionReport.Index.endDatePlaceholder')"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" /> {{ t('ProductionReport.Index.buttonSearch') }}
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('ProductionReport.Index.buttonReset') }}
</el-button> </el-button>
<el-button type="success" plain @click="handleExport" :loading="exportLoading"> <el-button type="success" plain @click="handleExport" :loading="exportLoading">
<Icon icon="ep:download" class="mr-5px" /> 导出 <Icon icon="ep:download" class="mr-5px" /> {{ t('ProductionReport.Index.buttonExport') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
<!-- 列表 -->
<ContentWrap> <ContentWrap>
<!-- 状态Tabs -->
<el-tabs v-model="activeStatusTab" @tab-click="handleTabClick"> <el-tabs v-model="activeStatusTab" @tab-click="handleTabClick">
<el-tab-pane label="全部" name="" /> <el-tab-pane :label="t('ProductionReport.Index.tabAll')" name="" />
<el-tab-pane label="已下达" name="2" /> <el-tab-pane :label="t('ProductionReport.Index.tabIssued')" name="2" />
<el-tab-pane label="部分排产" name="7" /> <el-tab-pane :label="t('ProductionReport.Index.tabPartialScheduled')" name="7" />
<el-tab-pane label="待生产" name="8" /> <el-tab-pane :label="t('ProductionReport.Index.tabPendingProduction')" name="8" />
<el-tab-pane label="生产中" name="9" /> <el-tab-pane :label="t('ProductionReport.Index.tabInProduction')" name="9" />
<el-tab-pane label="已完成" name="10" /> <el-tab-pane :label="t('ProductionReport.Index.tabCompleted')" name="10" />
</el-tabs> </el-tabs>
<el-table <el-table
v-loading="loading" v-loading="loading"
@ -96,26 +93,26 @@
highlight-current-row highlight-current-row
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
> >
<el-table-column label="任务单编码" align="center" prop="code" width="200px" sortable /> <el-table-column :label="t('ProductionReport.Index.tableCode')" align="center" prop="code" width="200px" sortable />
<el-table-column label="下达日期" align="center" prop="orderDate" :formatter="dateFormatter2" sortable /> <el-table-column :label="t('ProductionReport.Index.tableOrderDate')" align="center" prop="orderDate" :formatter="dateFormatter2" sortable />
<el-table-column <el-table-column
label="交货日期" :label="t('ProductionReport.Index.tableDeliveryDate')"
align="center" align="center"
prop="deliveryDate" prop="deliveryDate"
:formatter="deliveryDateFormatter" :formatter="deliveryDateFormatter"
sortable sortable
/> />
<el-table-column label="状态" align="center" prop="status" sortable> <el-table-column :label="t('ProductionReport.Index.tableStatus')" align="center" prop="status" sortable>
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.MES_TASK_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.MES_TASK_STATUS" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="是否排产" align="center"> <el-table-column :label="t('ProductionReport.Index.tableIsScheduled')" align="center">
<template #default="scope"> <template #default="scope">
<el-tag :type="scope.row.isScheduled ? 'success' : 'info'">{{ scope.row.isScheduled ? '是' : '否' }}</el-tag> <el-tag :type="scope.row.isScheduled ? 'success' : 'info'">{{ scope.row.isScheduled ? t('ProductionReport.Index.yes') : t('ProductionReport.Index.no') }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="生产进度" align="center" min-width="60px"> <el-table-column :label="t('ProductionReport.Index.tableProductionProgress')" align="center" min-width="60px">
<template #default="scope"> <template #default="scope">
<div class="production-progress-cell"> <div class="production-progress-cell">
<el-progress <el-progress
@ -130,9 +127,8 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column :label="t('ProductionReport.Index.tableRemark')" align="center" prop="remark" />
</el-table> </el-table>
<!-- 分页 -->
<Pagination <Pagination
:total="total" :total="total"
v-model:page="queryParams.pageNo" v-model:page="queryParams.pageNo"
@ -141,23 +137,18 @@
/> />
</ContentWrap> </ContentWrap>
<!-- 详情Tabs -->
<ContentWrap v-if="currentRow && currentRow.id"> <ContentWrap v-if="currentRow && currentRow.id">
<el-tabs v-model="activeTabName"> <el-tabs v-model="activeTabName">
<!-- 基础信息 --> <el-tab-pane :label="t('ProductionReport.Index.detailTabBasicInfo')" name="basicInfo">
<el-tab-pane label="基础信息" name="basicInfo">
<ProductionReportBasicInfo :task-id="currentRow.id" :task-delivery-date="currentRow.deliveryDate" /> <ProductionReportBasicInfo :task-id="currentRow.id" :task-delivery-date="currentRow.deliveryDate" />
</el-tab-pane> </el-tab-pane>
<!-- 关联计划 --> <el-tab-pane :label="t('ProductionReport.Index.detailTabRelatedPlan')" name="relatedPlan">
<el-tab-pane label="关联计划" name="relatedPlan">
<ProductionReportRelatedPlan :task-id="currentRow.id" /> <ProductionReportRelatedPlan :task-id="currentRow.id" />
</el-tab-pane> </el-tab-pane>
<!-- 质检信息 --> <el-tab-pane :label="t('ProductionReport.Index.detailTabQualityInfo')" name="qualityInfo">
<el-tab-pane label="质检信息" name="qualityInfo">
<ProductionReportQualityInfo :task-id="currentRow.id" /> <ProductionReportQualityInfo :task-id="currentRow.id" />
</el-tab-pane> </el-tab-pane>
<!-- 报工信息 --> <el-tab-pane :label="t('ProductionReport.Index.detailTabBaogongInfo')" name="baogongInfo">
<el-tab-pane label="报工信息" name="baogongInfo">
<ProductionReportBaogongInfo :task-id="currentRow.id" /> <ProductionReportBaogongInfo :task-id="currentRow.id" />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -173,15 +164,16 @@ import ProductionReportBasicInfo from './components/ProductionReportBasicInfo.vu
import ProductionReportRelatedPlan from './components/ProductionReportRelatedPlan.vue' import ProductionReportRelatedPlan from './components/ProductionReportRelatedPlan.vue'
import ProductionReportQualityInfo from './components/ProductionReportQualityInfo.vue' import ProductionReportQualityInfo from './components/ProductionReportQualityInfo.vue'
import ProductionReportBaogongInfo from './components/ProductionReportBaogongInfo.vue' import ProductionReportBaogongInfo from './components/ProductionReportBaogongInfo.vue'
import { useI18n } from '@/hooks/web/useI18n'
/** 生产报表 列表 */
defineOptions({ name: 'ProductionReport' }) defineOptions({ name: 'ProductionReport' })
const message = useMessage() // const { t } = useI18n()
const message = useMessage()
const loading = ref(true) // const loading = ref(true)
const list = ref<TaskVO[]>([]) // const list = ref<TaskVO[]>([])
const total = ref(0) // const total = ref(0)
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
@ -192,11 +184,11 @@ const queryParams = reactive({
remark: undefined, remark: undefined,
createTime: [] createTime: []
}) })
const queryFormRef = ref() // const queryFormRef = ref()
const exportLoading = ref(false) // const exportLoading = ref(false)
const activeTabName = ref('basicInfo') // tab const activeTabName = ref('basicInfo')
const activeStatusTab = ref('') // tab const activeStatusTab = ref('')
const deliveryDateFormatter = (_row: any, _column: any, value: any) => { const deliveryDateFormatter = (_row: any, _column: any, value: any) => {
if (value) return dateFormatter2(_row, _column, value) if (value) return dateFormatter2(_row, _column, value)
@ -211,7 +203,6 @@ const getProductionProgressPercent = (row: any) => {
return Math.max(0, Math.min(100, Number(rawPercent.toFixed(2)))) return Math.max(0, Math.min(100, Number(rawPercent.toFixed(2))))
} }
/** 查询列表 */
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
try { try {
@ -223,35 +214,29 @@ const getList = async () => {
} }
} }
/** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNo = 1 queryParams.pageNo = 1
getList() getList()
} }
/** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields() queryFormRef.value.resetFields()
handleQuery() handleQuery()
} }
/** 导出按钮操作 */
const handleExport = async () => { const handleExport = async () => {
try { try {
//
await message.exportConfirm() await message.exportConfirm()
//
exportLoading.value = true exportLoading.value = true
const data = await TaskApi.exportTask(queryParams) const data = await TaskApi.exportTask(queryParams)
download.excel(data, '生产报表.xls') download.excel(data, t('ProductionReport.Index.exportFilename') + '.xls')
} catch { } catch {
} finally { } finally {
exportLoading.value = false exportLoading.value = false
} }
} }
/** 选中行操作 */ const currentRow = ref<TaskVO>({} as TaskVO)
const currentRow = ref<TaskVO>({} as TaskVO) //
const handleCurrentChange = (row: TaskVO | null) => { const handleCurrentChange = (row: TaskVO | null) => {
if (row) { if (row) {
currentRow.value = row currentRow.value = row
@ -259,7 +244,6 @@ const handleCurrentChange = (row: TaskVO | null) => {
} }
} }
/** tab 切换 */
const handleTabClick = (tab: any) => { const handleTabClick = (tab: any) => {
queryParams.status = tab.paneName === '' ? undefined : tab.paneName queryParams.status = tab.paneName === '' ? undefined : tab.paneName
currentRow.value = {} as TaskVO currentRow.value = {} as TaskVO
@ -267,7 +251,6 @@ const handleTabClick = (tab: any) => {
getList() getList()
} }
/** 初始化 **/
onMounted(() => { onMounted(() => {
getList() getList()
}) })

Loading…
Cancel
Save