|
|
@@ -0,0 +1,331 @@
|
|
|
+<template>
|
|
|
+ <div class="process-production-page" :class="{ 'in-dialog': inDialog }">
|
|
|
+ <div class="process-production-inner">
|
|
|
+ <el-form class="process-production-search" inline @keyup.enter="handleSearch">
|
|
|
+ <el-input
|
|
|
+ v-model="searchForm.workorder"
|
|
|
+ placeholder="请输入订单编号"
|
|
|
+ clearable
|
|
|
+ style="width: 200px"
|
|
|
+ />
|
|
|
+ <el-button type="primary" icon="search" :loading="loading" @click="handleSearch">
|
|
|
+ 查询
|
|
|
+ </el-button>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-alert
|
|
|
+ v-if="pageHint"
|
|
|
+ class="process-production-hint"
|
|
|
+ :title="pageHint"
|
|
|
+ type="warning"
|
|
|
+ show-icon
|
|
|
+ :closable="false"
|
|
|
+ />
|
|
|
+
|
|
|
+ <el-descriptions class="order-summary-desc" :column="3" border>
|
|
|
+ <el-descriptions-item label="订单编号">
|
|
|
+ {{ orderSummary.订单编号 }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="生产款号">
|
|
|
+ {{ orderSummary.生产款号 }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="款式">
|
|
|
+ {{ orderSummary.款式 }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+
|
|
|
+ <div class="process-production-table-wrap">
|
|
|
+ <el-table
|
|
|
+ v-loading="loading"
|
|
|
+ :data="displayTableData"
|
|
|
+ :height="tableHeight"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ row-key="rowKey"
|
|
|
+ style="width: 100%"
|
|
|
+ :row-style="{ height: '32px' }"
|
|
|
+ :header-row-style="{ height: '36px' }"
|
|
|
+ :span-method="tableSpanMethod"
|
|
|
+ :summary-method="tableSummaryMethod"
|
|
|
+ show-summary
|
|
|
+ show-overflow-tooltip
|
|
|
+ >
|
|
|
+ <el-table-column label="工序编号" prop="工序号" width="100" align="center" />
|
|
|
+ <el-table-column label="工序名称" prop="工序名称" min-width="280" align="left" show-overflow-tooltip />
|
|
|
+ <el-table-column label="员工姓名" prop="员工姓名" width="100" align="left" />
|
|
|
+ <el-table-column label="完工数量" prop="数量" width="90" align="center" />
|
|
|
+ <el-table-column v-if="!hideAmountColumns" label="金额" prop="金额" width="90" align="center" />
|
|
|
+ <el-table-column v-if="!hideAmountColumns" label="工时" prop="工时" width="90" align="center" />
|
|
|
+ <el-table-column v-if="!hideAmountColumns" label="工资" prop="工资" width="90" align="center" />
|
|
|
+ <el-table-column label="开工日期" prop="开工日期" width="110" align="center" />
|
|
|
+ <el-table-column label="完工日期" prop="完工日期" width="110" align="center" />
|
|
|
+ <template #empty>
|
|
|
+ <el-empty
|
|
|
+ v-if="!loading"
|
|
|
+ :description="emptyDescription"
|
|
|
+ :image-size="100"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { computed, reactive, ref, watch } from 'vue'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import { checkProcessProduction } from '@/api/mes/job'
|
|
|
+
|
|
|
+defineOptions({ name: 'ProcessProduction' })
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ /** 弹窗打开时传入的订单编号,会自动查询 */
|
|
|
+ initialWorkorder: { type: String, default: '' },
|
|
|
+ /** 嵌入订单资料弹窗时使用 */
|
|
|
+ inDialog: { type: Boolean, default: false },
|
|
|
+ /** 为 true 时不显示金额、工时、工资列(如样衣批核页) */
|
|
|
+ hideAmountColumns: { type: Boolean, default: false },
|
|
|
+})
|
|
|
+
|
|
|
+/** 主页面表格高度:改此值即可 */
|
|
|
+const MAIN_TABLE_HEIGHT = 'calc(100vh - 300px)'
|
|
|
+/** 弹窗内表格最大高度(距视口顶部的预留像素,含标题/搜索/订单信息栏) */
|
|
|
+const DIALOG_TABLE_OFFSET = 220
|
|
|
+const DIALOG_TABLE_EMPTY_HEIGHT = 320
|
|
|
+const TABLE_SUMMARY_ROW_HEIGHT = 40
|
|
|
+
|
|
|
+const sumTableColumn = (data, prop) => {
|
|
|
+ return data.reduce((sum, row) => {
|
|
|
+ const n = Number(row[prop])
|
|
|
+ return sum + (Number.isFinite(n) ? n : 0)
|
|
|
+ }, 0)
|
|
|
+}
|
|
|
+
|
|
|
+const formatSummaryValue = (total, prop) => {
|
|
|
+ if (!Number.isFinite(total)) return ''
|
|
|
+ if (prop === '数量') {
|
|
|
+ return Number.isInteger(total) ? String(total) : total.toFixed(2)
|
|
|
+ }
|
|
|
+ return total.toFixed(2)
|
|
|
+}
|
|
|
+
|
|
|
+const tableSummaryMethod = ({ columns, data }) => {
|
|
|
+ const summaryProps = props.hideAmountColumns
|
|
|
+ ? ['数量']
|
|
|
+ : ['数量', '金额', '工时', '工资']
|
|
|
+
|
|
|
+ return columns.map((column, index) => {
|
|
|
+ if (index === 0) return '合计'
|
|
|
+ const prop = column.property
|
|
|
+ if (!summaryProps.includes(prop)) return ''
|
|
|
+ return formatSummaryValue(sumTableColumn(data, prop), prop)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/** 弹窗按行数自适应高度,避免数据少时底部大块空白;主页面固定高度 */
|
|
|
+const tableHeight = computed(() => {
|
|
|
+ if (!props.inDialog) return MAIN_TABLE_HEIGHT
|
|
|
+ const rows = displayTableData.value.length
|
|
|
+ const maxH = Math.max(200, window.innerHeight - DIALOG_TABLE_OFFSET)
|
|
|
+ if (!rows) return Math.min(DIALOG_TABLE_EMPTY_HEIGHT, maxH)
|
|
|
+ const needH = rows * 32 + 36 + 12 + TABLE_SUMMARY_ROW_HEIGHT
|
|
|
+ return Math.min(Math.max(needH, 200), maxH)
|
|
|
+})
|
|
|
+
|
|
|
+const loading = ref(false)
|
|
|
+const searched = ref(false)
|
|
|
+/** 查询结果页内提示(如:未找到报工数据),不用 ElMessage 弹窗 */
|
|
|
+const pageHint = ref('')
|
|
|
+
|
|
|
+const emptyDescription = computed(() => {
|
|
|
+ if (!searched.value) return '请输入订单编号后查询'
|
|
|
+ return pageHint.value || '暂无数据'
|
|
|
+})
|
|
|
+
|
|
|
+const searchForm = reactive({
|
|
|
+ workorder: '',
|
|
|
+})
|
|
|
+
|
|
|
+const orderSummary = reactive({
|
|
|
+ 订单编号: '',
|
|
|
+ 生产款号: '',
|
|
|
+ 款式: '',
|
|
|
+})
|
|
|
+
|
|
|
+const processListRaw = ref([])
|
|
|
+
|
|
|
+const flattenProcessList = (processList = []) => {
|
|
|
+ const rows = []
|
|
|
+ for (const proc of processList) {
|
|
|
+ const staffs = Array.isArray(proc.staffs) ? proc.staffs : []
|
|
|
+ if (!staffs.length) {
|
|
|
+ rows.push({
|
|
|
+ rowKey: `${proc.工序号}-empty`,
|
|
|
+ 工序号: proc.工序号 ?? '',
|
|
|
+ 工序名称: proc.工序名称 ?? '',
|
|
|
+ 员工姓名: '',
|
|
|
+ 数量: '',
|
|
|
+ 开工日期: '',
|
|
|
+ 完工日期: '',
|
|
|
+ 金额: '',
|
|
|
+ 工时: '',
|
|
|
+ 工资: '',
|
|
|
+ _processRowSpan: 1,
|
|
|
+ })
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ staffs.forEach((staff, index) => {
|
|
|
+ rows.push({
|
|
|
+ rowKey: `${proc.工序号}-${index}-${staff.员工姓名 || ''}`,
|
|
|
+ 工序号: proc.工序号 ?? '',
|
|
|
+ 工序名称: proc.工序名称 ?? '',
|
|
|
+ 员工姓名: staff.员工姓名 ?? '',
|
|
|
+ 数量: staff.数量 ?? '',
|
|
|
+ 开工日期: staff.开工日期 ?? '',
|
|
|
+ 完工日期: staff.完工日期 ?? '',
|
|
|
+ 金额: staff.金额 ?? '',
|
|
|
+ 工时: staff.工时 ?? '',
|
|
|
+ 工资: staff.工资 ?? '',
|
|
|
+ _processRowSpan: index === 0 ? staffs.length : 0,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ return rows
|
|
|
+}
|
|
|
+
|
|
|
+const displayTableData = computed(() => flattenProcessList(processListRaw.value || []))
|
|
|
+
|
|
|
+const tableSpanMethod = ({ row, column }) => {
|
|
|
+ if (column.property !== '工序号' && column.property !== '工序名称') {
|
|
|
+ return { rowspan: 1, colspan: 1 }
|
|
|
+ }
|
|
|
+ const rowspan = row._processRowSpan || 0
|
|
|
+ if (rowspan > 0) {
|
|
|
+ return { rowspan, colspan: 1 }
|
|
|
+ }
|
|
|
+ return { rowspan: 0, colspan: 0 }
|
|
|
+}
|
|
|
+
|
|
|
+const loadByWorkorder = async (workorder) => {
|
|
|
+ const w = String(workorder || '').trim()
|
|
|
+ if (!w) return
|
|
|
+ searchForm.workorder = w
|
|
|
+ await handleSearch()
|
|
|
+}
|
|
|
+
|
|
|
+const handleSearch = async () => {
|
|
|
+ const workorder = String(searchForm.workorder || '').trim()
|
|
|
+ if (!workorder) {
|
|
|
+ ElMessage.warning('请输入订单编号')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ loading.value = true
|
|
|
+ searched.value = true
|
|
|
+ pageHint.value = ''
|
|
|
+ try {
|
|
|
+ const res = await checkProcessProduction({ workorder })
|
|
|
+ if (res?.code !== 0) {
|
|
|
+ processListRaw.value = []
|
|
|
+ orderSummary.订单编号 = workorder
|
|
|
+ orderSummary.生产款号 = ''
|
|
|
+ orderSummary.款式 = ''
|
|
|
+ pageHint.value = res?.msg || '未找到报工数据'
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const data = res.data || {}
|
|
|
+ orderSummary.订单编号 = data.订单编号 || workorder
|
|
|
+ orderSummary.生产款号 = data.生产款号 || ''
|
|
|
+ orderSummary.款式 = data.款式 || ''
|
|
|
+ processListRaw.value = data.工序列表 || []
|
|
|
+ if (!processListRaw.value.length) {
|
|
|
+ pageHint.value = '未找到报工数据'
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error)
|
|
|
+ pageHint.value = ''
|
|
|
+ ElMessage.error('查询失败,请稍后重试')
|
|
|
+ processListRaw.value = []
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => props.initialWorkorder,
|
|
|
+ (val) => {
|
|
|
+ if (val) loadByWorkorder(val)
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+)
|
|
|
+
|
|
|
+defineExpose({ loadByWorkorder })
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.process-production-page {
|
|
|
+ height: 100%;
|
|
|
+ background: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.process-production-page.in-dialog {
|
|
|
+ min-height: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 主页面:右侧与左侧留白一致(左侧由布局已有间距,此处补右侧) */
|
|
|
+.process-production-page:not(.in-dialog) {
|
|
|
+ padding-right: 16px;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.process-production-inner {
|
|
|
+ padding: 0 16px 12px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ max-width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.process-production-search {
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.process-production-hint {
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.process-production-search :deep(.el-form-item) {
|
|
|
+ margin-bottom: 0;
|
|
|
+ margin-right: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.order-summary-desc {
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.order-summary-desc :deep(.el-descriptions__label) {
|
|
|
+ width: 100px;
|
|
|
+ font-weight: normal;
|
|
|
+ color: #606266;
|
|
|
+ background: #f5f7fa;
|
|
|
+}
|
|
|
+
|
|
|
+.order-summary-desc :deep(.el-descriptions__content) {
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+/* 不用 gva-table-box,避免全局白底卡片(main.scss: bg-white p-6 rounded) */
|
|
|
+.process-production-table-wrap {
|
|
|
+ width: 100%;
|
|
|
+ padding: 0;
|
|
|
+ background: transparent;
|
|
|
+ border-radius: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.process-production-table-wrap :deep(.el-table__footer-wrapper td) {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ background: #fafafa;
|
|
|
+}
|
|
|
+</style>
|