<template>
|
<div class="reporting-page">
|
<!-- 页面头部 -->
|
<PageHeader content="生产工单报工">
|
</PageHeader>
|
<!-- 步骤指示器 -->
|
<div class="step-indicator">
|
<div v-for="(step, index) in steps"
|
:key="index"
|
class="step-item"
|
:class="{
|
'active': index === activeStep,
|
'completed': index < activeStep
|
}">
|
<div class="step-circle">
|
<span v-if="index < activeStep"
|
class="step-check">
|
<el-icon>
|
<Check />
|
</el-icon>
|
</span>
|
<span v-else
|
class="step-number">{{ index + 1 }}</span>
|
</div>
|
<div class="step-content">
|
<div class="step-title">{{ step.title }}</div>
|
<div class="step-description">{{ step.description }}</div>
|
</div>
|
<div class="step-line"
|
v-if="index < steps.length - 1"></div>
|
</div>
|
</div>
|
<!-- 页面内容 -->
|
<div class="page-content">
|
<!-- 第一步:选择生产订单 -->
|
<div v-if="activeStep === 0"
|
class="step-panel">
|
<div class="panel-header">
|
<div>
|
<h3 class="panel-title">选择生产订单</h3>
|
<p class="panel-subtitle">请从以下列表中选择需要报工的生产订单</p>
|
</div>
|
<div class="header-actions">
|
<el-button @click="activeStep--"
|
v-if="activeStep > 0"
|
:disabled="isSubmitting">
|
<el-icon>
|
<ArrowLeft />
|
</el-icon> 上一步
|
</el-button>
|
<el-button type="primary"
|
@click="handleNextStep"
|
v-if="activeStep < 3"
|
:disabled="isSubmitting">
|
下一步 <el-icon>
|
<ArrowRight />
|
</el-icon>
|
</el-button>
|
</div>
|
</div>
|
<el-form :model="form"
|
ref="formRef"
|
class="form-container">
|
<el-form-item label="生产订单"
|
prop="orderId"
|
required>
|
<el-select v-model="orderId"
|
placeholder="请选择生产订单"
|
clearable
|
filterable
|
class="form-select"
|
:loading="orderLoading"
|
@change="handleOrderChange">
|
<el-option v-for="order in orderList"
|
:key="order.id"
|
:label="`${order.npsNo} - ${order.productName} ${order.model}`"
|
:value="order.id" />
|
</el-select>
|
</el-form-item>
|
</el-form>
|
</div>
|
<!-- 第二步:填写基础信息 -->
|
<div v-else-if="activeStep === 1"
|
class="step-panel">
|
<div class="panel-header">
|
<div>
|
<h3 class="panel-title">填写基础信息</h3>
|
<p class="panel-subtitle">请填写报工的基本信息</p>
|
</div>
|
<div class="header-actions">
|
<el-button @click="activeStep--"
|
v-if="activeStep > 0"
|
:disabled="isSubmitting">
|
<el-icon>
|
<ArrowLeft />
|
</el-icon> 上一步
|
</el-button>
|
<el-button type="primary"
|
@click="handleNextStep"
|
v-if="activeStep < 3"
|
:disabled="isSubmitting">
|
下一步 <el-icon>
|
<ArrowRight />
|
</el-icon>
|
</el-button>
|
</div>
|
</div>
|
<el-form :model="form"
|
:rules="rules"
|
ref="formRef"
|
class="form-container">
|
<div class="form-grid">
|
<el-form-item label="生产订单号"
|
prop="npsNo"
|
class="form-item">
|
<el-input disabled
|
v-model="form.npsNo"
|
class="form-input" />
|
</el-form-item>
|
<el-form-item label="班组"
|
prop="teamName"
|
required
|
class="form-item">
|
<el-select v-model="form.teamName"
|
placeholder="请选择班组"
|
class="form-select">
|
<el-option label="白班"
|
value="白班" />
|
<el-option label="夜班"
|
value="夜班" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="产品编码"
|
prop="materialCode"
|
class="form-item">
|
<el-input disabled
|
v-model="form.materialCode"
|
class="form-input" />
|
</el-form-item>
|
<el-form-item label="产品名称"
|
prop="productName"
|
class="form-item">
|
<el-input disabled
|
v-model="form.productName"
|
class="form-input" />
|
</el-form-item>
|
<el-form-item label="规格"
|
prop="specification"
|
class="form-item">
|
<el-input disabled
|
v-model="form.specification"
|
class="form-input" />
|
</el-form-item>
|
<el-form-item label="创建人"
|
prop="createBy"
|
required
|
class="form-item">
|
<el-select v-model="form.createBy"
|
placeholder="请选择创建人"
|
class="form-select"
|
:loading="userLoading">
|
<el-option v-for="user in userList"
|
:key="user.id"
|
:label="user.nickName || user.userName"
|
:value="user.nickName || user.userName" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="创建时间"
|
prop="createTime"
|
class="form-item">
|
<el-date-picker disabled
|
v-model="form.createTime"
|
type="datetime"
|
placeholder="请选择创建时间"
|
class="form-input" />
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
<!-- 第三步:查看工序参数 -->
|
<div v-else-if="activeStep === 2"
|
class="step-panel process-panel">
|
<div class="panel-header">
|
<div>
|
<h3 class="panel-title">工序参数管理</h3>
|
<p class="panel-subtitle">请查看并填写各工序的参数信息</p>
|
</div>
|
<div class="header-actions">
|
<el-button @click="activeStep--"
|
v-if="activeStep > 0"
|
:disabled="isSubmitting">
|
<el-icon>
|
<ArrowLeft />
|
</el-icon> 上一步
|
</el-button>
|
<el-button type="primary"
|
@click="handleNextStep"
|
v-if="activeStep < 3"
|
:disabled="isSubmitting">
|
下一步 <el-icon>
|
<ArrowRight />
|
</el-icon>
|
</el-button>
|
</div>
|
</div>
|
<div class="process-container">
|
<!-- 左侧工序导航 -->
|
<div class="process-nav">
|
<div v-for="process in processList"
|
:key="process.processId"
|
class="process-nav-item"
|
:class="{ 'active': activeProcessId === process.processId + '' }"
|
@click="handleProcessClick(process.processId)">
|
<span class="process-name">{{ process.processName }}</span>
|
<span class="process-badge"
|
v-if="getProcessInfo(parseInt(process.processId)).postPersonnel">
|
{{ getProcessInfo(parseInt(process.processId)).postPersonnel }}
|
</span>
|
</div>
|
</div>
|
<!-- 右侧工序内容 -->
|
<div class="process-content">
|
<div v-if="activeProcessId"
|
class="process-details">
|
<!-- 固定参数 -->
|
<div class="param-section">
|
<div class="section-header">
|
<h4 class="section-title">工序基本信息</h4>
|
</div>
|
<div class="param-form">
|
<el-form :label-position="'top'">
|
<div class="form-grid">
|
<el-form-item label="岗位人员"
|
class="form-item">
|
<el-select v-model="getProcessInfo(parseInt(activeProcessId)).postPersonnel"
|
placeholder="请选择岗位人员"
|
class="form-select"
|
:loading="userLoading">
|
<el-option v-for="user in userList"
|
:key="user.id"
|
:label="user.nickName || user.userName"
|
:value="user.nickName || user.userName" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="设备异常情况"
|
class="form-item">
|
<el-input v-model="getProcessInfo(parseInt(activeProcessId)).equipmentAbnormality"
|
placeholder="请输入设备异常情况"
|
type="textarea"
|
:rows="2"
|
class="form-textarea" />
|
</el-form-item>
|
<el-form-item label="当班设备处置"
|
class="form-item">
|
<el-input v-model="getProcessInfo(parseInt(activeProcessId)).equipmentHandling"
|
placeholder="请输入当班设备处置"
|
type="textarea"
|
:rows="2"
|
class="form-textarea" />
|
</el-form-item>
|
<el-form-item label="工艺人员交待"
|
class="form-item">
|
<el-input v-model="getProcessInfo(parseInt(activeProcessId)).processInstructions"
|
placeholder="请输入工艺人员交待"
|
type="textarea"
|
:rows="2"
|
class="form-textarea" />
|
</el-form-item>
|
<el-form-item label="上传文件"
|
class="form-item"
|
:span="24">
|
<el-upload class="upload-demo upload-block"
|
action="#"
|
:on-preview="handlePreview"
|
:on-remove="handleRemove"
|
:file-list="getProcessInfo(parseInt(activeProcessId)).files || []"
|
:auto-upload="false"
|
:accept="'.jpg,.png'"
|
:max-size="500000"
|
:on-change="handleFileChange">
|
<el-button type="primary"
|
:icon="Upload">点击上传</el-button>
|
<template #tip>
|
<div class="el-upload__tip">
|
只能上传jpg/png文件,且不超过500kb
|
</div>
|
</template>
|
</el-upload>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</div>
|
<!-- BOM信息 -->
|
<div class="param-section"
|
v-if="getProcessStructures(parseInt(activeProcessId)).length > 0">
|
<div class="section-header">
|
<h4 class="section-title">BOM信息</h4>
|
</div>
|
<div class="param-form">
|
<el-form :label-position="'top'">
|
<div class="form-grid">
|
<el-form-item v-for="item in getProcessStructures(parseInt(activeProcessId))"
|
:key="item.id"
|
:label="`${item.productName} ${item.model}`"
|
class="form-item">
|
<div class="consumable-input-group">
|
<el-input-number v-model="getProcessInfo(parseInt(activeProcessId)).consumables[item.id]"
|
:min="0"
|
:model-value="getConsumableValue(parseInt(activeProcessId), item.id)"
|
@change="val => getProcessInfo(parseInt(activeProcessId)).consumables[item.id] = val"
|
class="consumable-input" />
|
<span class="consumable-unit">{{ item.unit }}</span>
|
</div>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</div>
|
<!-- 参数组列表 -->
|
<div class="param-section">
|
<div class="section-header">
|
<h4 class="section-title">参数组管理</h4>
|
<div class="section-actions">
|
<el-switch v-model="useTableView"
|
active-text="表格视图"
|
inactive-text="卡片视图"
|
inline-prompt />
|
<el-button type="primary"
|
@click="addParamGroup(parseInt(activeProcessId))"
|
:icon="Plus">
|
新增参数组
|
</el-button>
|
</div>
|
</div>
|
<!-- 卡片视图 -->
|
<div v-if="!useTableView"
|
class="param-cards">
|
<div v-for="(group, index) in form.paramGroups[activeProcessId] || []"
|
:key="index"
|
class="param-card">
|
<div class="card-header">
|
<span class="card-title">参数组 {{ index + 1 }}</span>
|
<el-button type="danger"
|
size="small"
|
@click="removeParamGroup(parseInt(activeProcessId), index)"
|
v-if="(form.paramGroups[activeProcessId] || []).length > 1"
|
circle>
|
<el-icon>
|
<Delete />
|
</el-icon>
|
</el-button>
|
</div>
|
<div class="card-body">
|
<div class="param-grid">
|
<el-form-item v-for="param in params"
|
:key="param.id"
|
:label="param.paramName"
|
:label-width="120"
|
class="param-item">
|
<template v-if="param.paramType == '1'">
|
<!-- 数字类型 -->
|
<div class="param-input-group">
|
<el-input-number v-model="form.paramGroups[activeProcessId][index][param.id]"
|
controls-position="right"
|
:precision="getPrecision(param.paramFormat)"
|
class="param-input" />
|
<span v-if="param.unit && param.unit != '/'"
|
class="param-unit">
|
{{ param.unit }}
|
</span>
|
</div>
|
</template>
|
<template v-else-if="param.paramType == '2'">
|
<!-- 文本类型 -->
|
<div class="param-input-group">
|
<el-input v-model="form.paramGroups[activeProcessId][index][param.id]"
|
class="param-input" />
|
<span v-if="param.unit && param.unit != '/'"
|
class="param-unit">
|
{{ param.unit }}
|
</span>
|
</div>
|
</template>
|
<template v-else-if="param.paramType == '3'">
|
<!-- 字典类型 -->
|
<div class="param-input-group">
|
<el-select v-model="form.paramGroups[activeProcessId][index][param.id]"
|
placeholder="请选择"
|
class="param-select"
|
style="width: 100%">
|
<el-option v-for="option in dictOptions[param.paramFormat] || []"
|
:key="option.dictValue"
|
:label="option.dictLabel"
|
:value="option.dictValue" />
|
</el-select>
|
<span v-if="param.unit && param.unit != '/'"
|
class="param-unit">
|
{{ param.unit }}
|
</span>
|
</div>
|
</template>
|
<template v-else-if="param.paramType == '4'">
|
<!-- 日期类型 -->
|
<div class="param-input-group">
|
<el-date-picker :value-format="param.paramFormat"
|
:format="param.paramFormat"
|
:type="param.paramFormat=='YYYY-MM-DD'?'date':'datetime'"
|
v-model="form.paramGroups[activeProcessId][index][param.id]"
|
class="param-input" />
|
<span v-if="param.unit && param.unit != '/'"
|
class="param-unit">
|
{{ param.unit }}
|
</span>
|
</div>
|
</template>
|
<template v-else>
|
<!-- 其他类型 -->
|
<div class="param-input-group">
|
<el-input v-model="form.paramGroups[activeProcessId][index][param.id]"
|
class="param-input" />
|
<span v-if="param.unit && param.unit != '/'"
|
class="param-unit">
|
{{ param.unit }}
|
</span>
|
</div>
|
</template>
|
</el-form-item>
|
</div>
|
</div>
|
</div>
|
</div>
|
<!-- 表格视图 -->
|
<div v-else
|
class="param-table">
|
<el-table :data="form.paramGroups[activeProcessId] || []"
|
style="width: 100%"
|
class="table-view">
|
<!-- 操作列 -->
|
<el-table-column label="操作"
|
width="100"
|
fixed>
|
<template #default="{ $index }">
|
<el-button type="danger"
|
size="small"
|
@click="removeParamGroup(parseInt(activeProcessId), $index)"
|
circle>
|
<el-icon>
|
<Delete />
|
</el-icon>
|
</el-button>
|
</template>
|
</el-table-column>
|
<!-- 参数列 -->
|
<el-table-column v-for="param in params"
|
:key="param.id"
|
:label="param.paramName"
|
min-width="200">
|
<template #default="{ row }">
|
<template v-if="param.paramType == '1'">
|
<!-- 数字类型 -->
|
<el-input-number v-model="row[param.id]"
|
controls-position="right"
|
:precision="getPrecision(param.paramFormat)"
|
class="table-input" />
|
</template>
|
<template v-else-if="param.paramType == '2'">
|
<!-- 文本类型 -->
|
<el-input v-model="row[param.id]"
|
class="table-input" />
|
</template>
|
<template v-else-if="param.paramType == '3'">
|
<!-- 字典类型 -->
|
<el-select v-model="row[param.id]"
|
placeholder="请选择"
|
class="table-select">
|
<el-option v-for="option in dictOptions[param.paramFormat] || []"
|
:key="option.dictValue"
|
:label="option.dictLabel"
|
:value="option.dictValue" />
|
</el-select>
|
</template>
|
<template v-else-if="param.paramType == '4'">
|
<!-- 日期类型 -->
|
<el-date-picker :value-format="param.paramFormat"
|
:format="param.paramFormat"
|
width="100%"
|
:type="param.paramFormat=='YYYY-MM-DD'?'date':'datetime'"
|
v-model="row[param.id]"
|
class="table-input table-select" />
|
</template>
|
<template v-else>
|
<!-- 其他类型 -->
|
<el-input v-model="row[param.id]"
|
class="table-input" />
|
</template>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
<!-- 新增参数组按钮 -->
|
<!-- <div class="param-actions">
|
<el-button type="primary"
|
@click="addParamGroup(parseInt(activeProcessId))"
|
:icon="Plus">
|
新增参数组
|
</el-button>
|
</div> -->
|
</div>
|
</div>
|
<div v-else
|
class="empty-process">
|
<el-empty description="请选择一个工序"
|
:image-size="120" />
|
</div>
|
</div>
|
</div>
|
</div>
|
<!-- 第四步:填写产量信息 -->
|
<div v-else-if="activeStep === 3"
|
class="step-panel">
|
<div class="panel-header">
|
<div>
|
<h3 class="panel-title">填写产量信息</h3>
|
<p class="panel-subtitle">请填写本次报工的产量数据</p>
|
</div>
|
<div class="header-actions">
|
<el-button @click="activeStep--"
|
v-if="activeStep > 0"
|
:disabled="isSubmitting">
|
<el-icon>
|
<ArrowLeft />
|
</el-icon> 上一步
|
</el-button>
|
<el-button type="primary"
|
@click="handleSubmit"
|
v-if="activeStep === 3"
|
:loading="isSubmitting">
|
<el-icon v-if="!isSubmitting">
|
<Check />
|
</el-icon>
|
<el-icon v-else>
|
<Loading />
|
</el-icon>
|
{{ isSubmitting ? '提交中...' : '确认提交' }}
|
</el-button>
|
</div>
|
</div>
|
<el-form :model="form"
|
:rules="rules"
|
ref="formRef"
|
:label-position="'top'"
|
class="form-container">
|
<div class="form-grid1">
|
<el-form-item label="产出方量"
|
prop="outputVolume"
|
required
|
class="form-item">
|
<div class="volume-input-group">
|
<el-input-number v-model="form.outputVolume"
|
:min="0"
|
:precision="2"
|
class="volume-input" />
|
<span class="volume-unit">方</span>
|
</div>
|
</el-form-item>
|
<el-form-item label="不合格方量"
|
prop="unqualifiedVolume"
|
required
|
class="form-item">
|
<div class="volume-input-group">
|
<el-input-number v-model="form.unqualifiedVolume"
|
:min="0"
|
:precision="2"
|
class="volume-input" />
|
<span class="volume-unit">方</span>
|
</div>
|
</el-form-item>
|
<el-form-item label="完成方量"
|
prop="completedVolume"
|
required
|
class="form-item">
|
<div class="volume-input-group">
|
<el-input-number v-model="form.completedVolume"
|
:min="0"
|
:precision="2"
|
class="volume-input" />
|
<span class="volume-unit">方</span>
|
</div>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
</div>
|
<!-- 底部按钮 -->
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed, watch, onMounted } from "vue";
|
import { ElMessage, ElEmpty } from "element-plus";
|
import { useRouter, useRoute } from "vue-router";
|
import { getDicts } from "@/api/system/dict/data";
|
import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
|
import { productionRecordAdd } from "@/api/productionManagement/productProcessRoute.js";
|
import { userListNoPage } from "@/api/system/user.js";
|
import { getInfo } from "@/api/login.js";
|
import {
|
Check,
|
Close,
|
Delete,
|
Plus,
|
ArrowLeft,
|
ArrowRight,
|
Loading,
|
Upload,
|
} from "@element-plus/icons-vue";
|
|
const router = useRouter();
|
|
// 从路由参数获取数据
|
const route = useRoute();
|
const data = route.query.data ? JSON.parse(route.query.data) : {};
|
|
const dialogTitle = computed(() => (data.id ? "编辑报工" : "新增报工"));
|
|
const formRef = ref(null);
|
const isSubmitting = ref(false);
|
const orderLoading = ref(false);
|
const processLoading = ref(false);
|
const activeStep = ref(0);
|
|
const steps = [
|
{ title: "选择生产订单", description: "选择需要报工的生产订单" },
|
{ title: "填写基础信息", description: "填写报工的基本信息" },
|
{ title: "查看工序参数", description: "填写各工序的参数信息" },
|
{ title: "填写产量信息", description: "填写本次报工的产量数据" },
|
];
|
|
// 计算当前工序的参数组数量
|
const paramGroupCount = computed(() => {
|
if (!activeProcessId.value) return 0;
|
return (form.paramGroups[activeProcessId.value] || []).length;
|
});
|
|
const orderId = ref(data.orderId || "");
|
const processId = ref(data.processId || "");
|
const activeProcessId = ref("");
|
const orderList = ref([]);
|
const processList = ref([]);
|
const params = ref([]);
|
const dictOptions = ref({});
|
const userList = ref([]);
|
const userLoading = ref(false);
|
const useTableView = ref(false); // 控制是否使用表格视图
|
|
const form = reactive({
|
id: data.id || undefined,
|
orderId: data.orderId || "",
|
npsNo: data.npsNo || "",
|
teamName: data.teamName || "",
|
materialCode: data.materialCode || "",
|
productName: data.productName || "",
|
specification: data.specification || "",
|
outputVolume: data.outputVolume || 0,
|
unqualifiedVolume: data.unqualifiedVolume || 0,
|
completedVolume: data.completedVolume || 0,
|
createBy: data.createBy || "当前登录人",
|
createTime: data.createTime || new Date(),
|
paramGroups: data.paramGroups || {}, // 存储每个工序的参数组
|
processInfo: data.processInfo || {}, // 存储每个工序的基本信息
|
});
|
|
const rules = {
|
teamName: [{ required: true, message: "请选择班组", trigger: "blur" }],
|
outputVolume: [
|
{ required: true, message: "请输入产出方量", trigger: "blur" },
|
],
|
unqualifiedVolume: [
|
{ required: true, message: "请输入不合格方量", trigger: "blur" },
|
],
|
completedVolume: [
|
{ required: true, message: "请输入完成方量", trigger: "blur" },
|
],
|
createBy: [{ required: true, message: "请输入创建人", trigger: "blur" }],
|
};
|
|
// 加载生产订单列表
|
const loadOrders = () => {
|
orderLoading.value = true;
|
productOrderListPage({ pageNum: 1, pageSize: 100 })
|
.then(res => {
|
orderList.value = res.data.records || [];
|
})
|
.finally(() => {
|
orderLoading.value = false;
|
});
|
};
|
|
// 处理生产订单选择
|
const handleOrderChange = val => {
|
if (val) {
|
const order = orderList.value.find(item => item.id === val);
|
if (order) {
|
form.orderId = val;
|
form.npsNo = order.npsNo;
|
form.materialCode = order.materialCode;
|
form.productName = order.productName;
|
form.specification = order.model;
|
}
|
// 加载工序列表
|
loadProcesses(val);
|
} else {
|
form.orderId = "";
|
form.npsNo = "";
|
form.materialCode = "";
|
form.productName = "";
|
form.specification = "";
|
processId.value = "";
|
activeProcessId.value = "";
|
processList.value = [];
|
params.value = [];
|
form.params = {};
|
}
|
};
|
|
// 加载工序列表
|
const loadProcesses = orderId => {
|
processLoading.value = true;
|
// 调用新的接口
|
productionRecordAdd(orderId)
|
.then(res => {
|
if (res.code === 200) {
|
const data = res.data;
|
// 提取工序列表
|
processList.value = data.productionOrderRouteItemVos || [];
|
// 存储工序结构数据
|
form.processStructures = {};
|
processList.value.forEach(process => {
|
form.processStructures[process.processId] =
|
process.orderStructureVos || [];
|
});
|
// 如果有工序,默认选择第一个
|
if (processList.value.length > 0) {
|
const firstProcess = processList.value[0];
|
activeProcessId.value = firstProcess.processId + "";
|
processId.value = firstProcess.processId;
|
form.processId = firstProcess.processId;
|
// 加载第一个工序的参数
|
loadParams(firstProcess.processId, orderId);
|
}
|
}
|
})
|
.finally(() => {
|
processLoading.value = false;
|
});
|
};
|
|
// 处理工序导航点击
|
const handleProcessClick = selectedProcessId => {
|
activeProcessId.value = selectedProcessId + "";
|
processId.value = selectedProcessId;
|
form.processId = selectedProcessId;
|
// 加载参数列表
|
loadParams(selectedProcessId, form.orderId);
|
};
|
|
// 获取工序基本信息,不存在则初始化
|
const getProcessInfo = processId => {
|
if (!form.processInfo) {
|
form.processInfo = {};
|
}
|
if (!form.processInfo[processId]) {
|
form.processInfo[processId] = {
|
postPersonnel: "",
|
equipmentAbnormality: "",
|
equipmentHandling: "",
|
processInstructions: "",
|
files: [],
|
consumables: {},
|
};
|
}
|
return form.processInfo[processId];
|
};
|
|
// 获取工序结构数据(BOM列表)
|
const getProcessStructures = processId => {
|
return form.processStructures && form.processStructures[processId]
|
? form.processStructures[processId]
|
: [];
|
};
|
|
// 获取消耗品数量,默认为0
|
const getConsumableValue = (processId, itemId) => {
|
const processInfo = getProcessInfo(processId);
|
if (!processInfo.consumables[itemId]) {
|
processInfo.consumables[itemId] = 0;
|
}
|
return processInfo.consumables[itemId];
|
};
|
|
// 处理文件预览
|
const handlePreview = file => {
|
// 检查是否是图片文件
|
if (file.raw && file.raw.type.startsWith("image/")) {
|
// 创建图片预览
|
const imageUrl = URL.createObjectURL(file.raw);
|
const image = new Image();
|
image.src = imageUrl;
|
|
// 创建预览容器
|
const previewContainer = document.createElement("div");
|
previewContainer.style.position = "fixed";
|
previewContainer.style.top = "0";
|
previewContainer.style.left = "0";
|
previewContainer.style.width = "100%";
|
previewContainer.style.height = "100%";
|
previewContainer.style.backgroundColor = "rgba(0, 0, 0, 0.8)";
|
previewContainer.style.display = "flex";
|
previewContainer.style.alignItems = "center";
|
previewContainer.style.justifyContent = "center";
|
previewContainer.style.zIndex = "9999";
|
previewContainer.style.cursor = "pointer";
|
|
// 添加图片
|
previewContainer.appendChild(image);
|
image.style.maxWidth = "90%";
|
image.style.maxHeight = "90%";
|
|
// 添加关闭功能
|
previewContainer.addEventListener("click", () => {
|
URL.revokeObjectURL(imageUrl);
|
document.body.removeChild(previewContainer);
|
});
|
|
// 添加到文档
|
document.body.appendChild(previewContainer);
|
}
|
};
|
|
// 处理文件删除
|
const handleRemove = (file, fileList) => {
|
const processId = parseInt(activeProcessId.value);
|
if (processId) {
|
const processInfo = getProcessInfo(processId);
|
processInfo.files = fileList;
|
}
|
};
|
|
// 处理文件变更
|
const handleFileChange = (file, fileList) => {
|
const processId = parseInt(activeProcessId.value);
|
if (processId) {
|
const processInfo = getProcessInfo(processId);
|
processInfo.files = fileList;
|
}
|
};
|
|
// 获取字典数据
|
const getDictOptions = async dictType => {
|
if (!dictType) return [];
|
if (dictOptions.value[dictType]) return dictOptions.value[dictType];
|
|
try {
|
const res = await getDicts(dictType);
|
if (res.code === 200) {
|
dictOptions.value[dictType] = res.data;
|
return res.data;
|
}
|
return [];
|
} catch (error) {
|
console.error("获取字典数据失败:", error);
|
return [];
|
}
|
};
|
|
// 加载参数列表
|
const loadParams = (processId, orderId) => {
|
// 从已加载的工序数据中获取参数列表
|
const process = processList.value.find(
|
p => p.processId === parseInt(processId)
|
);
|
if (process) {
|
params.value = process.orderRouteItemParaVos || [];
|
|
// 初始化参数组
|
if (!form.paramGroups[processId]) {
|
form.paramGroups[processId] = [];
|
}
|
// 如果没有参数组,添加一个默认参数组
|
if (form.paramGroups[processId].length === 0) {
|
const defaultGroup = {};
|
for (const param of params.value) {
|
defaultGroup[param.id] = param.standardValue || "";
|
// 如果是字典类型参数,获取字典数据
|
if (param.paramType == "3" && param.paramFormat) {
|
getDictOptions(param.paramFormat);
|
}
|
}
|
form.paramGroups[processId].push(defaultGroup);
|
}
|
}
|
};
|
|
// 获取小数精度
|
const getPrecision = format => {
|
if (!format) return 2;
|
const match = format.match(/\.(\d+)/);
|
return match ? parseInt(match[1].length) : 2;
|
};
|
|
// 处理下一步
|
const handleNextStep = () => {
|
if (activeStep.value === 0) {
|
// 第一步:验证生产订单选择
|
if (!orderId.value) {
|
ElMessage.error("请选择生产订单");
|
return;
|
}
|
activeStep.value = 1;
|
} else if (activeStep.value === 1) {
|
// 第二步:验证基础信息
|
formRef.value.validate(valid => {
|
if (valid) {
|
activeStep.value = 2;
|
}
|
});
|
} else if (activeStep.value === 2) {
|
// 第三步:直接进入第四步
|
activeStep.value = 3;
|
}
|
};
|
|
// 处理提交
|
const handleSubmit = () => {
|
formRef.value.validate(valid => {
|
if (valid) {
|
isSubmitting.value = true;
|
// 这里可以调用API进行提交
|
setTimeout(() => {
|
ElMessage.success(data.id ? "修改成功" : "新增成功");
|
router.back();
|
isSubmitting.value = false;
|
}, 1000);
|
}
|
});
|
};
|
|
// 处理取消
|
const handleCancel = () => {
|
router.back();
|
};
|
|
// 新增参数组
|
const addParamGroup = processId => {
|
if (!form.paramGroups[processId]) {
|
form.paramGroups[processId] = [];
|
}
|
// 创建一个新的参数组,使用默认值
|
const newGroup = {};
|
params.value.forEach(param => {
|
newGroup[param.id] = param.standardValue || "";
|
});
|
form.paramGroups[processId].push(newGroup);
|
};
|
|
// 删除参数组
|
const removeParamGroup = (processId, index) => {
|
if (form.paramGroups[processId] && form.paramGroups[processId].length > 1) {
|
form.paramGroups[processId].splice(index, 1);
|
}
|
};
|
|
// 加载用户列表
|
const loadUsers = () => {
|
userLoading.value = true;
|
userListNoPage()
|
.then(res => {
|
userList.value = res.data || [];
|
})
|
.finally(() => {
|
userLoading.value = false;
|
});
|
};
|
|
// 获取当前登录人信息
|
const getCurrentUser = async () => {
|
try {
|
const res = await getInfo();
|
if (res && res.user) {
|
form.createBy = res.user.nickName || res.user.userName;
|
}
|
} catch (error) {
|
console.error("获取当前登录人信息失败:", error);
|
}
|
};
|
|
// 初始化
|
const init = () => {
|
// 无论新增还是编辑,都加载订单列表和用户列表
|
loadOrders();
|
loadUsers();
|
getCurrentUser();
|
|
if (data.id) {
|
// 编辑时设置表单数据
|
Object.assign(form, data);
|
// 设置orderId
|
orderId.value = data.orderId || "";
|
// 如果有订单ID,加载工序和参数
|
if (data.orderId) {
|
// 模拟选择订单的操作,触发数据加载
|
setTimeout(() => {
|
handleOrderChange(data.orderId);
|
}, 100);
|
}
|
} else {
|
// 新增时设置默认值
|
form.createTime = new Date();
|
}
|
// 始终从第一步开始
|
activeStep.value = 0;
|
};
|
|
// 页面加载时初始化
|
onMounted(() => {
|
init();
|
});
|
</script>
|
|
<style scoped>
|
/* 页面容器 */
|
.reporting-page {
|
min-height: 100vh;
|
padding: 24px;
|
background-color: #f0f2f5;
|
}
|
|
/* 页面头部 */
|
.page-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 32px;
|
padding-bottom: 16px;
|
border-bottom: 1px solid #e8e8e8;
|
}
|
|
.page-title {
|
margin: 0;
|
font-size: 24px;
|
font-weight: 600;
|
color: #1f2329;
|
}
|
|
.header-actions {
|
display: flex;
|
gap: 12px;
|
}
|
|
/* 步骤指示器 */
|
.step-indicator {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
margin-bottom: 32px;
|
padding: 0 16px;
|
padding-top: 16px;
|
}
|
|
.step-item {
|
display: flex;
|
align-items: center;
|
flex: 1;
|
position: relative;
|
}
|
|
.step-circle {
|
width: 40px;
|
height: 40px;
|
border-radius: 50%;
|
background-color: #f0f0f0;
|
color: #999;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
font-size: 16px;
|
font-weight: 600;
|
z-index: 2;
|
transition: all 0.3s ease;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
}
|
|
.step-item.active .step-circle {
|
background-color: #1890ff;
|
color: white;
|
box-shadow: 0 0 0 8px rgba(24, 144, 255, 0.1);
|
}
|
|
.step-item.completed .step-circle {
|
background-color: #52c41a;
|
color: white;
|
box-shadow: 0 0 0 8px rgba(82, 196, 26, 0.1);
|
}
|
|
.step-check {
|
font-size: 20px;
|
}
|
|
.step-content {
|
margin-left: 16px;
|
flex: 1;
|
}
|
|
.step-title {
|
font-size: 14px;
|
font-weight: 600;
|
color: #666;
|
margin-bottom: 4px;
|
transition: color 0.3s ease;
|
}
|
|
.step-item.active .step-title {
|
color: #1890ff;
|
}
|
|
.step-item.completed .step-title {
|
color: #52c41a;
|
}
|
|
.step-description {
|
font-size: 12px;
|
color: #999;
|
}
|
|
.step-line {
|
position: absolute;
|
top: 20px;
|
left: 50%;
|
right: -50%;
|
height: 2px;
|
background-color: #e8e8e8;
|
z-index: 1;
|
transition: all 0.3s ease;
|
}
|
|
.step-item.completed .step-line {
|
background-color: #52c41a;
|
}
|
|
/* 页面内容 */
|
.page-content {
|
background-color: white;
|
border-radius: 12px;
|
padding: 0;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
margin-bottom: 32px;
|
overflow: hidden;
|
}
|
|
/* 步骤面板 */
|
.step-panel {
|
padding: 32px;
|
}
|
|
.panel-header {
|
margin-bottom: 24px;
|
display: flex;
|
justify-content: space-between;
|
align-items: flex-start;
|
}
|
|
.panel-title {
|
font-size: 18px;
|
font-weight: 600;
|
color: #1f2329;
|
margin: 0 0 8px 0;
|
}
|
|
.panel-subtitle {
|
font-size: 14px;
|
color: #666;
|
margin: 0;
|
}
|
|
.header-actions {
|
display: flex;
|
gap: 12px;
|
align-items: center;
|
}
|
|
/* 表单容器 */
|
.form-container {
|
width: 100%;
|
}
|
|
/* 上传组件样式 */
|
.upload-block {
|
display: block;
|
}
|
|
.upload-block .el-upload__tip {
|
margin-top: 8px;
|
}
|
|
/* BOM输入组样式 */
|
.consumable-input-group {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
}
|
|
.consumable-input {
|
flex: 1;
|
}
|
|
.consumable-unit {
|
font-size: 14px;
|
color: #666;
|
white-space: nowrap;
|
}
|
|
.form-grid {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
gap: 20px;
|
}
|
.form-grid1 {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
gap: 20px;
|
}
|
|
.form-item {
|
margin-bottom: 0;
|
}
|
|
.form-select,
|
.form-input {
|
width: 100%;
|
}
|
|
.form-textarea {
|
width: 100%;
|
resize: vertical;
|
}
|
|
/* 产量输入组 */
|
.volume-input-group {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
}
|
|
.volume-input {
|
flex: 1;
|
width: 100%;
|
}
|
|
.volume-unit {
|
font-size: 14px;
|
color: #666;
|
white-space: nowrap;
|
}
|
|
/* 工序容器 */
|
.process-container {
|
display: flex;
|
gap: 24px;
|
min-height: 500px;
|
}
|
|
/* 工序导航 */
|
.process-nav {
|
width: 200px;
|
background-color: #fafafa;
|
border-radius: 8px;
|
padding: 16px;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
overflow-y: auto;
|
}
|
|
.process-nav-item {
|
padding: 12px 16px;
|
margin-bottom: 8px;
|
border-radius: 6px;
|
cursor: pointer;
|
transition: all 0.3s ease;
|
font-size: 14px;
|
font-weight: 500;
|
color: #666;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
.process-nav-item:hover {
|
background-color: #e6f7ff;
|
color: #1890ff;
|
}
|
|
.process-nav-item.active {
|
background-color: #1890ff;
|
color: white;
|
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3);
|
}
|
|
.process-badge {
|
font-size: 12px;
|
background-color: rgba(255, 255, 255, 0.2);
|
padding: 2px 8px;
|
border-radius: 10px;
|
}
|
|
/* 工序内容 */
|
.process-content {
|
flex: 1;
|
overflow-y: auto;
|
}
|
|
.empty-process {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
height: 400px;
|
}
|
|
/* 参数部分 */
|
.param-section {
|
margin-bottom: 32px;
|
padding: 24px;
|
background-color: #fafafa;
|
border-radius: 8px;
|
}
|
|
.section-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 16px;
|
}
|
|
.section-title {
|
font-size: 16px;
|
font-weight: 600;
|
color: #1f2329;
|
margin: 0;
|
}
|
|
.section-actions {
|
display: flex;
|
gap: 8px;
|
}
|
|
/* 参数表单 */
|
.param-form {
|
background-color: white;
|
padding: 20px;
|
border-radius: 8px;
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
}
|
|
/* 参数卡片 */
|
.param-cards {
|
display: flex;
|
flex-direction: column;
|
gap: 16px;
|
}
|
|
.param-card {
|
background-color: white;
|
border-radius: 8px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
overflow: hidden;
|
transition: all 0.3s ease;
|
}
|
|
.param-card:hover {
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
transform: translateY(-2px);
|
}
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 16px 20px;
|
background-color: #fafafa;
|
border-bottom: 1px solid #e8e8e8;
|
}
|
|
.card-title {
|
font-size: 14px;
|
font-weight: 600;
|
color: #1f2329;
|
}
|
|
.card-body {
|
padding: 20px;
|
}
|
|
/* 参数网格 */
|
.param-grid {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
gap: 16px;
|
}
|
|
.param-item {
|
margin-bottom: 0;
|
}
|
|
.param-input-group {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
width: 100%;
|
}
|
|
.param-input,
|
.param-select {
|
flex: 1;
|
}
|
|
.param-unit {
|
font-size: 14px;
|
color: #666;
|
white-space: nowrap;
|
}
|
|
/* 表格视图 */
|
.param-table {
|
margin: 16px 0;
|
}
|
|
.table-view {
|
border-radius: 8px;
|
overflow: hidden;
|
}
|
|
.table-view th {
|
background-color: #fafafa;
|
font-weight: 600;
|
}
|
|
.table-input,
|
.table-select {
|
width: 100%;
|
}
|
|
/* 参数操作 */
|
.param-actions {
|
margin-top: 16px;
|
display: flex;
|
justify-content: flex-end;
|
}
|
|
/* 页面底部 */
|
.page-footer {
|
display: flex;
|
justify-content: center;
|
padding: 24px;
|
background-color: white;
|
border-top: 1px solid #e8e8e8;
|
border-radius: 0 0 12px 12px;
|
}
|
|
.footer-actions {
|
display: flex;
|
gap: 12px;
|
}
|
|
/* 响应式设计 */
|
@media (max-width: 1024px) {
|
.form-grid {
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
}
|
|
.param-grid {
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
}
|
}
|
|
@media (max-width: 768px) {
|
.reporting-page {
|
padding: 16px;
|
}
|
|
.step-indicator {
|
flex-direction: column;
|
align-items: flex-start;
|
gap: 16px;
|
padding: 0;
|
}
|
|
.step-item {
|
flex-direction: row;
|
width: 100%;
|
}
|
|
.step-line {
|
display: none;
|
}
|
|
.step-panel {
|
padding: 20px;
|
}
|
|
.form-grid {
|
grid-template-columns: 1fr;
|
}
|
|
.process-container {
|
flex-direction: column;
|
}
|
|
.process-nav {
|
width: 100%;
|
margin-bottom: 16px;
|
}
|
|
.param-grid {
|
grid-template-columns: 1fr;
|
}
|
|
.footer-actions {
|
flex-direction: column;
|
width: 100%;
|
}
|
|
.footer-actions button {
|
width: 100%;
|
}
|
}
|
|
/* 滚动条样式 */
|
.process-nav::-webkit-scrollbar,
|
.process-content::-webkit-scrollbar {
|
width: 6px;
|
height: 6px;
|
}
|
|
.process-nav::-webkit-scrollbar-track,
|
.process-content::-webkit-scrollbar-track {
|
background: #f1f1f1;
|
border-radius: 3px;
|
}
|
|
.process-nav::-webkit-scrollbar-thumb,
|
.process-content::-webkit-scrollbar-thumb {
|
background: #c1c1c1;
|
border-radius: 3px;
|
}
|
|
.process-nav::-webkit-scrollbar-thumb:hover,
|
.process-content::-webkit-scrollbar-thumb:hover {
|
background: #a8a8a8;
|
}
|
|
/* 动画效果 */
|
@keyframes fadeIn {
|
from {
|
opacity: 0;
|
transform: translateY(10px);
|
}
|
to {
|
opacity: 1;
|
transform: translateY(0);
|
}
|
}
|
|
.step-panel {
|
animation: fadeIn 0.3s ease;
|
}
|
|
/* 加载状态 */
|
.loading-overlay {
|
position: fixed;
|
top: 0;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
background-color: rgba(255, 255, 255, 0.8);
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
z-index: 9999;
|
}
|
</style>
|