From 384fb98c2d78e3ef2d37af24eb9da56a4e8be8da Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期一, 23 三月 2026 11:17:56 +0800
Subject: [PATCH] Merge branch 'dev_银川_中盛建材' of http://114.132.189.42:9002/r/product-inventory-management into dev_银川_中盛建材
---
src/views/productionManagement/productionReporting/components/ReportingDialog.vue | 588 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 588 insertions(+), 0 deletions(-)
diff --git a/src/views/productionManagement/productionReporting/components/ReportingDialog.vue b/src/views/productionManagement/productionReporting/components/ReportingDialog.vue
new file mode 100644
index 0000000..8eff508
--- /dev/null
+++ b/src/views/productionManagement/productionReporting/components/ReportingDialog.vue
@@ -0,0 +1,588 @@
+<template>
+ <el-dialog v-model="localVisible"
+ :title="dialogTitle"
+ width="800px"
+ @close="handleClose">
+ <!-- 姝ラ鏉� -->
+ <el-steps :active="activeStep"
+ finish-status="success">
+ <el-step title="閫夋嫨鐢熶骇璁㈠崟" />
+ <el-step title="濉啓鍩虹淇℃伅" />
+ <el-step title="鏌ョ湅宸ュ簭鍙傛暟" />
+ <el-step title="濉啓浜ч噺淇℃伅" />
+ </el-steps>
+ <!-- 绗竴姝ワ細閫夋嫨鐢熶骇璁㈠崟 -->
+ <div v-if="activeStep === 0">
+ <el-form :model="form"
+ ref="formRef"
+ label-width="120px">
+ <el-form-item label="鐢熶骇璁㈠崟"
+ prop="orderId"
+ required>
+ <el-select v-model="orderId"
+ placeholder="璇烽�夋嫨鐢熶骇璁㈠崟"
+ clearable
+ filterable
+ style="width: 100%"
+ :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">
+ <el-form :model="form"
+ :rules="rules"
+ ref="formRef"
+ label-width="120px">
+ <el-form-item label="鐢熶骇璁㈠崟鍙�"
+ prop="npsNo">
+ <el-input disabled
+ v-model="form.npsNo" />
+ </el-form-item>
+ <el-form-item label="鐝粍"
+ prop="teamName"
+ required>
+ <el-select v-model="form.teamName"
+ placeholder="璇烽�夋嫨鐝粍"
+ style="width: 100%">
+ <el-option label="鐧界彮"
+ value="鐧界彮" />
+ <el-option label="澶滅彮"
+ value="澶滅彮" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="浜у搧缂栫爜"
+ prop="materialCode">
+ <el-input disabled
+ v-model="form.materialCode" />
+ </el-form-item>
+ <el-form-item label="浜у搧鍚嶇О"
+ prop="productName">
+ <el-input disabled
+ v-model="form.productName" />
+ </el-form-item>
+ <el-form-item label="瑙勬牸"
+ prop="specification">
+ <el-input disabled
+ v-model="form.specification" />
+ </el-form-item>
+ <el-form-item label="鍒涘缓浜�"
+ prop="createBy"
+ required>
+ <el-input v-model="form.createBy"
+ placeholder="璇疯緭鍏ュ垱寤轰汉" />
+ </el-form-item>
+ <el-form-item label="鍒涘缓鏃堕棿"
+ prop="createTime">
+ <el-date-picker disabled
+ v-model="form.createTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨鍒涘缓鏃堕棿"
+ style="width: 100%" />
+ </el-form-item>
+ </el-form>
+ </div>
+ <!-- 绗笁姝ワ細鏌ョ湅宸ュ簭鍙傛暟 -->
+ <div v-else-if="activeStep === 2">
+ <!-- 宸ュ簭Tab椤� -->
+ <el-tabs v-model="activeProcessId"
+ @tab-click="handleTabClick">
+ <el-tab-pane v-for="process in processList"
+ :key="process.id"
+ :label="process.processName"
+ :name="process.id + ''">
+ <div>
+ <!-- 鍙傛暟缁勫垪琛� -->
+ <div v-for="(group, groupIndex) in form.paramGroups[process.id] || []"
+ :key="groupIndex"
+ class="param-group">
+ <div class="group-header">
+ <span>鍙傛暟缁� {{ groupIndex + 1 }}</span>
+ <el-button type="danger"
+ size="small"
+ @click="removeParamGroup(process.id, groupIndex)"
+ v-if="(form.paramGroups[process.id] || []).length > 1">
+ 鍒犻櫎
+ </el-button>
+ </div>
+ <el-form :model="form"
+ ref="formRef"
+ label-width="120px">
+ <!-- 鍔ㄦ�佸弬鏁� -->
+ <el-form-item v-for="param in params"
+ :key="param.id"
+ :label="param.paramName">
+ <template v-if="param.paramType == '1'">
+ <!-- 鏁板瓧绫诲瀷 -->
+ <div style="display: flex; align-items: center; gap: 8px;">
+ <el-input-number v-model="group[param.id]"
+ controls-position="right"
+ :precision="getPrecision(param.paramFormat)"
+ style="flex: 1" />
+ <span v-if="param.unit && param.unit != '/'">
+ {{ param.unit }}
+ </span>
+ </div>
+ </template>
+ <template v-else-if="param.paramType == '2'">
+ <!-- 鏂囨湰绫诲瀷 -->
+ <div style="display: flex; align-items: center; gap: 8px;">
+ <el-input v-model="group[param.id]"
+ style="flex: 1" />
+ <span v-if="param.unit && param.unit != '/'">
+ {{ param.unit }}
+ </span>
+ </div>
+ </template>
+ <template v-else-if="param.paramType == '3'">
+ <!-- 瀛楀吀绫诲瀷 -->
+ <div style="display: flex; align-items: center; gap: 8px;">
+ <el-select v-model="group[param.id]"
+ placeholder="璇烽�夋嫨"
+ style="flex: 1;width: 150px">
+ <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 != '/'">
+ {{ param.unit }}
+ </span>
+ </div>
+ </template>
+ <template v-else-if="param.paramType == '4'">
+ <!-- 鏃ユ湡绫诲瀷 -->
+ <div style="display: flex; align-items: center; gap: 8px;">
+ <el-date-picker :value-format="param.paramFormat"
+ :format="param.paramFormat"
+ :type="param.paramFormat=='YYYY-MM-DD'?'daterange':'datetimerange'"
+ v-model="group[param.id]"
+ style="flex: 1" />
+ <span v-if="param.unit && param.unit != '/'">
+ {{ param.unit }}
+ </span>
+ </div>
+ </template>
+ <template v-else>
+ <!-- 鍏朵粬绫诲瀷 -->
+ <div style="display: flex; align-items: center; gap: 8px;">
+ <el-input v-model="group[param.id]"
+ style="flex: 1" />
+ <span v-if="param.unit && param.unit != '/'">
+ {{ param.unit }}
+ </span>
+ </div>
+ </template>
+ </el-form-item>
+ </el-form>
+ </div>
+ <!-- 鏂板鍙傛暟缁勬寜閽� -->
+ <el-button type="primary"
+ size="small"
+ @click="addParamGroup(process.id)">
+ 鏂板鍙傛暟缁�
+ </el-button>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+ </div>
+ <!-- 绗洓姝ワ細濉啓浜ч噺淇℃伅 -->
+ <div v-else-if="activeStep === 3">
+ <el-form :model="form"
+ :rules="rules"
+ ref="formRef"
+ label-width="120px">
+ <el-form-item label="浜у嚭鏂归噺"
+ prop="outputVolume"
+ required>
+ <el-input-number v-model="form.outputVolume"
+ :min="0"
+ :precision="2"
+ style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="涓嶅悎鏍兼柟閲�"
+ prop="unqualifiedVolume"
+ required>
+ <el-input-number v-model="form.unqualifiedVolume"
+ :min="0"
+ :precision="2"
+ style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="瀹屾垚鏂归噺"
+ prop="completedVolume"
+ required>
+ <el-input-number v-model="form.completedVolume"
+ :min="0"
+ :precision="2"
+ style="width: 100%" />
+ </el-form-item>
+ </el-form>
+ </div>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="handleClose">鍙� 娑�</el-button>
+ <el-button type="primary"
+ v-if="activeStep > 0"
+ @click="activeStep--">涓婁竴姝�</el-button>
+ <el-button type="primary"
+ v-if="activeStep < 3"
+ @click="handleNextStep">涓嬩竴姝�</el-button>
+ <el-button type="primary"
+ v-if="activeStep === 3"
+ :loading="submitLoading"
+ @click="handleSubmit">纭� 璁�</el-button>
+ </span>
+ </template>
+ </el-dialog>
+</template>
+
+<script setup>
+ import { ref, reactive, computed, watch } from "vue";
+ import { ElMessage } from "element-plus";
+ import { getDicts } from "@/api/system/dict/data";
+ import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
+ import {
+ findProductProcessRouteItemList,
+ findProcessParamListOrder,
+ } from "@/api/productionManagement/productProcessRoute.js";
+
+ const props = defineProps({
+ visible: {
+ type: Boolean,
+ default: false,
+ },
+ data: {
+ type: Object,
+ default: () => ({}),
+ },
+ });
+
+ const emit = defineEmits(["update:visible", "completed"]);
+
+ const dialogTitle = computed(() => (props.data.id ? "缂栬緫鎶ュ伐" : "鏂板鎶ュ伐"));
+
+ const formRef = ref(null);
+ const submitLoading = ref(false);
+ const orderLoading = ref(false);
+ const processLoading = ref(false);
+ const localVisible = ref(props.visible);
+ const activeStep = ref(0);
+
+ const orderId = ref(props.data.orderId || "");
+ const processId = ref(props.data.processId || "");
+ const activeProcessId = ref("");
+ const orderList = ref([]);
+ const processList = ref([]);
+ const params = ref([]);
+ const dictOptions = ref({});
+
+ const form = reactive({
+ id: props.data.id || undefined,
+ orderId: props.data.orderId || "",
+ npsNo: props.data.npsNo || "",
+ teamName: props.data.teamName || "",
+ materialCode: props.data.materialCode || "",
+ productName: props.data.productName || "",
+ specification: props.data.specification || "",
+ outputVolume: props.data.outputVolume || 0,
+ unqualifiedVolume: props.data.unqualifiedVolume || 0,
+ completedVolume: props.data.completedVolume || 0,
+ createBy: props.data.createBy || "褰撳墠鐧诲綍浜�",
+ createTime: props.data.createTime || new Date(),
+ paramGroups: props.data.paramGroups || {}, // 瀛樺偍姣忎釜宸ュ簭鐨勫弬鏁扮粍
+ });
+
+ 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;
+ findProductProcessRouteItemList({ orderId })
+ .then(res => {
+ processList.value = res.data || [];
+ // 濡傛灉鏈夊伐搴忥紝榛樿閫夋嫨绗竴涓�
+ if (processList.value.length > 0) {
+ const firstProcess = processList.value[0];
+ activeProcessId.value = firstProcess.id + "";
+ processId.value = firstProcess.id;
+ form.processId = firstProcess.id;
+ // 鍔犺浇绗竴涓伐搴忕殑鍙傛暟
+ loadParams(firstProcess.id, orderId);
+ }
+ })
+ .finally(() => {
+ processLoading.value = false;
+ });
+ };
+
+ // 澶勭悊tab椤电偣鍑�
+ const handleTabClick = tab => {
+ const selectedProcessId = parseInt(tab.paneName);
+ processId.value = selectedProcessId;
+ form.processId = selectedProcessId;
+ // 鍔犺浇鍙傛暟鍒楄〃
+ loadParams(selectedProcessId, form.orderId);
+ };
+
+ // 鑾峰彇瀛楀吀鏁版嵁
+ 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) => {
+ findProcessParamListOrder({ orderId, routeItemId: processId }).then(
+ async res => {
+ params.value = res.data || [];
+ // 鍒濆鍖栧弬鏁扮粍
+ 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) {
+ await 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) {
+ submitLoading.value = true;
+ // 杩欓噷鍙互璋冪敤API杩涜鎻愪氦
+ setTimeout(() => {
+ ElMessage.success(props.data.id ? "淇敼鎴愬姛" : "鏂板鎴愬姛");
+ localVisible.value = false;
+ emit("completed");
+ submitLoading.value = false;
+ }, 1000);
+ }
+ });
+ };
+
+ // 澶勭悊鍏抽棴
+ const handleClose = () => {
+ // 閲嶇疆鍒板垵濮嬬姸鎬�
+ activeStep.value = 0;
+ orderId.value = "";
+ processId.value = "";
+ activeProcessId.value = "";
+ processList.value = [];
+ params.value = [];
+ Object.assign(form, {
+ id: undefined,
+ orderId: "",
+ npsNo: "",
+ teamName: "",
+ materialCode: "",
+ productName: "",
+ specification: "",
+ outputVolume: 0,
+ unqualifiedVolume: 0,
+ completedVolume: 0,
+ createBy: "褰撳墠鐧诲綍浜�",
+ createTime: new Date(),
+ params: {},
+ });
+ localVisible.value = false;
+ };
+
+ // 鏂板鍙傛暟缁�
+ 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 init = () => {
+ // 鏃犺鏂板杩樻槸缂栬緫锛岄兘鍔犺浇璁㈠崟鍒楄〃
+ loadOrders();
+
+ if (props.data.id) {
+ // 缂栬緫鏃惰缃〃鍗曟暟鎹�
+ Object.assign(form, props.data);
+ // 璁剧疆orderId
+ orderId.value = props.data.orderId || "";
+ // 濡傛灉鏈夎鍗旾D锛屽姞杞藉伐搴忓拰鍙傛暟
+ if (props.data.orderId) {
+ // 妯℃嫙閫夋嫨璁㈠崟鐨勬搷浣滐紝瑙﹀彂鏁版嵁鍔犺浇
+ setTimeout(() => {
+ handleOrderChange(props.data.orderId);
+ }, 100);
+ }
+ } else {
+ // 鏂板鏃惰缃粯璁ゅ��
+ form.createBy = "褰撳墠鐧诲綍浜�";
+ form.createTime = new Date();
+ }
+ // 濮嬬粓浠庣涓�姝ュ紑濮�
+ activeStep.value = 0;
+ };
+
+ // 鐩戝惉props.visible鍙樺寲
+ watch(
+ () => props.visible,
+ newVal => {
+ localVisible.value = newVal;
+ if (newVal) {
+ init();
+ }
+ }
+ );
+
+ // 鐩戝惉localVisible鍙樺寲
+ watch(localVisible, newVal => {
+ emit("update:visible", newVal);
+ });
+</script>
+
+<style scoped>
+ .dialog-footer {
+ text-align: right;
+ }
+
+ .param-group {
+ border: 1px solid #e4e7ed;
+ border-radius: 4px;
+ padding: 16px;
+ margin-bottom: 16px;
+ background-color: #f9f9f9;
+ }
+
+ .group-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #e4e7ed;
+ }
+
+ .group-header span {
+ font-weight: bold;
+ font-size: 14px;
+ }
+</style>
\ No newline at end of file
--
Gitblit v1.9.3