From 7ffa19f1fe3b37519e83ed1f86715154b13c00f3 Mon Sep 17 00:00:00 2001
From: maven <2163098428@qq.com>
Date: 星期二, 26 八月 2025 15:16:10 +0800
Subject: [PATCH] yys 生产管控(完成基础逻辑)
---
src/views/production/components/ProductionDetailsTable.vue | 30
src/views/warehouseManagement/index.vue | 31
src/views/production/productionReporting/components/useDialog.js | 62 +
src/views/production/operationScheduling/components/useTableData.js | 132 +++
src/views/production/productionReporting/components/ProductionDialog.vue | 348 ++++++++
src/views/production/operationScheduling/index.vue | 250 ++++++
src/views/production/productionReporting/components/useTableData.js | 132 +++
src/api/productionScheduling/index.js | 38
src/views/production/operationScheduling/components/ProductionDetailsTable.vue | 360 +++++++++
src/views/production/productionReporting/components/useCoalData.js | 96 ++
src/components/Table/ETable.vue | 4
src/views/production/operationScheduling/components/ProductionDialog.vue | 410 ++++++++++
src/views/procureMent/index.vue | 8
src/views/production/operationScheduling/components/useDialog.js | 62 +
src/views/procureMent/components/ProductionDialog.vue | 25
src/views/production/operationScheduling/components/useCoalData.js | 96 ++
src/views/production/productionReporting/index.vue | 277 ++++++
17 files changed, 2,361 insertions(+), 0 deletions(-)
diff --git a/src/api/productionScheduling/index.js b/src/api/productionScheduling/index.js
new file mode 100644
index 0000000..3a2adb5
--- /dev/null
+++ b/src/api/productionScheduling/index.js
@@ -0,0 +1,38 @@
+// 鐢熶骇浠诲姟锛屾姤宸�
+import request from '@/utils/request'
+
+// 鏌ヨ鐢熶骇鍔犲伐-姝e紡搴撶叅绉嶅垪琛�
+export function listPage(query) {
+ return request({
+ url: '/productionScheduling/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 鎺掍骇
+export function addProductionScheduling(data) {
+ return request({
+ url: '/productionScheduling/addProductionScheduling',
+ method: 'post',
+ data: data
+ })
+}
+
+// 鍒犻櫎
+export function delProductionScheduling(data) {
+ return request({
+ url: '/productionScheduling/delProductionScheduling',
+ method: 'delete',
+ data: data
+ })
+}
+
+//鎶ュ伐
+export function work(data) {
+ return request({
+ url: '/productionScheduling/work',
+ method: 'post',
+ data: data
+ })
+}
\ No newline at end of file
diff --git a/src/components/Table/ETable.vue b/src/components/Table/ETable.vue
index 3921d6b..b7089d2 100644
--- a/src/components/Table/ETable.vue
+++ b/src/components/Table/ETable.vue
@@ -53,6 +53,10 @@
<!-- 榛樿鎿嶄綔鎸夐挳 -->
<el-button v-if="operations.includes('edit')" link type="primary" size="small"
@click="handleEdit(scope.row)">缂栬緫</el-button>
+ <el-button v-if="operations.includes('scheduling') && scope.row.status != 3" link type="primary" size="small"
+ @click="handleEdit(scope.row)">鎺掍骇</el-button>
+ <el-button v-if="operations.includes('work') && scope.row.status != 3" link type="primary" size="small"
+ @click="handleEdit(scope.row)">鎶ュ伐</el-button>
<el-button v-if="operations.includes('viewRow')" link type="primary" size="small"
@click="handleView(scope.row)">鏌ョ湅</el-button>
<el-button v-if="operations.includes('viewFile')" link type="primary" size="small"
diff --git a/src/views/procureMent/components/ProductionDialog.vue b/src/views/procureMent/components/ProductionDialog.vue
index 859cbea..79b2bac 100644
--- a/src/views/procureMent/components/ProductionDialog.vue
+++ b/src/views/procureMent/components/ProductionDialog.vue
@@ -30,6 +30,20 @@
/>
</el-select>
</el-form-item>
+ <el-form-item label="鐓ゆ枡绫诲瀷" prop="type">
+ <el-select
+ v-model="form.type"
+ placeholder="璇烽�夋嫨鐓ょ"
+ :disabled="isViewMode"
+ >
+ <el-option
+ :label="item.label"
+ v-for="item in typeList"
+ :key="item.value"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
<el-form-item label="鐓ょ" prop="coalId">
<el-select
v-model="form.coalId"
@@ -183,6 +197,16 @@
required: true,
type: Boolean,
});
+const typeList = [
+ {
+ value: 1,
+ label: "鎴愬搧",
+ },
+ {
+ value: 2,
+ label: "鍘熸枡",
+ },
+];
const form = defineModel("form", {
required: true,
type: Object,
@@ -343,6 +367,7 @@
supplierName: [
{ required: true, message: "璇疯緭鍏ヤ緵搴斿晢鍚嶇О", trigger: "blur" },
],
+ type: [{ required: true, message: "璇烽�夋嫨绫诲瀷", trigger: "change" }],
coal: [{ required: true, message: "璇疯緭鍏ョ叅绉�", trigger: "blur" }],
purchaseQuantity: [
{ required: true, message: "璇疯緭鍏ラ噰璐暟閲�", trigger: "blur" },
diff --git a/src/views/procureMent/index.vue b/src/views/procureMent/index.vue
index 159c0fc..e067737 100644
--- a/src/views/procureMent/index.vue
+++ b/src/views/procureMent/index.vue
@@ -145,6 +145,14 @@
},
},
{
+ prop: "type",
+ label: "鐓ゆ枡绫诲瀷",
+ minWidth: 120,
+ formatter: (row) => {
+ return row.type === 1 ? "鎴愬搧" : "鍘熸枡";
+ },
+ },
+ {
prop: "coalId",
label: "鐓ょ绫诲瀷",
minWidth: 120,
diff --git a/src/views/production/components/ProductionDetailsTable.vue b/src/views/production/components/ProductionDetailsTable.vue
index 48308f4..43be265 100644
--- a/src/views/production/components/ProductionDetailsTable.vue
+++ b/src/views/production/components/ProductionDetailsTable.vue
@@ -1,5 +1,24 @@
<template>
<el-table :data="tableData" :border="border" style="width: 100%">
+ <el-table-column label="鐓ゆ枡绫诲瀷" min-width="120">
+ <template #default="{ row, $index }">
+ <el-select
+ clearable
+ v-model="row.type"
+ placeholder="璇烽�夋嫨鐓ゆ枡绫诲瀷"
+ filterable
+ :key="`coalId-select-${$index}-${typeList.length}`"
+ :disabled="isViewMode"
+ >
+ <el-option
+ v-for="(item, index) of typeList"
+ :key="`option-${index}-${item.value}`"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </template>
+ </el-table-column>
<el-table-column label="鐓ょ" min-width="120">
<template #default="{ row, $index }">
<el-select
@@ -164,6 +183,16 @@
import {getCoalInfoList} from "@/api/production";
import {userListAll} from "@/api/publicApi";
+const typeList = [
+ {
+ label: "鎴愬搧",
+ value: 1,
+ },
+ {
+ label: "鍘熸枡",
+ value: 2,
+ }
+ ]
const props = defineProps({
modelValue: {
type: Array,
@@ -387,6 +416,7 @@
purchasePrice: "",
totalCost: "",
producerId: "",
+ type: 1,
...rowData,
};
tableData.value = [...tableData.value, defaultRow];
diff --git a/src/views/production/operationScheduling/components/ProductionDetailsTable.vue b/src/views/production/operationScheduling/components/ProductionDetailsTable.vue
new file mode 100644
index 0000000..72b4064
--- /dev/null
+++ b/src/views/production/operationScheduling/components/ProductionDetailsTable.vue
@@ -0,0 +1,360 @@
+<template>
+ <el-table :data="tableData" :border="border" style="width: 100%">
+ <el-table-column label="宸ュ簭" min-width="120">
+ <template #default="{ row, $index }">
+ <el-select
+ clearable
+ v-model="row.process"
+ placeholder="璇烽�夋嫨宸ュ簭"
+ filterable
+ :disabled="isViewMode"
+ >
+ <el-option
+ v-for="(item, index) of process_list"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍗曚綅" min-width="120">
+ <template #default="{ row, $index }">
+ <el-input
+ v-model="row.unit"
+ placeholder="璇疯緭鍏ュ崟浣�"
+ :disabled="isViewMode"
+ />
+ </template>
+
+ </el-table-column>
+ <el-table-column label="鎺掍骇鏁伴噺" min-width="120">
+ <template #default="{ row, $index }">
+ <el-input
+ v-model="row.schedulingNum"
+ placeholder="璇疯緭鍏ユ帓浜ф暟閲�"
+ type="number"
+ :disabled="isViewMode"
+ />
+ </template>
+ </el-table-column>
+
+ <el-table-column label="宸ユ椂瀹氶" min-width="120">
+ <template #default="{ row, $index }">
+ <el-input
+ v-model="row.workHours"
+ placeholder="璇疯緭鍏ュ伐鏃跺畾棰�"
+ type="number"
+ @input="handleInput('workHours', $index, $event)"
+ :disabled="isViewMode"
+ >
+ </el-input>
+ </template>
+ </el-table-column>
+
+ <el-table-column label="鎺掍骇鏃ユ湡" min-width="120">
+ <template #default="{ row, $index }">
+ <el-date-picker
+ v-model="row.schedulingDate"
+ type="datetime"
+ clearable
+ placeholder="閫夋嫨鏃ユ湡"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ />
+ </template>
+ </el-table-column>
+
+ <el-table-column label="鎺掍骇浜�" min-width="120">
+ <template #default="{ row, $index }">
+ <el-select
+ clearable
+ v-model="row.schedulingUserId"
+ placeholder="璇烽�夋嫨鎺掍骇浜�"
+ filterable
+ :key="`producer-select-${$index}-${userList.length}`"
+ :disabled="isViewMode"
+ >
+ <el-option
+ v-for="(item, index) of userList"
+ :key="`option-${index}-${item.key}`"
+ :label="item.value"
+ :value="item.key"
+ />
+ </el-select>
+ </template>
+ </el-table-column>
+ <el-table-column
+ label="鎿嶄綔"
+ width="120"
+ fixed="right"
+ v-if="dialogType !== 'viewRow'"
+ >
+ <template #default="{ $index }">
+ <el-button
+ type="danger"
+ size="small"
+ @click="handleDelete($index)"
+ :icon="Delete"
+ >
+ 鍒犻櫎
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+</template>
+
+<script setup name="ProductionDetailsTable">
+import {ref, computed, watch, onMounted, nextTick} from "vue";
+import {Delete} from "@element-plus/icons-vue";
+import {ElMessage} from "element-plus";
+import {getCoalFieldList} from "@/api/basicInformation/coalQualityMaintenance";
+import {userListAll} from "@/api/publicApi";
+const { proxy } = getCurrentInstance();
+const {process_list} = proxy.useDict("process_list");
+
+const props = defineProps({
+ modelValue: {
+ type: Array,
+ default: () => [],
+ },
+ border: {
+ type: Boolean,
+ default: false,
+ },
+ showOperations: {
+ type: Boolean,
+ default: true,
+ },
+ autoCalculate: {
+ type: Boolean,
+ default: true,
+ },
+ dialogType:{
+ type: String,
+ default:'add'
+ }
+});
+const isViewMode = computed(() => props.dialogType === "viewRow");
+const emit = defineEmits(["update:modelValue", "input-change", "delete-row"]);
+
+// 浣跨敤 v-model 杩涜鍙屽悜缁戝畾
+const tableData = computed({
+ get() {
+ return props.modelValue;
+ },
+ set(value) {
+ emit("update:modelValue", value);
+ },
+});
+
+// 澶勭悊杈撳叆鍙樺寲
+const handleInput = (field, index, value) => {
+ // 纭繚杈撳叆鍊兼槸鏁板瓧鎴栫┖瀛楃涓茶�屼笖闈炶礋鏁�
+ if (!/^\d*\.?\d*$/.test(value) && value !== "") {
+ ElMessage.error("璇疯緭鍏ユ湁鏁堢殑鏁板瓧");
+ return;
+ }
+ const newData = [...tableData.value];
+ newData[index][field] = value;
+
+ // 濡傛灉寮�鍚嚜鍔ㄨ绠楁�绘垚鏈�
+ if (
+ props.autoCalculate &&
+ [
+ "laborCost",
+ "energyCost",
+ "equipmentDepreciation",
+ "purchasePrice",
+ ].includes(field)
+ ) {
+ calculateTotalCost(newData[index]);
+ }
+
+ tableData.value = newData;
+ emit("input-change", {field, index, value, row: newData[index]});
+};
+
+// 璁$畻鎬绘垚鏈�
+const calculateTotalCost = (row) => {
+ const laborCost = parseFloat(row.laborCost) || 0;
+ const energyCost = parseFloat(row.energyConsumptionCost) || 0;
+ const equipmentDepreciation = parseFloat(row.equipmentDepreciation) || 0;
+ const purchasePrice = parseFloat(row.purchasePrice) || 0;
+
+ row.totalCost = (
+ laborCost +
+ energyCost +
+ equipmentDepreciation +
+ purchasePrice
+ ).toFixed(2);
+};
+
+// 鍒犻櫎琛�
+const handleDelete = (index) => {
+ const newData = [...tableData.value];
+ newData.splice(index, 1);
+ tableData.value = newData;
+ emit("delete-row", index);
+};
+
+// 澶勭悊鐓ょ閫夋嫨鍙樺寲
+
+// 澶勭悊鐓ょ閫夋嫨鍙樺寲锛堟柊鏂规硶锛氬悕绉伴�夋嫨杞琁D锛�
+const handleCoalSelectChange = (row, selectedName) => {
+ // 鏍规嵁閫夋嫨鐨勫悕绉版壘鍒板搴旂殑ID
+ const coalItem = weekList.value.find(item => item.value === selectedName);
+ if (coalItem) {
+ row.coalId = coalItem.key; // 璁剧疆涓篒D
+ } else {
+ row.coalId = ''; // 濡傛灉娌℃壘鍒帮紝娓呯┖
+ }
+};
+
+// 鏍规嵁ID鑾峰彇鐓ょ鍚嶇О锛堢敤浜庢樉绀猴級
+const getCoalNameById = (id) => {
+ const coalId = weekList.value.find(item => item.key == id);
+ return coalId ? coalId.value : id;
+};
+
+const weekList = ref([]);
+
+
+// 鐩戝惉琛ㄦ牸鏁版嵁鍙樺寲锛岀‘淇濇樉绀烘纭�
+watch(() => props.modelValue, (newValue) => {
+ if (newValue && weekList.value.length > 0) {
+ // 褰撴暟鎹姞杞藉畬鎴愪笖weekList宸茶幏鍙栨椂锛岀‘淇濇樉绀烘纭�
+ }
+}, {deep: true});
+
+// 鐩戝惉weekList鍙樺寲锛屽綋涓嬫媺鏁版嵁鍔犺浇瀹屾垚鍚庡鐞嗘樉绀�
+watch(weekList, (newList) => {
+ if (newList.length > 0 && tableData.value.length > 0) {
+ // 寮哄埗瑙﹀彂琛ㄦ牸閲嶆柊娓叉煋浠ョ‘淇漞l-select姝g‘鏄剧ず
+ nextTick(() => {
+ // 瑙﹀彂涓�涓井灏忕殑鏁版嵁鍙樺寲鏉ュ己鍒堕噸鏂版覆鏌�
+ const tempData = [...tableData.value];
+ tableData.value = tempData;
+ });
+ }
+}, {deep: true});
+
+onMounted(async () => {
+ let ress = await userListAll();
+ ress.data.forEach(item => {
+ let obj = {};
+ obj.value = item.nickName;
+ obj.key = item.userId;
+ userList.value.push(obj);
+ });
+})
+const dropdownList = ref([]);
+// 鑾峰彇涓嬫媺鏁版嵁
+const getDropdownData = async () => {
+ let res = await getCoalFieldList();
+ if (res.code === 200) {
+ dropdownList.value = res.data.map((item) => ({
+ value: item.coal,
+ key: item.id,
+ }));
+ } else {
+ ElMessage.error("鑾峰彇涓嬫媺鏁版嵁澶辫触");
+ }
+};
+const userList = ref([]);
+const getUserList = (async () => {
+ let res = await userListAll();
+ if (res.code === 200) {
+ userList.value = res.data.map((item) => ({
+ value: item.nickName,
+ key: item.userId,
+ }));
+ } else {
+ ElMessage.error("鑾峰彇鐢ㄦ埛鍒楄〃澶辫触");
+ }
+})
+// 鐩戝惉琛ㄦ牸鏁版嵁鍙樺寲锛岀‘淇濇樉绀烘纭�
+watch(() => props.modelValue, (newValue) => {
+ if (newValue && userList.value.length > 0) {
+ // 褰撴暟鎹姞杞藉畬鎴愪笖weekList宸茶幏鍙栨椂锛岀‘淇濇樉绀烘纭�
+ }
+}, {deep: true});
+
+// 鐩戝惉userList鍙樺寲锛屽綋涓嬫媺鏁版嵁鍔犺浇瀹屾垚鍚庡鐞嗘樉绀�
+watch(userList, (newList) => {
+ if (newList.length > 0 && tableData.value.length > 0) {
+ // 寮哄埗瑙﹀彂琛ㄦ牸閲嶆柊娓叉煋浠ョ‘淇漞l-select姝g‘鏄剧ず
+ nextTick(() => {
+ // 瑙﹀彂涓�涓井灏忕殑鏁版嵁鍙樺寲鏉ュ己鍒堕噸鏂版覆鏌�
+ const tempData = [...tableData.value];
+ tableData.value = tempData;
+ });
+ }
+}, {deep: true});
+
+const getUserNameById = (id) => {
+ const producer = userList.value.find(item => item.key == id);
+ return producer ? producer.value : id;
+};
+// 澶勭悊鐢ㄦ埛閫夋嫨鍙樺寲锛堟柊鏂规硶锛氬悕绉伴�夋嫨杞琁D锛�
+const handleUserSelectChange = (row, selectedName) => {
+ console.log("handleUserSelectChange", row, selectedName);
+ // 鏍规嵁閫夋嫨鐨勫悕绉版壘鍒板搴旂殑ID
+ const userItem = userList.value.find(item => item.value === selectedName);
+ if (userItem) {
+ row.producerId = userItem.key; // 璁剧疆涓篒D
+ } else {
+ row.producerId = ''; // 濡傛灉娌℃壘鍒帮紝娓呯┖
+ }
+};
+// 鏆撮湶鏂规硶缁欑埗缁勪欢浣跨敤
+defineExpose({
+ calculateTotalCost,
+ getDropdownData,
+ getUserList,
+ getCoalNameById, // 鏆撮湶鑾峰彇鐓ょ鍚嶇О鐨勬柟娉�
+ weekList, // 鏆撮湶weekList璁╃埗缁勪欢鍙互璁块棶
+ addRow: (rowData = {}) => {
+ const defaultRow = {
+ process: "宸ュ簭1",
+ unit: "椤�",
+ schedulingNum: "",
+ workHours: "",
+ schedulingDate: "",
+ schedulingUserId: "",
+ schedulingUserName: ""
+ };
+ tableData.value = [...tableData.value, defaultRow];
+ },
+ clearData: () => {
+ tableData.value = [];
+ },
+ // 娣诲姞涓�涓柟娉曟潵绛夊緟weekList鍔犺浇瀹屾垚
+ waitForWeekList: () => {
+ return new Promise((resolve) => {
+ if (weekList.value.length > 0) {
+ resolve();
+ } else {
+ const unwatch = watch(weekList, (newList) => {
+ if (newList.length > 0) {
+ unwatch();
+ resolve();
+ }
+ });
+ }
+ });
+ },
+ // 寮哄埗鍒锋柊琛ㄦ牸鏄剧ず
+ forceRefresh: () => {
+ nextTick(() => {
+ const tempData = [...tableData.value];
+ tableData.value = tempData;
+ });
+ }
+});
+</script>
+
+<style scoped>
+:deep(.el-table .el-table__cell) {
+ padding: 8px 0;
+}
+</style>
diff --git a/src/views/production/operationScheduling/components/ProductionDialog.vue b/src/views/production/operationScheduling/components/ProductionDialog.vue
new file mode 100644
index 0000000..4e26ec9
--- /dev/null
+++ b/src/views/production/operationScheduling/components/ProductionDialog.vue
@@ -0,0 +1,410 @@
+<template>
+ <el-dialog
+ v-model="dialogVisible"
+ title="宸ュ簭鎺掍骇"
+ width="1200px"
+ :close-on-click-modal="false"
+ @close="handleClose"
+ >
+ <div class="empty-table">
+ <el-row :gutter="10">
+ <el-col :span="2">
+ <el-button type="primary" @click="addNewRow">
+ <el-icon>
+ <Plus/>
+ </el-icon>
+ 鏂板
+ </el-button>
+ </el-col>
+ <el-col :span="4">
+ <div style="font-size: 16px;">寰呮帓浜ф暟閲忥細{{productionQuantity}}</div>
+ </el-col>
+ <!-- <el-col :span="2">
+ <el-button type="danger" @click="clearAllRows">
+ <el-icon>
+ <Delete />
+ </el-icon>
+ 娓呯┖
+ </el-button>
+ </el-col> -->
+ </el-row>
+ <ProductionDetailsTable
+ v-model="detailsTableData"
+ :border="false"
+ :show-operations="dialogType !== 'viewRow'"
+ :auto-calculate="true"
+ @input-change="handleDetailsChange"
+ @delete-row="handleDeleteRow"
+ :dialogType="dialogType"
+ />
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button
+ @click="handleClose"
+ >{{ dialogType === 'viewRow' ? '鍏� 闂�' : '鍙� 娑�' }}
+ </el-button
+ >
+ <!-- <el-button @click="handleReset" v-if="dialogType === 'edit'"
+ >閲� 缃�</el-button
+ > -->
+ <el-button
+ v-if="dialogType !== 'viewRow'"
+ type="primary"
+ :loading="loading"
+ @click="handleSubmit"
+ >纭� 瀹�
+ </el-button
+ >
+ </div>
+ </template>
+ </el-dialog>
+ <el-dialog
+ v-model="innerVisible"
+ width="1000"
+ title="閫夋嫨閰嶇疆鏁版嵁"
+ center
+ append-to-body
+ >
+ <div style="margin-bottom: 10px">
+ <el-alert
+ v-if="tableData.length > 0"
+ :title="`褰撳墠宸查�夋嫨 ${tableData.length} 鏉℃暟鎹甡"
+ type="info"
+ :closable="false"
+ show-icon
+ />
+ </div>
+ <ETable
+ :showIndex="false"
+ :showOverflowTooltip="false"
+ @selection-change="handleSelectionChange"
+ :showOperations="false"
+ ref="etableRef"
+ :tableData="formalDatabaseData"
+ :defaultSelectedIds="selectedIds"
+ :rowKey="'id'"
+ height="400"
+ @cell-edit="handleCellEdit"
+ :show-selection="true"
+ />
+ <el-row :gutter="24" style="margin-top: 15px">
+ <el-col :span="12">
+ <el-text type="info">
+ 宸查�夋嫨 {{ formalDatabaseSelectedData.length }} 鏉℃暟鎹�
+ </el-text>
+ </el-col>
+ <el-col :span="12" style="text-align: right">
+ <el-button @click="innerVisible = false">鍙栨秷</el-button>
+ <el-button
+ type="primary"
+ @click="handleSelectData"
+ :disabled="formalDatabaseSelectedData.length === 0"
+ >
+ 纭畾娣诲姞
+ </el-button>
+ </el-col>
+ </el-row>
+ </el-dialog>
+</template>
+
+<script setup>
+import {ref, reactive, watch, onMounted, nextTick, computed} from "vue";
+import ETable from "@/components/Table/ETable.vue";
+import ETableModify from "@/components/Table/EtableModify.vue";
+import ProductionDetailsTable from "./ProductionDetailsTable.vue";
+import {ElMessage, ElMessageBox, ElAlert, ElText} from "element-plus";
+import {Delete, Warning, Plus} from "@element-plus/icons-vue";
+import {validateFormData, validateNumber, deepClone, createDefaultProductionRow} from "@/utils/production";
+import {useCoalData} from "./useCoalData";
+import useUserStore from "@/store/modules/user";
+import {addProductionScheduling} from '@/api/productionScheduling/index'
+
+// Props 鍜� Emits
+const props = defineProps({
+ visible: {type: Boolean, default: false},
+ type: {type: String, default: "add"},
+ rowData: {type: Object, default: () => ({})},
+});
+
+const dialogVisible = defineModel("visible", {type: Boolean, default: false});
+const emit = defineEmits(["update:visible", "success", "update:productionAndProcessing"]);
+
+// 鐢ㄦ埛淇℃伅鍜岀叅绉嶆暟鎹�
+const userStore = useUserStore();
+const {getCoalNameById} = useCoalData();
+let userInfo;
+
+// 瀵硅瘽妗嗙姸鎬�
+const innerVisible = ref(false);
+const dialogType = ref("add");
+const loading = ref(false);
+const etableRef = ref(null);
+
+// 鏁版嵁鐘舵��
+const tableData = ref([]);
+const detailsTableData = ref([]);
+const formalDatabaseData = ref([]);
+const formalDatabaseSelectedData = ref([]);
+const selectedIds = ref([]);
+const currentRow = ref(null);
+const copyForm = ref(null);
+const coalList = ref([])
+const supplierList = ref([]);
+const productionQuantity = ref(0);
+
+// 宸ュ叿鍑芥暟
+const debugIdMatching = () => {
+ if (formalDatabaseData.value.length > 0 && selectedIds.value.length > 0) {
+ const matchedRows = formalDatabaseData.value.filter((row) =>
+ selectedIds.value.includes(row.id)
+ );
+ }
+};
+
+const handleRowClick = (row) => {
+ currentRow.value = row;
+};
+
+// 鎵嬪姩璁剧疆琛ㄦ牸閫変腑鐘舵��
+const setTableSelection = (ids) => {
+ if (!etableRef.value || !Array.isArray(ids) || ids.length === 0) {
+ return;
+ }
+
+ nextTick(() => {
+ setTimeout(() => {
+ try {
+ etableRef.value.clearSelection();
+ const rowsToSelect = formalDatabaseData.value.filter((row) =>
+ ids.includes(row.id)
+ );
+ if (rowsToSelect.length > 0) {
+ etableRef.value.setRowsSelection(rowsToSelect, true);
+ }
+ } catch (error) {
+ }
+ }, 150);
+ });
+};
+
+// 鍒濆鍖栧拰缂栬緫鍒濆鍖�
+const Initialization = async () => {
+ tableData.value = [];
+ detailsTableData.value = [];
+ copyForm.value = null;
+ dialogType.value = "add";
+
+};
+
+const editInitialization = async (type, data) => {
+ productionQuantity.value = data.productionQuantity;
+ copyForm.value = deepClone(data);
+ tableData.value = data.productionInventoryList || [];
+ detailsTableData.value = data.productionList || [];
+ dialogType.value = type;
+ const existingOfficialIds = tableData.value
+ .map((item) => item.officialId)
+ .filter((id) => id);
+ selectedIds.value = existingOfficialIds;
+
+};
+// 鐩戝惉瀵硅瘽妗嗙姸鎬侊紝鍦ㄦ墦寮�鏃惰缃�変腑鐘舵��
+watch(innerVisible, (newVal) => {
+ if (newVal && selectedIds.value.length > 0) {
+ setTimeout(() => setTableSelection(selectedIds.value), 200);
+ }
+ // 瀵硅瘽妗嗗叧闂椂娓呯┖閫夋嫨鐘舵��
+ if (!newVal) {
+ formalDatabaseSelectedData.value = [];
+ }
+});
+
+defineExpose({
+ Initialization,
+ editInitialization,
+});
+const handleSelectData = (row) => {
+ tableData.value = [];
+ if (!innerVisible.value) return;
+ const selectedData = formalDatabaseSelectedData.value;
+ if (selectedData.length === 0) {
+ ElMessage.warning("璇疯嚦灏戦�夋嫨涓�鏉℃暟鎹�");
+ return;
+ }
+ let addedCount = 0;
+ let duplicateCount = 0;
+ selectedData.forEach((item) => {
+ const newItem = {
+ ...item, // 澶嶅埗鎵�鏈夊師濮嬫暟鎹�
+ officialId: item.id, // 淇濆瓨鍘熷鐨刬d浣滀负officialId
+ usedQuantity: 0, // 鍒濆浣跨敤鏁伴噺涓�0
+ // 鍙互鏍规嵁闇�瑕佹坊鍔犲叾浠栧瓧娈�
+ };
+ tableData.value.push(newItem);
+ addedCount++;
+ });
+
+ // 鏇存柊selectedIds锛岀‘淇濆寘鍚墍鏈夊綋鍓峵ableData涓殑officialId
+ const allOfficialIds = tableData.value
+ .map((item) => item.officialId)
+ .filter((id) => id);
+ selectedIds.value = allOfficialIds;
+
+ // 鍏抽棴閫夋嫨瀵硅瘽妗�
+ innerVisible.value = false;
+
+ // 鏄剧ず缁撴灉娑堟伅
+ let message = "";
+ if (addedCount > 0) {
+ message += `鎴愬姛娣诲姞 ${addedCount} 鏉℃暟鎹甡;
+ }
+ if (duplicateCount > 0) {
+ message += (message ? "锛�" : "") + `璺宠繃 ${duplicateCount} 鏉¢噸澶嶆暟鎹甡;
+ }
+ if (message) {
+ ElMessage.success(message);
+ } else {
+ ElMessage.info("娌℃湁鏂版暟鎹娣诲姞");
+ }
+};
+const handleSelectionChange = (selection) => {
+ formalDatabaseSelectedData.value = selection;
+};
+// 鎻愪氦琛ㄥ崟 - 浣跨敤宸ュ叿鍑芥暟楠岃瘉
+const handleSubmit = async () => {
+ // 楠岃瘉鐢熶骇鏄庣粏鏁版嵁
+ const detailsValidation = validateFormData(detailsTableData.value, [
+ "process",
+ "unit",
+ "schedulingNum",
+ "workHours",
+ "schedulingDate",
+ "schedulingUserId"
+ ]);
+
+ if (!detailsValidation.isValid) {
+ ElMessage.warning(detailsValidation.message);
+ return;
+ }
+ let num = 0;
+ detailsTableData.value.forEach((row) => {
+ num += row.schedulingNum
+ })
+ if(productionQuantity.value < num){
+ ElMessage.warning("寰呮帓浜ф暟閲忎笉鑳藉皬浜庣敓浜ф槑缁嗘暟閲�")
+ }
+ console.log(copyForm.value)
+ detailsTableData.value.forEach((row) => {
+ row.type = copyForm.value.type
+ row.productionId = copyForm.value.id
+ row.coalId = copyForm.value.coalId
+ })
+ detailsTableData.value[0].productionQuantity = copyForm.value.productionQuantity
+ try{
+ const res = await addProductionScheduling(detailsTableData.value)
+ if (res.code === 200) {
+ dialogVisible.value = false;
+ emit("success");
+ } else {
+ ElMessage.error("鎻愪氦澶辫触");
+ }
+ }catch (error){
+ ElMessage.error("鎻愪氦澶辫触锛岃閲嶈瘯");
+ }
+};
+// 鍏抽棴寮圭獥
+const handleClose = () => {
+ dialogVisible.value = false;
+};
+
+// 浣跨敤鏁伴噺楠岃瘉 - 浣跨敤宸ュ叿鍑芥暟
+const handleCellEdit = (row, prop, value) => {
+ if (prop === "usedQuantity") {
+ const validation = validateNumber(value, 0, Number(row.inventoryQuantity));
+
+ if (!validation.isValid) {
+ ElMessage.warning(validation.message);
+ row.usedQuantity = validation.value;
+ return;
+ }
+
+ row.usedQuantity = validation.value;
+ }
+};
+
+// 澶勭悊鐢熶骇鏄庣粏琛ㄦ牸鐨勬搷浣� - 浣跨敤宸ュ叿鍑芥暟
+const addNewRow = () => {
+ const newRow = createDefaultProductionRow(userInfo);
+ detailsTableData.value.push(newRow);
+};
+
+// 閲嶇疆鏁版嵁 - 浣跨敤娣辨嫹璐�
+const handleReset = () => {
+ if (copyForm.value) {
+ tableData.value = deepClone(copyForm.value.productionInventoryList) || [];
+ detailsTableData.value = deepClone(copyForm.value.productionList) || [];
+ }
+};
+
+// 鑾峰彇鐢ㄦ埛淇℃伅骞跺姞杞藉熀纭�鏁版嵁
+onMounted(async () => {
+ try {
+ userInfo = await userStore.getInfo();
+ } catch (error) {
+ ElMessage.error("鍒濆鍖栧け璐ワ紝璇烽噸璇�");
+ }
+});
+
+// 绠�鍖栫殑浜嬩欢澶勭悊鍑芥暟
+const handleDetailsChange = (data) => {
+};
+
+const handleDeleteRow = (index) => {
+ ElMessage.success(`宸插垹闄ょ ${index + 1} 琛屾暟鎹甡);
+};
+
+// 鍒犻櫎鍗曚釜宸查�夋暟鎹」
+const handleRemoveItem = (row) => {
+ const index = tableData.value.findIndex(
+ (item) => item.officialId === row.officialId
+ );
+ if (index > -1) {
+ tableData.value.splice(index, 1);
+
+ // 鏇存柊selectedIds
+ const updatedOfficialIds = tableData.value
+ .map((item) => item.officialId)
+ .filter((id) => id);
+ selectedIds.value = updatedOfficialIds;
+ ElMessage.success("宸插垹闄ら�変腑椤�");
+ }
+};
+
+
+// 璁$畻鎬讳娇鐢ㄩ噺
+const totalUsedQuantity = computed(() => {
+ return tableData.value.reduce((total, item) => {
+ const usedQty = Number(item.usedQuantity) || 0;
+ return total + usedQty;
+ }, 0);
+});
+</script>
+
+<style scoped lang="scss">
+.el-form {
+ .el-row {
+ padding-top: 20px;
+ background: rgba($color: #f8fafb, $alpha: 0.5);
+ }
+}
+
+.el-row > .el-col > h1 {
+ font-weight: bolder;
+}
+
+.empty-table > .el-row {
+ margin-bottom: 12px;
+}
+</style>
diff --git a/src/views/production/operationScheduling/components/useCoalData.js b/src/views/production/operationScheduling/components/useCoalData.js
new file mode 100644
index 0000000..9a86b81
--- /dev/null
+++ b/src/views/production/operationScheduling/components/useCoalData.js
@@ -0,0 +1,96 @@
+/**
+ * 鐓ょ鏁版嵁绠$悊缁勫悎寮忓嚱鏁�
+ * 鎻愪緵鐓ょ鏁版嵁鐨勮幏鍙栥�佺紦瀛樸�佽浆鎹㈢瓑鍔熻兘
+ */
+import {ref, computed, watch} from 'vue';
+import {getCoalInfoList} from "@/api/production";
+import {ElMessage} from 'element-plus';
+
+// 鍏ㄥ眬鐓ょ鏁版嵁缂撳瓨
+const coalData = ref([]);
+const isLoading = ref(false);
+const isLoaded = ref(false);
+
+export function useCoalData() {
+
+ // 鑾峰彇鐓ょ鏁版嵁
+ const getCoalData = async (forceRefresh = false) => {
+ if (isLoaded.value && !forceRefresh) {
+ return coalData.value;
+ }
+
+ if (isLoading.value) {
+ // 濡傛灉姝e湪鍔犺浇锛岀瓑寰呭姞杞藉畬鎴�
+ return new Promise((resolve) => {
+ const unwatch = watch(isLoading, (loading) => {
+ if (!loading) {
+ unwatch();
+ resolve(coalData.value);
+ }
+ });
+ });
+ }
+
+ isLoading.value = true;
+ try {
+ const res = await getCoalInfoList();
+ if (res.code === 200) {
+ coalData.value = res.data;
+ isLoaded.value = true;
+ return coalData.value;
+ } else {
+ ElMessage.error('鑾峰彇鐓ょ鏁版嵁澶辫触');
+ return [];
+ }
+ } catch (error) {
+ ElMessage.error('鑾峰彇鐓ょ鏁版嵁澶辫触');
+ console.error('鐓ょ鏁版嵁鑾峰彇閿欒:', error);
+ return [];
+ } finally {
+ isLoading.value = false;
+ }
+ };
+
+ // 鏍规嵁ID鑾峰彇鐓ょ鍚嶇О
+ const getCoalNameById = (id) => {
+ if (!id || coalData.value.length === 0) return id;
+ const coal = coalData.value.find(item => item.id == id);
+ return coal ? coal.coal : id;
+ };
+
+ // 鏍规嵁鍚嶇О鑾峰彇鐓ょID
+ const getCoalIdByName = (name) => {
+ if (!name || coalData.value.length === 0) return '';
+ const coal = coalData.value.find(item => item.coal === name);
+ return coal ? coal.id : '';
+ };
+
+ // 鐢熸垚涓嬫媺閫夐」
+ const coalOptions = computed(() => {
+ return coalData.value.map(item => ({
+ label: item.coal,
+ value: item.coal,
+ key: item.id
+ }));
+ });
+
+ // 鐢熸垚key-value鏄犲皠
+ const coalMap = computed(() => {
+ const map = {};
+ coalData.value.forEach(item => {
+ map[item.id] = item.coal;
+ });
+ return map;
+ });
+
+ return {
+ coalData: computed(() => coalData.value),
+ coalOptions,
+ coalMap,
+ isLoading: computed(() => isLoading.value),
+ isLoaded: computed(() => isLoaded.value),
+ getCoalData,
+ getCoalNameById,
+ getCoalIdByName
+ };
+}
diff --git a/src/views/production/operationScheduling/components/useDialog.js b/src/views/production/operationScheduling/components/useDialog.js
new file mode 100644
index 0000000..3d33158
--- /dev/null
+++ b/src/views/production/operationScheduling/components/useDialog.js
@@ -0,0 +1,62 @@
+/**
+ * 瀵硅瘽妗嗙鐞嗙粍鍚堝紡鍑芥暟
+ * 鎻愪緵瀵硅瘽妗嗙殑鎵撳紑銆佸叧闂�佹暟鎹鐞嗙瓑鍔熻兘
+ */
+import {ref} from 'vue';
+
+export function useDialog() {
+ const dialogVisible = ref(false);
+ const dialogType = ref('add');
+ const dialogRef = ref(null);
+ const currentRowData = ref(null);
+
+ // 鎵撳紑瀵硅瘽妗�
+ const openDialog = (type = 'add', rowData = null) => {
+ dialogType.value = type;
+ currentRowData.value = rowData;
+ dialogVisible.value = true;
+
+ // 璋冪敤瀵硅瘽妗嗙粍浠剁殑鍒濆鍖栨柟娉�
+ if (dialogRef.value) {
+ if (type === 'add') {
+ dialogRef.value.Initialization?.();
+ } else if ((type === 'edit' || type === 'viewRow' || type === 'scheduling') && rowData) {
+ dialogRef.value.editInitialization?.(type,rowData);
+ }
+ }
+ };
+ const viewRow = (type,rowData) => {
+ dialogType.value = type;
+ currentRowData.value = rowData;
+ dialogVisible.value = true;
+ openDialog('viewRow', rowData);
+ };
+ // 鍏抽棴瀵硅瘽妗�
+ const closeDialog = () => {
+ dialogVisible.value = false;
+ dialogType.value = 'add';
+ currentRowData.value = null;
+ };
+
+ // 瀵硅瘽妗嗘垚鍔熷洖璋�
+ const handleDialogSuccess = (callback) => {
+ closeDialog();
+ if (typeof callback === 'function') {
+ callback();
+ }
+ };
+
+ return {
+ // 鐘舵��
+ dialogVisible,
+ dialogType,
+ dialogRef,
+ currentRowData,
+
+ // 鏂规硶
+ openDialog,
+ closeDialog,
+ handleDialogSuccess,
+ viewRow
+ };
+}
diff --git a/src/views/production/operationScheduling/components/useTableData.js b/src/views/production/operationScheduling/components/useTableData.js
new file mode 100644
index 0000000..4f48ef6
--- /dev/null
+++ b/src/views/production/operationScheduling/components/useTableData.js
@@ -0,0 +1,132 @@
+/**
+ * 琛ㄦ牸鏁版嵁绠$悊缁勫悎寮忓嚱鏁�
+ * 鎻愪緵鍒嗛〉銆佹悳绱€�侀�夋嫨绛夐�氱敤鍔熻兘
+ */
+import {ref, reactive} from 'vue';
+import {ElMessage, ElMessageBox} from 'element-plus';
+
+export function useTableData(apiFunction, options = {}) {
+ const {
+ pageSize = 10,
+ searchField = 'searchAll'
+ } = options;
+
+ // 鍝嶅簲寮忔暟鎹�
+ const tableData = ref([]);
+ const loading = ref(false);
+ const total = ref(0);
+ const selectedRows = ref([]);
+
+ // 鏌ヨ鍙傛暟
+ const queryParams = reactive({
+ [searchField]: '',
+ current: 1,
+ size: pageSize,
+ });
+
+ // 鑾峰彇鍒楄〃鏁版嵁
+ const getList = async () => {
+ loading.value = true;
+ try {
+ const params = {
+ [searchField]: queryParams[searchField],
+ current: queryParams.current,
+ size: queryParams.size,
+ };
+ console.log('鏌ヨ鍙傛暟:', params);
+ const res = await apiFunction(params);
+ tableData.value = res.data.records || [];
+ total.value = res.data.total || 0;
+ } catch (error) {
+ ElMessage.error('鑾峰彇鏁版嵁澶辫触');
+ console.error('API閿欒:', error);
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ // 鎼滅储
+ const handleSearch = () => {
+ queryParams.current = 1;
+ getList();
+ };
+
+ // 閲嶇疆鎼滅储
+ const handleReset = () => {
+ queryParams[searchField] = '';
+ console.log('閲嶇疆鎼滅储鍙傛暟:', queryParams);
+ handleSearch();
+ };
+
+ // 鍒嗛〉澶勭悊
+ const handlePageChange = ({page, limit}) => {
+ if (page && page !== queryParams.current) {
+ queryParams.current = page;
+ }
+ if (limit && limit !== queryParams.size) {
+ queryParams.size = limit;
+ queryParams.current = 1; // 鏀瑰彉姣忛〉澶у皬鏃跺洖鍒扮涓�椤�
+ }
+ getList();
+ };
+
+ // 琛ㄦ牸閫夋嫨澶勭悊
+ const handleSelectionChange = (selection) => {
+ selectedRows.value = selection;
+ };
+
+ // 鎵归噺鍒犻櫎
+ const deleteSelected = async (deleteFunction) => {
+ if (selectedRows.value.length === 0) {
+ ElMessage.warning('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁');
+ return;
+ }
+
+ try {
+ await ElMessageBox.confirm(
+ `纭鍒犻櫎閫変腑鐨� ${selectedRows.value.length} 鏉℃暟鎹悧锛焋,
+ '鍒犻櫎纭',
+ {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }
+ );
+
+ const ids = selectedRows.value.map(row => row.id);
+ await deleteFunction(ids);
+
+ ElMessage.success('鍒犻櫎鎴愬姛');
+ selectedRows.value = [];
+ getList();
+ } catch (error) {
+ if (error !== 'cancel') {
+ ElMessage.error('鍒犻櫎澶辫触');
+ console.error('鍒犻櫎閿欒:', error);
+ }
+ }
+ };
+
+ // 鍒锋柊鏁版嵁
+ const refresh = () => {
+ getList();
+ };
+
+ return {
+ // 鏁版嵁
+ tableData,
+ loading,
+ total,
+ selectedRows,
+ queryParams,
+
+ // 鏂规硶
+ getList,
+ handleSearch,
+ handleReset,
+ handlePageChange,
+ handleSelectionChange,
+ deleteSelected,
+ refresh
+ };
+}
diff --git a/src/views/production/operationScheduling/index.vue b/src/views/production/operationScheduling/index.vue
new file mode 100644
index 0000000..ff8b2a6
--- /dev/null
+++ b/src/views/production/operationScheduling/index.vue
@@ -0,0 +1,250 @@
+<template>
+ <div class="app-container">
+ <!-- 鎼滅储琛ㄥ崟 -->
+ <el-form :inline="true" :model="queryParams" class="search-form">
+ <el-form-item label="鎼滅储">
+ <el-input
+ v-model="queryParams.searchAll"
+ placeholder="璇疯緭鍏ュ叧閿瘝"
+ clearable
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">鏌ヨ</el-button>
+ <el-button @click="handleReset">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <!-- 涓昏鍐呭鍖哄煙 -->
+ <el-card>
+ <!-- 鏁版嵁琛ㄦ牸 -->
+ <ETable
+ :showOverflowTooltip="false"
+ :loading="loading"
+ :table-data="tableData"
+ :columns="columns"
+ :current-page="queryParams.current"
+ :page-size="queryParams.size"
+ @selection-change="handleSelectionChange"
+ @edit="(row) => openDialog('scheduling', row)"
+ :show-selection="true"
+ :border="true"
+ :operations="['scheduling']"
+ :operationsWidth="200"
+ :show-overflow-tooltip="false"
+ style="width: 100%; height: calc(100vh - 26em)"
+ >
+ <template #coalId="{ row }">
+ <div class="coal-tags">
+ <template v-if="row.coalId">
+ <el-tag
+ v-for="coal in parseCoalArray(row.coalId)"
+ :key="coal"
+ size="small"
+ type="primary"
+ class="coal-tag"
+ >
+ {{ getDisplayCoalName(coal) }}
+ </el-tag>
+ </template>
+ <span v-else class="no-data">--</span>
+ </div>
+ </template>
+ </ETable>
+ <!-- 鍒嗛〉缁勪欢 -->
+ <Pagination
+ :layout="'total, prev, pager, next, jumper'"
+ :total="total"
+ v-model:page="queryParams.current"
+ :limit="queryParams.size"
+ @pagination="handlePageChange"
+ />
+ </el-card>
+
+ <!-- 鐢熶骇瀵硅瘽妗� -->
+ <!-- handleProductionAndProcessing -->
+ <ProductionDialog
+ v-model:visible="dialogVisible"
+ ref="dialogRef"
+ :type="dialogType"
+ @update:productionAndProcessing="handleProductionAndProcessing"
+ @success="handleDialogSuccess"
+ />
+ </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import { ElMessage } from "element-plus";
+import { Plus, Delete } from "@element-plus/icons-vue";
+import ProductionDialog from "./components/ProductionDialog.vue";
+import ETable from "@/components/Table/ETable.vue";
+import Pagination from "@/components/Pagination/index.vue";
+import { delPM,getProductionList } from "@/api/production";
+import { parseCoalArray } from "@/utils/production";
+import { useTableData } from "./components/useTableData.js";
+import { useDialog } from "./components/useDialog.js";
+import { useCoalData } from "./components/useCoalData.js";
+import { getCoalInfoList } from "@/api/production";
+
+// 鐓ょ淇℃伅鍒楄〃
+const coalInfoList = ref([]);
+
+// 琛ㄦ牸鍒楅厤缃�
+const columns = [
+ { prop: "coalId", label: "鐓ょ", minWidth: 150, slot: true },
+ {
+ prop: "status",
+ label: "鐘舵��",
+ minWidth: 150,
+ formatter: (row) => {
+ const statusMap = {
+ 1: '寰呮帓浜�',
+ 2: '鎺掍骇涓�',
+ 3: '宸叉帓浜�'
+ };
+ return statusMap[row.status] || '鏈煡鐘舵��';
+ }
+ },
+ { prop: "producer", label: "鐢熶骇浜�", minWidth: 150 },
+ { prop: "productionQuantity", label: "鐢熶骇鏁伴噺", minWidth: 120 },
+ { prop: "laborCost", label: "浜哄伐鎴愭湰", minWidth: 150 },
+ { prop: "energyConsumptionCost", label: "鑳借�楁垚鏈�", minWidth: 120 },
+ { prop: "equipmentDepreciation", label: "璁惧鎶樻棫", minWidth: 143 },
+ { prop: "totalCost", label: "鎬绘垚鏈�", minWidth: 150 },
+];
+
+// 浣跨敤琛ㄦ牸鏁版嵁缁勫悎寮忓嚱鏁�
+const {
+ tableData,
+ loading,
+ total,
+ selectedRows,
+ queryParams,
+ getList,
+ handleSearch,
+ handleReset,
+ handlePageChange,
+ handleSelectionChange,
+ deleteSelected,
+} = useTableData(getProductionList, { pageSize: 10 });
+
+// 浣跨敤瀵硅瘽妗嗙粍鍚堝紡鍑芥暟
+const {
+ dialogVisible,
+ dialogType,
+ dialogRef,
+ openDialog,
+ handleDialogSuccess: onDialogSuccess,
+} = useDialog();
+
+// 浣跨敤鐓ょ鏁版嵁缁勫悎寮忓嚱鏁�
+const { getCoalNameById, getCoalData } = useCoalData();
+
+// 鑾峰彇鐓ょ鏄剧ず鍚嶇О锛堝甫澶囩敤閫昏緫锛�
+const getDisplayCoalName = (coalId) => {
+ // 浼樺厛浣跨敤 useCoalData 鐨勬柟娉�
+ let name = getCoalNameById(coalId);
+
+ // 濡傛灉娌℃湁鎵惧埌锛屽皾璇曚粠 coalInfoList 涓煡鎵�
+ if (name === coalId && coalInfoList.value.length > 0) {
+ const found = coalInfoList.value.find((item) => item.id == coalId);
+ name = found ? found.coal : coalId;
+ }
+
+ return name || coalId;
+};
+
+// 澶勭悊鐢熶骇鏁版嵁鏇存柊
+const handleProductionAndProcessing = (row, rows) => {
+ const index = tableData.value.findIndex((item) => item.id === rows.id);
+ if (index !== -1) {
+ tableData.value[index] = { ...tableData.value[index], ...row };
+ }
+};
+
+// 瀵硅瘽妗嗘垚鍔熷洖璋�
+const handleDialogSuccess = () => {
+ onDialogSuccess(() => {
+ getList();
+ ElMessage.success("鎿嶄綔鎴愬姛");
+ });
+};
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
+onMounted(async () => {
+ try {
+ // 骞惰鍔犺浇鐓ょ鏁版嵁鍜岃〃鏍兼暟鎹�
+ await Promise.all([
+ getCoalData(), // 棰勫姞杞界叅绉嶆暟鎹�
+ (async () => {
+ const res = await getCoalInfoList();
+ if (res.code === 200) {
+ coalInfoList.value = res.data;
+ }
+ })(),
+ ]);
+
+ // 鍔犺浇琛ㄦ牸鏁版嵁
+ getList();
+ } catch (error) {
+ ElMessage.error("鏁版嵁鍔犺浇澶辫触锛岃鍒锋柊椤甸潰閲嶈瘯");
+ }
+});
+</script>
+
+<style scoped lang="scss">
+.production-container {
+ padding: 20px;
+
+ .el-card:nth-child(1) {
+ margin-bottom: 20px;
+ }
+}
+
+.search-bar {
+ margin-bottom: 20px;
+ display: flex;
+ gap: 10px;
+
+ .el-input {
+ width: 20%;
+ }
+}
+
+.search-form {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ margin-bottom: 20px;
+
+ .el-form-item {
+ margin-right: 10px;
+ }
+
+ .el-button {
+ margin-left: 10px;
+ }
+}
+
+.coal-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ align-items: center;
+
+ .coal-tag {
+ margin-right: 4px;
+ margin-bottom: 4px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+
+ .no-data {
+ color: #999;
+ font-style: italic;
+ }
+}
+</style>
diff --git a/src/views/production/productionReporting/components/ProductionDialog.vue b/src/views/production/productionReporting/components/ProductionDialog.vue
new file mode 100644
index 0000000..4d6bf8c
--- /dev/null
+++ b/src/views/production/productionReporting/components/ProductionDialog.vue
@@ -0,0 +1,348 @@
+<template>
+ <el-dialog
+ v-model="dialogVisible"
+ title="鐢熶骇鎶ュ伐"
+ width="70%"
+ @close="handleClose"
+ >
+ <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鎺掍骇鏁伴噺锛�">
+ <el-input v-model="productionQuantity" placeholder="璇疯緭鍏�" clearable disabled/>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏈鐢熶骇鏁伴噺锛�" prop="finishedNum">
+ <el-input-number
+ v-model="form.finishedNum"
+ placeholder="璇疯緭鍏�"
+ :min="0"
+ :step="0.1"
+ :precision="2"
+ clearable
+ style="width: 100%"
+ @change="changeNum"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="寰呯敓浜ф暟閲忥細">
+ <el-input v-model="pendingNum" placeholder="璇疯緭鍏�" clearable disabled/>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鐢熶骇浜猴細" prop="schedulingUserId">
+ <el-select
+ v-model="form.schedulingUserId"
+ placeholder="閫夋嫨浜哄憳"
+ style="width: 100%;"
+ >
+ <el-option
+ v-for="user in userList"
+ :key="user.userId"
+ :label="user.nickName"
+ :value="user.userId"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleSubmit">纭</el-button>
+ <el-button @click="handleClose">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+</template>
+
+<script setup>
+import {ref, reactive, watch, onMounted, nextTick, computed} from "vue";
+import ETable from "@/components/Table/ETable.vue";
+import ETableModify from "@/components/Table/EtableModify.vue";
+import {ElMessage, ElMessageBox, ElAlert, ElText} from "element-plus";
+import {Delete, Warning, Plus} from "@element-plus/icons-vue";
+import {validateFormData, validateNumber, deepClone, createDefaultProductionRow} from "@/utils/production";
+import {useCoalData} from "./useCoalData";
+import useUserStore from "@/store/modules/user";
+import {work} from '@/api/productionScheduling/index'
+import {userListAll} from "@/api/publicApi";
+const userList = ref([])
+
+const data = reactive({
+ form: {
+ successNum: 0,
+ schedulingNum: 0,
+ finishedNum: 0,
+ schedulingUserId: ""
+ },
+ rules: {
+ schedulingNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" },],
+ },
+});
+const { form, rules } = toRefs(data);
+const changeNum = (value) => {
+ if (value > pendingNum.value) {
+ form.value.finishedNum = pendingNum.value
+ ElMessage.warning("鏈鐢熶骇鏁伴噺涓嶅彲澶т簬鎺掍骇鏁伴噺");
+ }
+ pendingNum.value = pendingNum.value - form.value.finishedNum;
+}
+// Props 鍜� Emits
+const props = defineProps({
+ visible: {type: Boolean, default: false},
+ type: {type: String, default: "add"},
+ rowData: {type: Object, default: () => ({})},
+});
+
+const dialogVisible = defineModel("visible", {type: Boolean, default: false});
+const emit = defineEmits(["update:visible", "success", "update:productionAndProcessing"]);
+
+// 鐢ㄦ埛淇℃伅鍜岀叅绉嶆暟鎹�
+const userStore = useUserStore();
+const {getCoalNameById} = useCoalData();
+let userInfo;
+
+// 瀵硅瘽妗嗙姸鎬�
+const innerVisible = ref(false);
+const dialogType = ref("add");
+const loading = ref(false);
+const etableRef = ref(null);
+
+// 鏁版嵁鐘舵��
+const tableData = ref([]);
+const detailsTableData = ref([]);
+const formalDatabaseData = ref([]);
+const formalDatabaseSelectedData = ref([]);
+const selectedIds = ref([]);
+const currentRow = ref(null);
+const copyForm = ref(null);
+const productionQuantity = ref(0);
+const pendingNum = ref(0);
+
+
+const handleRowClick = (row) => {
+ currentRow.value = row;
+};
+
+// 鎵嬪姩璁剧疆琛ㄦ牸閫変腑鐘舵��
+const setTableSelection = (ids) => {
+ if (!etableRef.value || !Array.isArray(ids) || ids.length === 0) {
+ return;
+ }
+
+ nextTick(() => {
+ setTimeout(() => {
+ try {
+ etableRef.value.clearSelection();
+ const rowsToSelect = formalDatabaseData.value.filter((row) =>
+ ids.includes(row.id)
+ );
+ if (rowsToSelect.length > 0) {
+ etableRef.value.setRowsSelection(rowsToSelect, true);
+ }
+ } catch (error) {
+ }
+ }, 150);
+ });
+};
+
+// 鍒濆鍖栧拰缂栬緫鍒濆鍖�
+const Initialization = async () => {
+ tableData.value = [];
+ form.value = {
+ successNum: 0,
+ schedulingNum: 0,
+ finishedNum: 0,
+ schedulingUserId: ""
+ };
+ detailsTableData.value = [];
+ copyForm.value = null;
+ dialogType.value = "add";
+};
+
+const editInitialization = async (type, data) => {
+ //娓呯┖form
+ Initialization();
+ productionQuantity.value = data.schedulingNum;
+ pendingNum.value = data.schedulingNum - data.successNum;
+ copyForm.value = deepClone(data);
+ tableData.value = data.productionInventoryList || [];
+ detailsTableData.value = data.productionList || [];
+ dialogType.value = type;
+ const existingOfficialIds = tableData.value
+ .map((item) => item.officialId)
+ .filter((id) => id);
+ selectedIds.value = existingOfficialIds;
+
+};
+// 鐩戝惉瀵硅瘽妗嗙姸鎬侊紝鍦ㄦ墦寮�鏃惰缃�変腑鐘舵��
+watch(innerVisible, (newVal) => {
+ if (newVal && selectedIds.value.length > 0) {
+ setTimeout(() => setTableSelection(selectedIds.value), 200);
+ }
+ // 瀵硅瘽妗嗗叧闂椂娓呯┖閫夋嫨鐘舵��
+ if (!newVal) {
+ formalDatabaseSelectedData.value = [];
+ }
+});
+
+defineExpose({
+ Initialization,
+ editInitialization,
+});
+const handleSelectData = (row) => {
+ tableData.value = [];
+ if (!innerVisible.value) return;
+ const selectedData = formalDatabaseSelectedData.value;
+ if (selectedData.length === 0) {
+ ElMessage.warning("璇疯嚦灏戦�夋嫨涓�鏉℃暟鎹�");
+ return;
+ }
+ let addedCount = 0;
+ let duplicateCount = 0;
+ selectedData.forEach((item) => {
+ const newItem = {
+ ...item, // 澶嶅埗鎵�鏈夊師濮嬫暟鎹�
+ officialId: item.id, // 淇濆瓨鍘熷鐨刬d浣滀负officialId
+ usedQuantity: 0, // 鍒濆浣跨敤鏁伴噺涓�0
+ // 鍙互鏍规嵁闇�瑕佹坊鍔犲叾浠栧瓧娈�
+ };
+ tableData.value.push(newItem);
+ addedCount++;
+ });
+
+ // 鏇存柊selectedIds锛岀‘淇濆寘鍚墍鏈夊綋鍓峵ableData涓殑officialId
+ const allOfficialIds = tableData.value
+ .map((item) => item.officialId)
+ .filter((id) => id);
+ selectedIds.value = allOfficialIds;
+
+ // 鍏抽棴閫夋嫨瀵硅瘽妗�
+ innerVisible.value = false;
+
+ // 鏄剧ず缁撴灉娑堟伅
+ let message = "";
+ if (addedCount > 0) {
+ message += `鎴愬姛娣诲姞 ${addedCount} 鏉℃暟鎹甡;
+ }
+ if (duplicateCount > 0) {
+ message += (message ? "锛�" : "") + `璺宠繃 ${duplicateCount} 鏉¢噸澶嶆暟鎹甡;
+ }
+ if (message) {
+ ElMessage.success(message);
+ } else {
+ ElMessage.info("娌℃湁鏂版暟鎹娣诲姞");
+ }
+};
+const handleSelectionChange = (selection) => {
+ formalDatabaseSelectedData.value = selection;
+};
+// 鎻愪氦琛ㄥ崟 - 浣跨敤宸ュ叿鍑芥暟楠岃瘉
+const handleSubmit = async () => {
+ console.log(copyForm.value)
+ try{
+ const res = await work({id: copyForm.value.id,successNum: form.value.finishedNum,schedulingUserId: form.value.schedulingUserId})
+ if (res.code === 200) {
+ dialogVisible.value = false;
+ emit("success");
+ } else {
+ ElMessage.error("鎻愪氦澶辫触");
+ }
+ }catch (error){
+ ElMessage.error("鎻愪氦澶辫触锛岃閲嶈瘯");
+ }
+};
+// 鍏抽棴寮圭獥
+const handleClose = () => {
+ dialogVisible.value = false;
+};
+
+// 浣跨敤鏁伴噺楠岃瘉 - 浣跨敤宸ュ叿鍑芥暟
+const handleCellEdit = (row, prop, value) => {
+ if (prop === "usedQuantity") {
+ const validation = validateNumber(value, 0, Number(row.inventoryQuantity));
+
+ if (!validation.isValid) {
+ ElMessage.warning(validation.message);
+ row.usedQuantity = validation.value;
+ return;
+ }
+
+ row.usedQuantity = validation.value;
+ }
+};
+
+// 澶勭悊鐢熶骇鏄庣粏琛ㄦ牸鐨勬搷浣� - 浣跨敤宸ュ叿鍑芥暟
+const addNewRow = () => {
+ const newRow = createDefaultProductionRow(userInfo);
+ detailsTableData.value.push(newRow);
+};
+
+
+// 鑾峰彇鐢ㄦ埛淇℃伅骞跺姞杞藉熀纭�鏁版嵁
+onMounted(async () => {
+ try {
+ let ress = await userListAll();
+ userList.value = ress.data;
+ userInfo = await userStore.getInfo();
+ } catch (error) {
+ ElMessage.error("鍒濆鍖栧け璐ワ紝璇烽噸璇�");
+ }
+});
+
+// 绠�鍖栫殑浜嬩欢澶勭悊鍑芥暟
+const handleDetailsChange = (data) => {
+};
+
+const handleDeleteRow = (index) => {
+ ElMessage.success(`宸插垹闄ょ ${index + 1} 琛屾暟鎹甡);
+};
+
+// 鍒犻櫎鍗曚釜宸查�夋暟鎹」
+const handleRemoveItem = (row) => {
+ const index = tableData.value.findIndex(
+ (item) => item.officialId === row.officialId
+ );
+ if (index > -1) {
+ tableData.value.splice(index, 1);
+
+ // 鏇存柊selectedIds
+ const updatedOfficialIds = tableData.value
+ .map((item) => item.officialId)
+ .filter((id) => id);
+ selectedIds.value = updatedOfficialIds;
+ ElMessage.success("宸插垹闄ら�変腑椤�");
+ }
+};
+
+
+// 璁$畻鎬讳娇鐢ㄩ噺
+const totalUsedQuantity = computed(() => {
+ return tableData.value.reduce((total, item) => {
+ const usedQty = Number(item.usedQuantity) || 0;
+ return total + usedQty;
+ }, 0);
+});
+</script>
+
+<style scoped lang="scss">
+.el-form {
+ .el-row {
+ padding-top: 20px;
+ background: rgba($color: #f8fafb, $alpha: 0.5);
+ }
+}
+
+.el-row > .el-col > h1 {
+ font-weight: bolder;
+}
+
+.empty-table > .el-row {
+ margin-bottom: 12px;
+}
+</style>
diff --git a/src/views/production/productionReporting/components/useCoalData.js b/src/views/production/productionReporting/components/useCoalData.js
new file mode 100644
index 0000000..9a86b81
--- /dev/null
+++ b/src/views/production/productionReporting/components/useCoalData.js
@@ -0,0 +1,96 @@
+/**
+ * 鐓ょ鏁版嵁绠$悊缁勫悎寮忓嚱鏁�
+ * 鎻愪緵鐓ょ鏁版嵁鐨勮幏鍙栥�佺紦瀛樸�佽浆鎹㈢瓑鍔熻兘
+ */
+import {ref, computed, watch} from 'vue';
+import {getCoalInfoList} from "@/api/production";
+import {ElMessage} from 'element-plus';
+
+// 鍏ㄥ眬鐓ょ鏁版嵁缂撳瓨
+const coalData = ref([]);
+const isLoading = ref(false);
+const isLoaded = ref(false);
+
+export function useCoalData() {
+
+ // 鑾峰彇鐓ょ鏁版嵁
+ const getCoalData = async (forceRefresh = false) => {
+ if (isLoaded.value && !forceRefresh) {
+ return coalData.value;
+ }
+
+ if (isLoading.value) {
+ // 濡傛灉姝e湪鍔犺浇锛岀瓑寰呭姞杞藉畬鎴�
+ return new Promise((resolve) => {
+ const unwatch = watch(isLoading, (loading) => {
+ if (!loading) {
+ unwatch();
+ resolve(coalData.value);
+ }
+ });
+ });
+ }
+
+ isLoading.value = true;
+ try {
+ const res = await getCoalInfoList();
+ if (res.code === 200) {
+ coalData.value = res.data;
+ isLoaded.value = true;
+ return coalData.value;
+ } else {
+ ElMessage.error('鑾峰彇鐓ょ鏁版嵁澶辫触');
+ return [];
+ }
+ } catch (error) {
+ ElMessage.error('鑾峰彇鐓ょ鏁版嵁澶辫触');
+ console.error('鐓ょ鏁版嵁鑾峰彇閿欒:', error);
+ return [];
+ } finally {
+ isLoading.value = false;
+ }
+ };
+
+ // 鏍规嵁ID鑾峰彇鐓ょ鍚嶇О
+ const getCoalNameById = (id) => {
+ if (!id || coalData.value.length === 0) return id;
+ const coal = coalData.value.find(item => item.id == id);
+ return coal ? coal.coal : id;
+ };
+
+ // 鏍规嵁鍚嶇О鑾峰彇鐓ょID
+ const getCoalIdByName = (name) => {
+ if (!name || coalData.value.length === 0) return '';
+ const coal = coalData.value.find(item => item.coal === name);
+ return coal ? coal.id : '';
+ };
+
+ // 鐢熸垚涓嬫媺閫夐」
+ const coalOptions = computed(() => {
+ return coalData.value.map(item => ({
+ label: item.coal,
+ value: item.coal,
+ key: item.id
+ }));
+ });
+
+ // 鐢熸垚key-value鏄犲皠
+ const coalMap = computed(() => {
+ const map = {};
+ coalData.value.forEach(item => {
+ map[item.id] = item.coal;
+ });
+ return map;
+ });
+
+ return {
+ coalData: computed(() => coalData.value),
+ coalOptions,
+ coalMap,
+ isLoading: computed(() => isLoading.value),
+ isLoaded: computed(() => isLoaded.value),
+ getCoalData,
+ getCoalNameById,
+ getCoalIdByName
+ };
+}
diff --git a/src/views/production/productionReporting/components/useDialog.js b/src/views/production/productionReporting/components/useDialog.js
new file mode 100644
index 0000000..3351cdc
--- /dev/null
+++ b/src/views/production/productionReporting/components/useDialog.js
@@ -0,0 +1,62 @@
+/**
+ * 瀵硅瘽妗嗙鐞嗙粍鍚堝紡鍑芥暟
+ * 鎻愪緵瀵硅瘽妗嗙殑鎵撳紑銆佸叧闂�佹暟鎹鐞嗙瓑鍔熻兘
+ */
+import {ref} from 'vue';
+
+export function useDialog() {
+ const dialogVisible = ref(false);
+ const dialogType = ref('add');
+ const dialogRef = ref(null);
+ const currentRowData = ref(null);
+
+ // 鎵撳紑瀵硅瘽妗�
+ const openDialog = (type = 'add', rowData = null) => {
+ dialogType.value = type;
+ currentRowData.value = rowData;
+ dialogVisible.value = true;
+
+ // 璋冪敤瀵硅瘽妗嗙粍浠剁殑鍒濆鍖栨柟娉�
+ if (dialogRef.value) {
+ if (type === 'add') {
+ dialogRef.value.Initialization?.();
+ } else if ((type === 'edit' || type === 'viewRow' || type === 'scheduling' || type === 'work') && rowData) {
+ dialogRef.value.editInitialization?.(type,rowData);
+ }
+ }
+ };
+ const viewRow = (type,rowData) => {
+ dialogType.value = type;
+ currentRowData.value = rowData;
+ dialogVisible.value = true;
+ openDialog('viewRow', rowData);
+ };
+ // 鍏抽棴瀵硅瘽妗�
+ const closeDialog = () => {
+ dialogVisible.value = false;
+ dialogType.value = 'add';
+ currentRowData.value = null;
+ };
+
+ // 瀵硅瘽妗嗘垚鍔熷洖璋�
+ const handleDialogSuccess = (callback) => {
+ closeDialog();
+ if (typeof callback === 'function') {
+ callback();
+ }
+ };
+
+ return {
+ // 鐘舵��
+ dialogVisible,
+ dialogType,
+ dialogRef,
+ currentRowData,
+
+ // 鏂规硶
+ openDialog,
+ closeDialog,
+ handleDialogSuccess,
+ viewRow
+ };
+}
diff --git a/src/views/production/productionReporting/components/useTableData.js b/src/views/production/productionReporting/components/useTableData.js
new file mode 100644
index 0000000..4f48ef6
--- /dev/null
+++ b/src/views/production/productionReporting/components/useTableData.js
@@ -0,0 +1,132 @@
+/**
+ * 琛ㄦ牸鏁版嵁绠$悊缁勫悎寮忓嚱鏁�
+ * 鎻愪緵鍒嗛〉銆佹悳绱€�侀�夋嫨绛夐�氱敤鍔熻兘
+ */
+import {ref, reactive} from 'vue';
+import {ElMessage, ElMessageBox} from 'element-plus';
+
+export function useTableData(apiFunction, options = {}) {
+ const {
+ pageSize = 10,
+ searchField = 'searchAll'
+ } = options;
+
+ // 鍝嶅簲寮忔暟鎹�
+ const tableData = ref([]);
+ const loading = ref(false);
+ const total = ref(0);
+ const selectedRows = ref([]);
+
+ // 鏌ヨ鍙傛暟
+ const queryParams = reactive({
+ [searchField]: '',
+ current: 1,
+ size: pageSize,
+ });
+
+ // 鑾峰彇鍒楄〃鏁版嵁
+ const getList = async () => {
+ loading.value = true;
+ try {
+ const params = {
+ [searchField]: queryParams[searchField],
+ current: queryParams.current,
+ size: queryParams.size,
+ };
+ console.log('鏌ヨ鍙傛暟:', params);
+ const res = await apiFunction(params);
+ tableData.value = res.data.records || [];
+ total.value = res.data.total || 0;
+ } catch (error) {
+ ElMessage.error('鑾峰彇鏁版嵁澶辫触');
+ console.error('API閿欒:', error);
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ // 鎼滅储
+ const handleSearch = () => {
+ queryParams.current = 1;
+ getList();
+ };
+
+ // 閲嶇疆鎼滅储
+ const handleReset = () => {
+ queryParams[searchField] = '';
+ console.log('閲嶇疆鎼滅储鍙傛暟:', queryParams);
+ handleSearch();
+ };
+
+ // 鍒嗛〉澶勭悊
+ const handlePageChange = ({page, limit}) => {
+ if (page && page !== queryParams.current) {
+ queryParams.current = page;
+ }
+ if (limit && limit !== queryParams.size) {
+ queryParams.size = limit;
+ queryParams.current = 1; // 鏀瑰彉姣忛〉澶у皬鏃跺洖鍒扮涓�椤�
+ }
+ getList();
+ };
+
+ // 琛ㄦ牸閫夋嫨澶勭悊
+ const handleSelectionChange = (selection) => {
+ selectedRows.value = selection;
+ };
+
+ // 鎵归噺鍒犻櫎
+ const deleteSelected = async (deleteFunction) => {
+ if (selectedRows.value.length === 0) {
+ ElMessage.warning('璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁');
+ return;
+ }
+
+ try {
+ await ElMessageBox.confirm(
+ `纭鍒犻櫎閫変腑鐨� ${selectedRows.value.length} 鏉℃暟鎹悧锛焋,
+ '鍒犻櫎纭',
+ {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }
+ );
+
+ const ids = selectedRows.value.map(row => row.id);
+ await deleteFunction(ids);
+
+ ElMessage.success('鍒犻櫎鎴愬姛');
+ selectedRows.value = [];
+ getList();
+ } catch (error) {
+ if (error !== 'cancel') {
+ ElMessage.error('鍒犻櫎澶辫触');
+ console.error('鍒犻櫎閿欒:', error);
+ }
+ }
+ };
+
+ // 鍒锋柊鏁版嵁
+ const refresh = () => {
+ getList();
+ };
+
+ return {
+ // 鏁版嵁
+ tableData,
+ loading,
+ total,
+ selectedRows,
+ queryParams,
+
+ // 鏂规硶
+ getList,
+ handleSearch,
+ handleReset,
+ handlePageChange,
+ handleSelectionChange,
+ deleteSelected,
+ refresh
+ };
+}
diff --git a/src/views/production/productionReporting/index.vue b/src/views/production/productionReporting/index.vue
new file mode 100644
index 0000000..7a1d60e
--- /dev/null
+++ b/src/views/production/productionReporting/index.vue
@@ -0,0 +1,277 @@
+<template>
+ <div class="app-container">
+ <!-- 鎼滅储琛ㄥ崟 -->
+ <el-form :inline="true" :model="queryParams" class="search-form">
+ <el-form-item label="鎼滅储">
+ <el-input
+ v-model="queryParams.searchAll"
+ placeholder="璇疯緭鍏ュ叧閿瘝"
+ clearable
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">鏌ヨ</el-button>
+ <el-button @click="handleReset">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <!-- 涓昏鍐呭鍖哄煙 -->
+ <el-card>
+ <!-- 鎿嶄綔鎸夐挳 -->
+ <div class="toolbar">
+ <el-button
+ type="danger"
+ :icon="Delete"
+ :disabled="!selectedRows.length"
+ @click="() => deleteSelected(delProductionScheduling)"
+ >
+ 鍒犻櫎
+ </el-button>
+ </div>
+ <!-- 鏁版嵁琛ㄦ牸 -->
+ <ETable
+ :showOverflowTooltip="false"
+ :loading="loading"
+ :table-data="tableData"
+ :columns="columns"
+ :current-page="queryParams.current"
+ :page-size="queryParams.size"
+ @selection-change="handleSelectionChange"
+ @edit="(row) => openDialog('work', row)"
+ :show-selection="true"
+ :border="true"
+ :operations="['work']"
+ :operationsWidth="200"
+ :show-overflow-tooltip="false"
+ style="width: 100%; height: calc(100vh - 26em)"
+ >
+ <template #coalId="{ row }">
+ <div class="coal-tags">
+ <template v-if="row.coalId">
+ <el-tag
+ v-for="coal in parseCoalArray(row.coalId)"
+ :key="coal"
+ size="small"
+ type="primary"
+ class="coal-tag"
+ >
+ {{ getDisplayCoalName(coal) }}
+ </el-tag>
+ </template>
+ <span v-else class="no-data">--</span>
+ </div>
+ </template>
+ </ETable>
+ <!-- 鍒嗛〉缁勪欢 -->
+ <Pagination
+ :layout="'total, prev, pager, next, jumper'"
+ :total="total"
+ v-model:page="queryParams.current"
+ :limit="queryParams.size"
+ @pagination="handlePageChange"
+ />
+ </el-card>
+
+ <!-- 鐢熶骇瀵硅瘽妗� -->
+ <!-- handleProductionAndProcessing -->
+ <ProductionDialog
+ v-model:visible="dialogVisible"
+ ref="dialogRef"
+ :type="dialogType"
+ @update:productionAndProcessing="handleProductionAndProcessing"
+ @success="handleDialogSuccess"
+ />
+ </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import { ElMessage } from "element-plus";
+import { Plus, Delete } from "@element-plus/icons-vue";
+import ProductionDialog from "./components/ProductionDialog.vue";
+import ETable from "@/components/Table/ETable.vue";
+import Pagination from "@/components/Pagination/index.vue";
+import { listPage,delProductionScheduling } from "@/api/productionScheduling";
+import { parseCoalArray } from "@/utils/production";
+import { useTableData } from "./components/useTableData.js";
+import { useDialog } from "./components/useDialog.js";
+import { useCoalData } from "./components/useCoalData.js";
+import { getCoalInfoList } from "@/api/production";
+
+// 鐓ょ淇℃伅鍒楄〃
+const coalInfoList = ref([]);
+
+// 琛ㄦ牸鍒楅厤缃�
+const columns = [
+ { prop: "coalId", label: "鐓ょ", minWidth: 150, slot: true },
+ {
+ prop: "type",
+ label: "鐓ゆ枡绫诲瀷",
+ minWidth: 150,
+ formatter: (row) => {
+ const statusMap = {
+ 1: '鎴愬搧',
+ 2: '鍘熸枡'
+ };
+ return statusMap[row.type] || '鏈煡绫诲瀷';
+ }
+ },
+ {
+ prop: "status",
+ label: "鐘舵��",
+ minWidth: 150,
+ formatter: (row) => {
+ const statusMap = {
+ 1: '寰呯敓浜�',
+ 2: '鐢熶骇涓�',
+ 3: '宸插叆搴�'
+ };
+ return statusMap[row.status] || '鏈煡鐘舵��';
+ }
+ },
+ { prop: "schedulingUserName", label: "鐢熶骇浜�", minWidth: 150 },
+ { prop: "schedulingNum", label: "鐢熶骇鏁伴噺", minWidth: 120 },
+ { prop: "successNum", label: "鍏ュ簱鏁伴噺", minWidth: 120,
+ formatter: (row) => {
+ return row.successNum || '0';
+ } },
+ { prop: "workHours", label: "宸ユ椂瀹氶", minWidth: 150 },
+ { prop: "unit", label: "鍗曚綅", minWidth: 120 },
+ { prop: "process", label: "宸ュ簭", minWidth: 143 },
+ { prop: "schedulingDate", label: "鎺掍骇鏃ユ湡", minWidth: 150 },
+];
+
+// 浣跨敤琛ㄦ牸鏁版嵁缁勫悎寮忓嚱鏁�
+const {
+ tableData,
+ loading,
+ total,
+ selectedRows,
+ queryParams,
+ getList,
+ handleSearch,
+ handleReset,
+ handlePageChange,
+ handleSelectionChange,
+ deleteSelected,
+} = useTableData(listPage, { pageSize: 10 });
+
+// 浣跨敤瀵硅瘽妗嗙粍鍚堝紡鍑芥暟
+const {
+ dialogVisible,
+ dialogType,
+ dialogRef,
+ openDialog,
+ handleDialogSuccess: onDialogSuccess,
+} = useDialog();
+
+// 浣跨敤鐓ょ鏁版嵁缁勫悎寮忓嚱鏁�
+const { getCoalNameById, getCoalData } = useCoalData();
+
+// 鑾峰彇鐓ょ鏄剧ず鍚嶇О锛堝甫澶囩敤閫昏緫锛�
+const getDisplayCoalName = (coalId) => {
+ // 浼樺厛浣跨敤 useCoalData 鐨勬柟娉�
+ let name = getCoalNameById(coalId);
+
+ // 濡傛灉娌℃湁鎵惧埌锛屽皾璇曚粠 coalInfoList 涓煡鎵�
+ if (name === coalId && coalInfoList.value.length > 0) {
+ const found = coalInfoList.value.find((item) => item.id == coalId);
+ name = found ? found.coal : coalId;
+ }
+
+ return name || coalId;
+};
+
+// 澶勭悊鐢熶骇鏁版嵁鏇存柊
+const handleProductionAndProcessing = (row, rows) => {
+ const index = tableData.value.findIndex((item) => item.id === rows.id);
+ if (index !== -1) {
+ tableData.value[index] = { ...tableData.value[index], ...row };
+ }
+};
+
+// 瀵硅瘽妗嗘垚鍔熷洖璋�
+const handleDialogSuccess = () => {
+ onDialogSuccess(() => {
+ getList();
+ ElMessage.success("鎿嶄綔鎴愬姛");
+ });
+};
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
+onMounted(async () => {
+ try {
+ // 骞惰鍔犺浇鐓ょ鏁版嵁鍜岃〃鏍兼暟鎹�
+ await Promise.all([
+ getCoalData(), // 棰勫姞杞界叅绉嶆暟鎹�
+ (async () => {
+ const res = await getCoalInfoList();
+ if (res.code === 200) {
+ coalInfoList.value = res.data;
+ }
+ })(),
+ ]);
+
+ // 鍔犺浇琛ㄦ牸鏁版嵁
+ getList();
+ } catch (error) {
+ ElMessage.error("鏁版嵁鍔犺浇澶辫触锛岃鍒锋柊椤甸潰閲嶈瘯");
+ }
+});
+</script>
+
+<style scoped lang="scss">
+.production-container {
+ padding: 20px;
+
+ .el-card:nth-child(1) {
+ margin-bottom: 20px;
+ }
+}
+
+.search-bar {
+ margin-bottom: 20px;
+ display: flex;
+ gap: 10px;
+
+ .el-input {
+ width: 20%;
+ }
+}
+
+.search-form {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ margin-bottom: 20px;
+
+ .el-form-item {
+ margin-right: 10px;
+ }
+
+ .el-button {
+ margin-left: 10px;
+ }
+}
+
+.coal-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ align-items: center;
+
+ .coal-tag {
+ margin-right: 4px;
+ margin-bottom: 4px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+
+ .no-data {
+ color: #999;
+ font-style: italic;
+ }
+}
+</style>
diff --git a/src/views/warehouseManagement/index.vue b/src/views/warehouseManagement/index.vue
index 32946d7..a7d612c 100644
--- a/src/views/warehouseManagement/index.vue
+++ b/src/views/warehouseManagement/index.vue
@@ -82,6 +82,11 @@
width="180"
sortable
/>
+ <el-table-column prop="type" label="鐓ゆ枡绫诲瀷">
+ <template #default="scope">
+ {{scope.row.type === 1 ? '鎴愬搧' : '鍘熸枡'}}
+ </template>
+ </el-table-column>
<el-table-column prop="coal" label="鐓ょ" sortable />
<el-table-column prop="unit" label="鍗曚綅" width="70" />
<el-table-column
@@ -440,6 +445,22 @@
</el-select>
</el-form-item>
</el-col>
+ <el-col :span="12">
+ <el-form-item label="鐓ゆ枡绫诲瀷" prop="type">
+ <el-select
+ v-model="mergeForm.type"
+ placeholder="璇烽�夋嫨鐓ゆ枡绫诲瀷"
+ :disabled="operationType === 'view'"
+ >
+ <el-option
+ :label="item.label"
+ v-for="item in typeList"
+ :key="item.value"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
</el-row>
<el-divider></el-divider>
<el-row>
@@ -499,6 +520,16 @@
// 鍚堝苟寮规
const mergeVisible = ref(false);
const operationType = ref("");
+const typeList = ref([
+ {
+ label: "鎴愬搧",
+ value: 1,
+ },
+ {
+ label: "鍘熸枡",
+ value: 2,
+ },
+]);
const data = reactive({
form: {
supplierName: "",
--
Gitblit v1.9.3