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