From 4dfa438cbd4f3f475c97a2233688d1fe3628099c Mon Sep 17 00:00:00 2001
From: cuilei <ray_tsu1@163.com>
Date: 星期六, 09 八月 2025 17:52:11 +0800
Subject: [PATCH] 生产管控 排产工单页面调整

---
 src/views/mes/MesProductionWorkOrderListView.vue              |   84 +++-
 src/views/mes/modules/MesProductionWorkOrderScheduleModal.vue |  793 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/views/mes/modules/MesProductionWeekCalendar.vue           |   55 ++
 src/views/base/ShiftManager.vue                               |    2 
 4 files changed, 904 insertions(+), 30 deletions(-)

diff --git a/src/views/base/ShiftManager.vue b/src/views/base/ShiftManager.vue
index 69321bc..8dec947 100644
--- a/src/views/base/ShiftManager.vue
+++ b/src/views/base/ShiftManager.vue
@@ -179,7 +179,7 @@
       handleEdit(record) {
         this.$refs.modalForm.edit(record)
         this.$refs.modalForm.title = '缂栬緫鐝'
-        this.$refs.modalForm.disableSubmit = true
+        this.$refs.modalForm.disableSubmit = false
       },
       handleDelete: function(record){
         if(!this.url.deleteBatch){
diff --git a/src/views/mes/MesProductionWorkOrderListView.vue b/src/views/mes/MesProductionWorkOrderListView.vue
index f9e9b5f..17bf9dc 100644
--- a/src/views/mes/MesProductionWorkOrderListView.vue
+++ b/src/views/mes/MesProductionWorkOrderListView.vue
@@ -1,5 +1,5 @@
 <template>
-  <a-card :bordered="false" title="鎺掍骇宸ュ崟">
+  <a-card :bordered="false">
     <!-- 鏌ヨ鍖哄煙 -->
     <div class="table-page-search-wrapper">
       <a-form layout="inline" @keyup.enter.native="searchQuery">
@@ -14,24 +14,33 @@
               <j-input placeholder="璇疯緭鍏ョ墿鏂欑紪鍙�" v-model="queryParam.materialNumber"></j-input>
             </a-form-item>
           </a-col>
-          <a-col :xl="6" :lg="7" :md="8" :sm="24">
-            <a-form-item label="宸ュ崟鐘舵��">
-              <j-dict-select-tag dictCode="work_order_status" placeholder="璇疯緭鍏ュ伐鍗曠姸鎬�"
-                                 v-model="queryParam.workOrderStatus"></j-dict-select-tag>
-            </a-form-item>
-          </a-col>
-          <a-col :xl="6" :lg="7" :md="8" :sm="24">
-            <a-form-item label="閲嶅彂甯冧汉">
-              <j-select-user-by-dep placeholder="璇疯緭鍏ラ噸鍙戝竷浜�"
-                                    v-model="queryParam.republisher"></j-select-user-by-dep>
-            </a-form-item>
-          </a-col>
+          <template v-if="toggleSearchStatus">
+            <a-col :xl="6" :lg="7" :md="8" :sm="24">
+              <a-form-item label="宸ュ崟鐘舵��">
+                <j-dict-select-tag dictCode="work_order_status" placeholder="璇疯緭鍏ュ伐鍗曠姸鎬�"
+                                   v-model="queryParam.workOrderStatus"></j-dict-select-tag>
+              </a-form-item>
+            </a-col>
+            <a-col :xl="6" :lg="7" :md="8" :sm="24">
+              <a-form-item label="閲嶅彂甯冧汉">
+                <j-select-user-by-dep placeholder="璇疯緭鍏ラ噸鍙戝竷浜�"
+                                      v-model="queryParam.republisher"></j-select-user-by-dep>
+              </a-form-item>
+            </a-col>
+          </template>
           <a-col :xl="6" :lg="7" :md="8" :sm="24">
             <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
               <a-button type="primary" @click="searchQuery" icon="search">鏌ヨ</a-button>
-              <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">閲嶇疆</a-button>
+              <a-button type="info" @click="searchReset" icon="reload" style="margin-left: 8px">閲嶇疆</a-button>
+              <a @click="handleToggleSearch" style="margin-left: 8px">
+                {{ toggleSearchStatus ? '鏀惰捣' : '灞曞紑' }}
+                <a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
+              </a>
             </span>
           </a-col>
+        </a-row>
+        <a-row>
+          <a-button type="primary" @click="productionSchedule" icon="retweet" style="margin-bottom: 8px">鎺掍骇</a-button>
         </a-row>
       </a-form>
     </div>
@@ -73,14 +82,22 @@
           </template>
 
           <span slot="action" slot-scope="text, record">
-          <a @click="handleEdit(record)">缂栬緫</a>
-
-          <a-divider type="vertical" />
+            <a @click="handleDetail(record)">璇︽儏</a>
+          <span v-if="record.workOrderStatus === 'PUBLISHED'">
+            <a-divider type="vertical" />
+            <a @click="handleRePublish(record)">閲嶅彂甯�</a>
+          </span>
+          <span v-if="record.workOrderStatus === 'NEW'">
+            <a-divider type="vertical" />
+            <a-popconfirm title="纭畾鍙戝竷鍚�?" @confirm="() => handlePublish(record.id)">
+                <a>鍙戝竷</a>
+            </a-popconfirm>
+            <a-divider type="vertical" />
           <a-dropdown>
             <a class="ant-dropdown-link">鏇村 <a-icon type="down" /></a>
             <a-menu slot="overlay">
               <a-menu-item>
-                <a @click="handleDetail(record)">璇︽儏</a>
+                <a @click="handleEdit(record)">缂栬緫</a>
               </a-menu-item>
               <a-menu-item>
                 <a-popconfirm title="纭畾鍒犻櫎鍚�?" @confirm="() => handleDelete(record.id)">
@@ -89,6 +106,8 @@
               </a-menu-item>
             </a-menu>
           </a-dropdown>
+          </span>
+
         </span>
         </a-table>
       </div>
@@ -174,6 +193,8 @@
     <mes-production-order-modal ref="MesProductionOrderModal"></mes-production-order-modal>
     <MesMaterialUnloadingList ref="MesMaterialUnloadingList"></MesMaterialUnloadingList>
     <MesMaterialTransferDetailList ref="MesMaterialTransferDetailList"></MesMaterialTransferDetailList>
+    <MesProductionWorkOrderScheduleModal ref="MesProductionWorkOrderScheduleModal"></MesProductionWorkOrderScheduleModal>
+    <MesProductionWorkOrderRepublishModal ref="MesProductionWorkOrderRepublishModal" @ok="modalFormOk"></MesProductionWorkOrderRepublishModal>
   </a-card>
 </template>
 
@@ -186,10 +207,12 @@
 import { JVxeTableModelMixin } from '@/mixins/JVxeTableModelMixin.js'
 import { JVXETypes } from '@/components/jeecg/JVxeTable'
 import { filterMultiDictText } from '@/components/dict/JDictSelectUtil'
-import { getAction } from '@api/manage'
+import { getAction, requestPut } from '@api/manage'
 import MesProductionOrderModal from '@views/mes/modules/MesProductionOrderModal.vue'
 import MesMaterialUnloadingList from '@views/mes/MesMaterialUnloadingList.vue'
 import MesMaterialTransferDetailList from '@views/mes/MesMaterialTransferDetailList.vue'
+import MesProductionWorkOrderScheduleModal from '@views/mes/modules/MesProductionWorkOrderScheduleModal.vue'
+import MesProductionWorkOrderRepublishModal from '@views/mes/modules/MesProductionWorkOrderRepublishModal.vue'
 
 export default {
   name: 'MesProductionWorkOrderList',
@@ -198,7 +221,9 @@
     MesProductionWorkOrderModal,
     MesProductionOrderModal,
     MesMaterialUnloadingList,
-    MesMaterialTransferDetailList
+    MesMaterialTransferDetailList,
+    MesProductionWorkOrderScheduleModal,
+    MesProductionWorkOrderRepublishModal
   },
   data() {
     return {
@@ -648,7 +673,8 @@
         queryCompletenessCheckByWorkOrderId:'/meskittingcompletenesscheck/mesKittingCompletenessCheck/queryCompletenessCheckByWorkOrderId',
         queryOrderById:'/mesproductionwork/mesProductionOrder/queryById',
         queryUnloadingByLoadingId:'/mes/mesMaterialUnloading/queryUnloadingByLoadingId',
-        queryTransferDetailBy:'/mes/mesMaterialTransferDetail/queryTransferDetailBy'
+        queryTransferDetailBy:'/mes/mesMaterialTransferDetail/queryTransferDetailBy',
+        publish: '/mesproductionworkorder/mesProductionWorkOrder/publish'
       },
       dictOptions: {},
       superFieldList: []
@@ -663,6 +689,22 @@
     }
   },
   methods: {
+    productionSchedule() {
+      this.$refs.MesProductionWorkOrderScheduleModal.scheduleOpen()
+    },
+    handlePublish(id) {
+      requestPut(this.url.publish, null, { ids: id }).then((res) => {
+        if (res.success) {
+          this.$message.success(res.message)
+          this.loadData()
+        } else {
+          this.$message.warning(res.message)
+        }
+      })
+    },
+    handleRePublish(record) {
+      this.$refs.MesProductionWorkOrderRepublishModal.add(record)
+    },
     async handleTransferDetail(row){
       console.log('row---->',row)
       const transferDetailResult = await getAction(this.url.queryTransferDetailBy,{'requestId':row.id})
diff --git a/src/views/mes/modules/MesProductionWeekCalendar.vue b/src/views/mes/modules/MesProductionWeekCalendar.vue
index 46c0716..d902bae 100644
--- a/src/views/mes/modules/MesProductionWeekCalendar.vue
+++ b/src/views/mes/modules/MesProductionWeekCalendar.vue
@@ -1,11 +1,11 @@
 <!-- src/views/mes/modules/MesProductionWeekCalendar.vue -->
 <template>
   <div class="week-calendar">
-    <div class="calendar-header">
-      <a-button icon="left" @click="prevWeek" size="small" />
+    <div v-if="showHeader" class="calendar-header">
+      <a-button v-if="showNavigation" icon="left" @click="prevWeek" size="small" />
       <span class="current-week-range">{{ weekRangeText }}</span>
-      <a-button icon="right" @click="nextWeek" size="small" />
-      <a-button @click="goToToday" size="small" style="margin-left: 8px">浠婂ぉ</a-button>
+      <a-button v-if="showNavigation" icon="right" @click="nextWeek" size="small" />
+      <a-button v-if="showTodayButton" @click="goToToday" size="small" style="margin-left: 8px">浠婂ぉ</a-button>
     </div>
 
     <div class="calendar-grid">
@@ -14,11 +14,16 @@
           v-for="(day, index) in weekDays"
           :key="index"
           class="header-cell"
+          :class="{ 'first-day-highlight': showFirstDayHighlight && index === 0 }"
         >
           <div class="day-name">{{ day.format('ddd') }}</div>
           <div
             class="day-number"
-            :class="{ today: isToday(day), selected: isSelected(day) }"
+            :class="{
+              'highlight': showTodayHighlight && isToday(day),
+              today: isToday(day),
+              selected: isSelected(day)
+            }"
             @click="selectDay(day)"
           >
             {{ day.date() }}
@@ -31,7 +36,12 @@
           v-for="(day, index) in weekDays"
           :key="index"
           class="day-cell"
-          :class="{ today: isToday(day), selected: isSelected(day) }"
+          :class="{
+            'highlight': showTodayHighlight && isToday(day),
+            'first-day-highlight': showFirstDayHighlight && index === 0,
+            today: isToday(day),
+            selected: isSelected(day)
+          }"
           @click="selectDay(day)"
         >
           <div class="cell-content">
@@ -64,6 +74,31 @@
     startDate: {
       type: [Object, String, Date],
       default: null
+    },
+    // 鏄惁鏄剧ず澶撮儴瀵艰埅
+    showHeader: {
+      type: Boolean,
+      default: true
+    },
+    // 鏄惁鏄剧ず瀵艰埅鎸夐挳锛堝墠鍚庣炕椤碉級
+    showNavigation: {
+      type: Boolean,
+      default: true
+    },
+    // 鏄惁鏄剧ず浠婂ぉ鎸夐挳
+    showTodayButton: {
+      type: Boolean,
+      default: true
+    },
+    // 鏄惁楂樹寒褰撳ぉ
+    showTodayHighlight: {
+      type: Boolean,
+      default: true
+    },
+    // 鏄惁楂樹寒鏈懆绗竴澶�
+    showFirstDayHighlight: {
+      type: Boolean,
+      default: true
     }
   },
   data() {
@@ -179,6 +214,10 @@
   border-right: none;
 }
 
+.header-cell.first-day-highlight {
+  background-color: #ffffff; /* 閲嶇疆绗竴澶╃殑鑳屾櫙鑹� */
+}
+
 .day-name {
   font-size: 12px;
   color: #666;
@@ -196,7 +235,7 @@
   border-radius: 50%;
 }
 
-.day-number.today {
+.day-number.today.highlight {
   background-color: #1890ff;
   color: white;
 }
@@ -222,7 +261,7 @@
   border-right: none;
 }
 
-.day-cell.today {
+.day-cell.today.highlight {
   background-color: #e6f7ff;
 }
 
diff --git a/src/views/mes/modules/MesProductionWorkOrderScheduleModal.vue b/src/views/mes/modules/MesProductionWorkOrderScheduleModal.vue
new file mode 100644
index 0000000..8a0a54a
--- /dev/null
+++ b/src/views/mes/modules/MesProductionWorkOrderScheduleModal.vue
@@ -0,0 +1,793 @@
+<template>
+  <j-modal
+    :title="title"
+    :width="width"
+    :visible="visible"
+    :fullscreen="true"
+    switchFullscreen
+    @ok="handleOk"
+    :okButtonProps="{ class:{'jee-hidden': disableSubmit} }"
+    @cancel="handleCancel"
+    cancelText="鍏抽棴">
+
+    <a-row :gutter="{ xs: 4, sm: 8, md: 16}">
+      <a-col :span="12">
+        <a-card :bordered="false">
+          <!-- 鏌ヨ鍖哄煙 -->
+          <div class="table-page-search-wrapper">
+            <a-form layout="inline" @keyup.enter.native="searchQuery">
+              <a-row :gutter="12">
+                <a-col :span="7">
+                  <a-form-item label="浜х嚎" :label-col="{span: 6}" :wrapper-col="{span: 18}">
+                    <j-tree-select dict="base_factory,factory_name,id" pid-field="parent_id"
+                                   v-model="queryParam.factoryId" style="width: 100%"></j-tree-select>
+                  </a-form-item>
+                </a-col>
+                <a-col :span="9">
+                  <a-form-item label="鏃ユ湡" :label-col="{span: 4}" :wrapper-col="{span: 20}">
+                    <a-range-picker
+                      style="width: 100%"
+                      @change="dateRangeChange"
+                      :value="dateRange"
+                      :disabledDate="disabledDate"
+                      @openChange="onOpenChange"
+                    />
+                  </a-form-item>
+                </a-col>
+                <a-col :span="8">
+                  <span style="float: left;overflow: hidden;white-space: nowrap;" class="table-page-search-submitButtons">
+                    <a-button type="primary" @click="searchQuery" icon="search">鏌ヨ</a-button>
+                    <a-button type="info" @click="searchReset" icon="reload" style="margin-left: 8px">閲嶇疆</a-button>
+                  </span>
+                </a-col>
+              </a-row>
+              <a-row :gutter="12" style="margin-top: 8px;">
+                <a-col :span="24">
+                  <a-button type="primary" @click="generatePlan" icon="retweet">鐢熸垚鎺掍骇璁″垝</a-button>
+                </a-col>
+              </a-row>
+            </a-form>
+          </div>
+
+          <div class="table-operator">
+            <a-dropdown v-if="selectedRowKeys.length > 0">
+              <a-menu slot="overlay">
+                <a-menu-item key="1" @click="batchDel">
+                  <a-icon type="delete" />
+                  鍒犻櫎
+                </a-menu-item>
+              </a-menu>
+              <a-button style="margin-left: 8px"> 鎵归噺鎿嶄綔
+                <a-icon type="down" />
+              </a-button>
+            </a-dropdown>
+          </div>
+<!--          <div>-->
+<!--            <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">-->
+<!--              <i class="anticon anticon-info-circle ant-alert-icon"></i> 宸查�夋嫨 <a-->
+<!--              style="font-weight: 600">{{ selectedRowKeys.length }}</a>椤�-->
+<!--              <a style="margin-left: 24px" @click="onClearSelected">娓呯┖</a>-->
+<!--            </div>-->
+
+            <vxe-table
+              ref="table"
+              border
+              show-overflow
+              show-header-overflow
+              :scroll-x="{enabled: true}"
+              :data="dataSource"
+              :edit-config="{trigger: 'click', mode: 'cell', activeMethod: isRowEditable}"
+              :edit-rules="editRules"
+            >
+<!--              <vxe-table-column type="checkbox" width="40" fixed="left"></vxe-table-column>-->
+              <vxe-table-column type="seq" width="40" />
+              <vxe-table-column width="100" title="宸ュ崟鍙�" field="workOrderCode">
+                <template #default="{ row }">
+                  <span v-if="row.workOrderCode">{{ row.workOrderCode }}</span>
+                  <span v-else class="placeholder-text">绯荤粺鑷姩鐢熸垚</span>
+                </template>
+              </vxe-table-column>
+              <vxe-table-column
+                title="鐗╂枡缂栧彿"
+                field="materialNumber"
+                width="120"
+                :edit-render="{name: '$select', options: materialOptions, events: {change: handleMaterialChange}}">
+                <template #default="{ row }">
+                  <span v-if="row.materialNumber">{{ row.materialNumber }}</span>
+                  <span v-else class="placeholder-text">璇烽�夋嫨鐗╂枡缂栧彿</span>
+                </template>
+              </vxe-table-column>
+              <vxe-table-column title="鐗╂枡鍚嶇О" field="materialName" width="100">
+                <template #default="{ row }">
+                  <span v-if="row.materialName">{{ row.materialName }}</span>
+                  <span v-else class="placeholder-text">鐗╂枡鍚嶇О鑷姩濉厖</span>
+                </template>
+              </vxe-table-column>
+              <vxe-table-column title="鐝粍id" :visible="false" field="groupId"></vxe-table-column>
+              <vxe-table-column
+                title="鐝"
+                width="100"
+                field="shiftId"
+                :edit-render="{name: '$select', options: shiftOptions, events: {change: handleShiftChange}}">
+                <template #default="{ row }">
+                  <span v-if="row.shiftId">{{ row.shiftName || row.shiftId_dictText }}</span>
+                  <span v-else class="placeholder-text">璇烽�夋嫨鐝</span>
+                </template>
+              </vxe-table-column>
+              <vxe-table-column
+                title="鎺掍骇鏃ユ湡"
+                field="workOrderDate"
+                width="120"
+                :edit-render="{name: '$select', options: workOrderDateOptions, events: {change: handleWorkOrderDateChange}}">
+                <template #default="{ row }">
+                  <span v-if="row.workOrderDate">{{ row.workOrderDate }}</span>
+                  <span v-else class="placeholder-text">璇烽�夋嫨鎺掍骇鏃ユ湡</span>
+                </template>
+              </vxe-table-column>
+              <vxe-table-column
+                title="璁″垝鐢熶骇鏁伴噺"
+                field="planQuantity"
+                width="100"
+                :edit-render="{name: '$input'}">
+                <template #default="{ row }">
+                  <span v-if="row.planQuantity">{{ row.planQuantity }}</span>
+                  <span v-else class="placeholder-text">璇峰~鍐欒鍒掔敓浜ф暟閲�</span>
+                </template>
+              </vxe-table-column>
+              <vxe-table-column title="" width="40" fixed="right">
+                <template #default="{ row }">
+                  <span v-if="row.workOrderStatus === 'NEW' || !row.workOrderStatus">
+                    <i class="vxe-icon--remove" @click="handleRemove(row)"></i>
+                  </span>
+                </template>
+              </vxe-table-column>
+            </vxe-table>
+            <a-button
+              :disabled="addScheduleFlag"
+              style="width: 100%; margin-top: 16px; margin-bottom: 8px"
+              type="dashed"
+              icon="plus"
+              @click="addSchedulePlan"
+            >鏂板鎺掍骇璁″垝
+            </a-button>
+<!--            <vxe-pager-->
+<!--              :current-page="pagination.current"-->
+<!--              :page-size="pagination.pageSize"-->
+<!--              :total="pagination.total"-->
+<!--              :layouts="['PrevPage', 'JumpNumber', 'NextPage', 'FullJump', 'Sizes', 'Total']"-->
+<!--              @page-change="handlePageChange">-->
+<!--            </vxe-pager>-->
+          </div>
+        </a-card>
+      </a-col>
+      <a-col :span="12">
+        <a-card :bordered="false">
+          <MesProductionWeekCalendar
+            ref="weekCalendar"
+            :start-date="calendarStartDate"
+            :show-navigation="false"
+            :show-today-button="false"
+            :show-today-highlight="false"
+            :show-first-day-highlight="false"
+            @select="onDateSelect"
+            @change="onCalendarChange"
+          >
+            <template #dateCell="{ date, isSelected, isToday }">
+              <div class="custom-date-content">
+                <div
+                  v-for="(workOrder, index) in getWorkOrdersForDate(date)"
+                  :key="workOrder.id"
+                  class="work-order-item"
+                  :class="getColorClass(index)"
+                >
+                  <span class="work-order-shift">{{ workOrder.shiftCode }}</span>
+                  <span class="work-order-material">{{ workOrder.materialName }}</span>
+                  <span class="work-order-quantity">{{ workOrder.planQuantity }}</span>
+                </div>
+              </div>
+            </template>
+          </MesProductionWeekCalendar>
+        </a-card>
+      </a-col>
+    </a-row>
+
+  </j-modal>
+
+
+</template>
+
+<script>
+
+import '@/assets/less/TableExpand.less'
+import MesProductionWeekCalendar from '@views/mes/modules/MesProductionWeekCalendar.vue'
+import moment from 'moment'
+import { getAction, postAction, requestPut } from '@api/manage'
+import { ajaxGetDictItems } from '@api/api'
+
+export default {
+  name: 'MesProductionWorkOrderScheduleModal',
+  components: {
+    MesProductionWeekCalendar
+  },
+  data() {
+    return {
+      title: '鎺掍骇绠$悊椤甸潰',
+      width: 600,
+      visible: false,
+      disableSubmit: false,
+      addScheduleFlag: false,
+      queryParam: {},
+      dataSource: [],
+      shiftOptions: [],
+      groupShiftMap: {},
+      materialOptions: [],
+      materNumberNameMap: {},
+      workOrderDateOptions: [],
+      selectedRowKeys: [],
+      selectionRows: [],
+      /* 鍒嗛〉鍙傛暟 */
+      pagination: {
+        current: 1,
+        pageSize: 10,
+        total: 0
+      },
+      // 琛ㄥご
+      url: {
+        list: '/mesproductionworkorder/mesProductionWorkOrder/list',
+        delete: '/mesproductionworkorder/mesProductionWorkOrder/delete',
+        deleteBatch: '/mesproductionworkorder/mesProductionWorkOrder/deleteBatch',
+        exportXlsUrl: '/mesproductionworkorder/mesProductionWorkOrder/exportXls',
+        importExcelUrl: 'mesproductionworkorder/mesProductionWorkOrder/importExcel',
+        listProductionLinesOption: '/base/factory/queryIdTree',
+        queryShiftGroupByFactoryId: '/base/shiftGroup/queryShiftGroupByFactoryId',
+        queryFactoryById: '/base/factory/queryById',
+        schedule: '/mesproductionworkorder/mesProductionWorkOrder/schedule',
+        addSchedulePlan: '/mesproductionworkorder/mesProductionWorkOrder/addSchedulePlan'
+      },
+      editRules: {
+        materialNumber: [
+          { required: true, message: '鐗╂枡缂栧彿蹇呴』閫夋嫨' }
+        ],
+        shiftId: [
+          { required: true, message: '鐝蹇呴』閫夋嫨' }
+        ],
+        workOrderDate: [
+          { required: true, message: '鎺掍骇鏃ユ湡蹇呴』閫夋嫨' }
+        ],
+        planQuantity: [
+          { required: true, message: '璁″垝鐢熶骇鏁伴噺涓哄繀濉�' }
+        ]
+      },
+      dictOptions: {},
+      superFieldList: [],
+      // 鐢ㄤ簬婕旂ず鐨勫伐鍗曟暟鎹�
+      workOrdersByDate: {},
+      // 鏃ュ巻璧峰鏃ユ湡
+      calendarStartDate: moment().startOf('week'), // 榛樿鏄剧ず褰撳墠鍛ㄧ殑鍛ㄤ竴
+      productionLineData: [],
+      dateRange: [],
+      tempStartDate: null, // 涓存椂瀛樺偍寮�濮嬫棩鏈�
+      hoveredDate: null    // 瀛樺偍榧犳爣鎮仠鐨勬棩鏈�
+    }
+  },
+  created() {
+  },
+  watch: {
+    dataSource: {
+      handler() {
+        // 浣跨敤 nextTick 纭繚鍦� DOM 鏇存柊鍚庡啀鏇存柊鏃ュ巻
+        this.$nextTick(() => {
+          this.updateCalendarWorkOrders()
+        })
+      },
+      deep: true
+    }
+  },
+  computed: {
+    importExcelUrl: function() {
+      return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`
+    }
+  },
+  methods: {
+    isRowEditable({row, rowIndex}) {
+      return row.workOrderStatus === 'NEW' || !row.workOrderStatus
+    },
+    getColorClass(index) {
+      return index % 2 === 0 ? 'blue-item' : 'red-item';
+    },
+    scheduleOpen() {
+      this.visible = true
+    },
+    close() {
+      this.$emit('close')
+      this.visible = false
+    },
+    handleOk() {
+      // 琛ㄦ牸鍏ㄨ〃鏍¢獙
+      this.$refs.table.validate((valid) => {
+        if (valid) {
+          this.$message.error("璇峰畬鎴愭墍鏈夊繀濉俊鎭悗鍐嶆彁浜わ紒")
+        } else {
+          let tableData = this.$refs.table.getTableData().fullData
+          postAction(this.url.addSchedulePlan, tableData).then(res=> {
+            if (res.success) {
+              this.$message.success(res.message)
+              this.submitCallback()
+            } else {
+              this.$message.warning(res.message)
+            }
+          })
+        }
+      })
+    },
+    submitCallback() {
+      this.$emit('ok')
+      this.queryParam = {}
+      this.dateRange = []
+      this.workOrdersByDate = {}
+      this.dataSource = []
+      this.visible = false
+    },
+    handleCancel() {
+      this.queryParam = {}
+      this.dateRange = []
+      this.workOrdersByDate = {}
+      this.dataSource = []
+      this.pagination.current = 1
+      this.pagination.pageSize = 10
+      this.pagination.total = 0
+      this.close()
+    },
+    onClearSelected() {
+      this.selectedRowKeys = []
+      this.selectionRows = []
+    },
+    loadData(arg) {
+      return new Promise((resolve, reject) => {
+        //鍔犺浇鏁版嵁 鑻ヤ紶鍏ュ弬鏁�1鍒欏姞杞界涓�椤电殑鍐呭
+        if (arg === 1) {
+          this.pagination.current = 1
+        }
+        var params = Object.assign({}, this.queryParam, {
+          pageNo: this.pagination.current,
+          pageSize: this.pagination.pageSize
+        })
+        console.log('params', params)
+        if (!params) {
+          reject(new Error('鏌ヨ鍙傛暟鏃犳晥'))
+          return false
+        }
+        this.loading = true
+        getAction(this.url.list, params).then((res) => {
+          if (res.success) {
+            this.dataSource = res.result.records || res.result
+            if (res.result.total) {
+              this.pagination.total = res.result.total
+            } else {
+              this.pagination.total = 0
+            }
+            resolve(res)
+          } else {
+            this.$message.warning(res.message)
+            reject(new Error(res.message))
+          }
+        }).catch(error => {
+          reject(error)
+        }).finally(() => {
+          this.loading = false
+        })
+      })
+    },
+    handlePageChange({ currentPage, pageSize }) {
+      this.pagination.current = currentPage
+      this.pagination.pageSize = pageSize
+      this.loadData()
+    },
+    searchQuery() {
+      if (!this.queryParam.factoryId || this.dateRange.length === 0) {
+        this.$message.warning('璇烽�夋嫨浜х嚎鍙婃帓浜ф棩鏈熻寖鍥达紒')
+        return
+      }
+      this.queryParam = Object.assign(this.queryParam, this.dateRange)
+      this.loadData(1).then(() => {
+        this.initTableData(null, this.dateRange)
+        // 璁剧疆鏃ュ巻鏄剧ず涓烘煡璇㈡棩鏈熻寖鍥村唴鐨勭涓�鍛�
+        if (this.dateRange[0]) {
+          this.calendarStartDate = this.dateRange[0].clone().startOf('week');
+        }
+      })
+    },
+    searchReset() {
+      this.queryParam = {}
+      this.dateRange = []
+      this.workOrdersByDate = {}
+      this.dataSource = []
+      this.pagination.total = 0
+    },
+    // 鐢熸垚宸ュ崟鍙风殑鏂规硶
+    generateWorkOrderCode(row) {
+      // 鑾峰彇鍚勪釜瀛楁鐨勫��
+      const factoryCode = row.factoryCode || '';
+      const materialNumber = row.materialNumber || '';
+      const workDate = row.workOrderDate || '';
+      const shiftCode = row.shiftCode || '';
+
+      let formattedDate = workDate;
+      if (workDate && workDate.length >= 10) {
+        // 濡傛灉鏄畬鏁存棩鏈熸牸寮忥紝鎻愬彇 YYYY-MM-DD 閮ㄥ垎
+        formattedDate = workDate.substring(0, 10).replace(/-/g, '');
+      }
+      return `${factoryCode}${materialNumber}${formattedDate}${shiftCode}`;
+    },
+    updateWorkOrderCode(row) {
+      row.workOrderCode = this.generateWorkOrderCode(row);
+    },
+    // 鏍煎紡鍖栨棩鏈熶负 YYYY-MM-DD
+    formatDate(date) {
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      return `${year}-${month}-${day}`;
+    },
+    // 鐢熸垚鏃ユ湡鑼冨洿鏁扮粍
+    generateDateRangeOptions(startDate, endDate) {
+      const dateOptions = [];
+      const start = new Date(startDate);
+      const end = new Date(endDate);
+
+      // 璁剧疆鏃堕棿涓烘瘡澶╃殑0鐐�
+      start.setHours(0, 0, 0, 0);
+      end.setHours(0, 0, 0, 0);
+
+      // 鐢熸垚鏃ユ湡鑼冨洿鍐呯殑鎵�鏈夋棩鏈�
+      for (let date = new Date(start); date <= end; date.setDate(date.getDate() + 1)) {
+        const formattedDate = this.formatDate(date);
+        dateOptions.push({
+          value: formattedDate,
+          label: formattedDate
+        });
+      }
+      return dateOptions;
+    },
+    initDictSelectOptions(record) {
+      return new Promise((resolve) => {
+        // 骞惰鎵ц澶氫釜寮傛璇锋眰
+        const promises = [];
+
+        let factoryId
+        if (record && record.length > 0) {
+          factoryId = record[0].factoryId
+        } else {
+          factoryId = this.queryParam.factoryId
+        }
+
+        const shiftGroupPromise = getAction(this.url.queryShiftGroupByFactoryId, { factoryId: factoryId }).then(res => {
+          if (res.success) {
+            this.shiftOptions = res.result.map(item => {
+              return {
+                value: item.shiftId,
+                label: item.shiftId_dictText
+              }
+            })
+            this.groupShiftMap = res.result.reduce((map, item) => {
+              map[item.shiftId] = {
+                groupId: item.id,
+                shiftCode: item.shiftCode_dictText,
+                shiftName: item.shiftId_dictText
+              }
+              return map
+            }, {})
+          }
+        }).catch(() => {
+        });
+        promises.push(shiftGroupPromise);
+
+        const materialNumberPromise = ajaxGetDictItems("lsw_material,material_name,material_number,del_flag!='1' order by material_number asc", null).then(res => {
+          if (res.success) {
+            this.materialOptions = res.result.map(item => {
+              return {
+                value: item.value,
+                label: item.value
+              }
+            });
+            this.materNumberNameMap = res.result.reduce((map, item) => {
+              map[item.value] = item.text
+              return map
+            }, {})
+          }
+        }).catch(() => {
+        });
+        promises.push(materialNumberPromise);
+
+        // 绛夊緟鎵�鏈夎姹傚畬鎴�
+        Promise.all(promises).then(() => {
+          resolve();
+        });
+      })
+    },
+    generatePlan() {
+      if (!this.queryParam.factoryId || this.dateRange.length === 0) {
+        this.$message.warning('璇烽�夋嫨浜х嚎鍙婃帓浜ф棩鏈熻寖鍥达紒')
+        return
+      }
+      getAction(this.url.schedule, {
+        factoryId: this.queryParam.factoryId,
+        startDate: this.dateRange[0].format('YYYY-MM-DD'),
+        endDate: this.dateRange[1].format('YYYY-MM-DD')
+      }).then(res => {
+        if (res.success) {
+          const record = res.result
+          this.initTableData(record, this.dateRange)
+          // 璁剧疆鏃ュ巻鏄剧ず涓虹敓鎴愯鍒掓棩鏈熻寖鍥村唴鐨勭涓�鍛�
+          if (this.dateRange[0]) {
+            this.calendarStartDate = this.dateRange[0].clone().startOf('week');
+          }
+        }
+      })
+    },
+    initTableData(record, dateRange) {
+      // 鍏堝姞杞藉瓧鍏告暟鎹紝鍐嶅垵濮嬪寲琛ㄥ崟
+      this.initDictSelectOptions(record).then(() => {
+        this.$nextTick(() => {
+          // 鏍规嵁鏃ユ湡鑼冨洿鐢熸垚鎺掍骇鏃ユ湡閫夐」
+          if (dateRange && dateRange.length === 2) {
+            this.workOrderDateOptions = this.generateDateRangeOptions(dateRange[0], dateRange[1]);
+          }
+
+          // 涓烘瘡鏉¤褰曠敓鎴愬伐鍗曞彿
+          if (record && record.length > 0) {
+            record.forEach(row => {
+              if (!row.workOrderCode) {
+                this.updateWorkOrderCode(row);
+              }
+            });
+            this.dataSource.push(...record)
+          }
+          this.pagination.total = this.dataSource.length
+          this.$nextTick(() => {
+            if (this.$refs.table && this.dataSource.length > 0) {
+              // 婵�娲荤涓�琛岃繘鍏ョ紪杈戠姸鎬�
+              this.$refs.table.setActiveRow(this.dataSource[0])
+            }
+          })
+        });
+      })
+    },
+    handleWorkOrderDateChange($event, value) {
+      $event.row.workOrderDate = value.value;
+      this.updateWorkOrderCode($event.row)
+    },
+    handleMaterialChange($event, value) {
+      const key = value.value
+      if (key && this.materNumberNameMap[key]) {
+        $event.row.materialName = this.materNumberNameMap[key]
+      } else {
+        $event.row.materialName = '';
+      }
+      // 鏇存柊鐗╂枡缂栧彿
+      $event.row.materialNumber = key;
+      // 閲嶆柊鐢熸垚宸ュ崟鍙�
+      this.updateWorkOrderCode($event.row);
+    },
+    handleShiftChange($event, value) {
+      const key = value.value
+      // 浠� groupShiftMap 涓幏鍙栧搴旂殑鐝粍id
+      if (key && this.groupShiftMap[key]) {
+        // 鏇存柊褰撳墠琛岀殑鐝粍id銆佺彮娆$紪鐮侊紙鎷兼帴宸ュ崟鍙风敤锛夊瓧娈�
+        $event.row.groupId = this.groupShiftMap[key].groupId
+        $event.row.shiftCode = this.groupShiftMap[key].shiftCode
+        $event.row.shiftName = this.groupShiftMap[key].shiftName
+      } else {
+        // 濡傛灉娌℃湁鍖归厤鐨勭彮缁勶紝鍒欐竻绌�
+        $event.row.groupId = ''
+        $event.row.shiftCode = ''
+        $event.row.shiftName = ''
+      }
+      // 閲嶆柊鐢熸垚宸ュ崟鍙�
+      this.updateWorkOrderCode($event.row);
+    },
+    async addSchedulePlan() {
+      if (!this.queryParam.factoryId || this.dateRange.length === 0) {
+        this.$message.warning('璇峰厛閫夋嫨浜х嚎鍙婃帓浜ф棩鏈熻寖鍥达紒')
+        return
+      }
+      let factoryCode = ''
+      if (this.dataSource.length === 0) {
+        const res = await getAction(this.url.queryFactoryById, {id: this.queryParam.factoryId})
+        if (res.success) {
+          factoryCode = res.result.factoryCode
+        }
+      } else {
+        factoryCode = this.dataSource[0].factoryCode
+      }
+      // 鍒涘缓鏂拌鏁版嵁
+      const newRow = {
+        workOrderCode: factoryCode,
+        materialNumber: '',
+        materialName: '',
+        factoryId: this.dataSource.length > 0 ? this.dataSource[0].factoryId : this.queryParam.factoryId,
+        factoryCode: factoryCode,
+        groupId: '',
+        shiftId: '',
+        shiftCode: '',
+        shiftName: '',
+        workOrderDate: '',
+        planQuantity: ''
+      }
+      this.initDictSelectOptions(null).then(() => {
+        // 鏍规嵁鏃ユ湡鑼冨洿鐢熸垚鎺掍骇鏃ユ湡閫夐」
+        if (this.dateRange && this.dateRange.length === 2) {
+          this.workOrderDateOptions = this.generateDateRangeOptions(this.dateRange[0], this.dateRange[1]);
+        }
+        this.dataSource.push(newRow)
+        this.pagination.total += 1
+        // 婵�娲绘柊澧炵殑琛岃繘鍏ョ紪杈戠姸鎬�
+        this.$nextTick(() => {
+          if (this.$refs.table) {
+            this.$refs.table.setActiveRow(newRow)
+          }
+        })
+      })
+    },
+    handleRemove(row) {
+      let table = this.$refs.table
+      this.$confirm({
+        title: '鎻愮ず',
+        content: '纭瑕佸垹闄ゅ悧锛�',
+        okText: '纭畾',
+        cancelText: '鍙栨秷',
+        onOk: () => {
+          const index = this.dataSource.indexOf(row)
+          if (index > -1) {
+            this.dataSource.splice(index, 1)
+            this.pagination.total -= 1
+          }
+          // 鍒犻櫎琛�
+          table.remove(row)
+        }
+      })
+    },
+    onOpenChange(open) {
+      if (!open) {
+        // 鍏抽棴閫夋嫨鍣ㄦ椂閲嶇疆鐘舵��
+        this.tempStartDate = null
+        this.hoveredDate = null
+      }
+    },
+    disabledDate(current) {
+      // 濡傛灉鏈変复鏃跺紑濮嬫棩鏈燂紝鍒欓檺鍒剁粨鏉熸棩鏈熻寖鍥�
+      if (this.tempStartDate) {
+        const startDate = this.tempStartDate.clone().startOf('day')
+        const maxDate = startDate.clone().add(6, 'days').endOf('day') // 7澶╁寘鎷捣濮嬫棩
+        const minDate = startDate.clone().subtract(6, 'days').startOf('day') // 涔熷彲浠ュ悜鍓嶉��6澶�
+        // 绂佺敤瓒呭嚭7澶╄寖鍥寸殑鏃ユ湡
+        return current && (current < minDate || current > maxDate)
+      }
+      // 榛樿涓嶇鐢�
+      return false
+    },
+    dateRangeChange(dates, dateStrings) {
+      this.dateRange = dates
+      if (dates && dates.length > 0) {
+        if (dates.length === 1) {
+          // 閫夋嫨浜嗗紑濮嬫棩鏈燂紝淇濆瓨鍒颁复鏃跺彉閲�
+          this.tempStartDate = dates[0]
+          this.hoveredDate = dates[0]
+        } else if (dates.length === 2) {
+          // 閫夋嫨浜嗙粨鏉熸棩鏈燂紝楠岃瘉鑼冨洿
+          const startDate = dates[0]
+          const endDate = dates[1]
+          const diffDays = endDate.diff(startDate, 'days') + 1
+
+          if (diffDays > 7) {
+            this.$message.warning('鏃ユ湡鑼冨洿涓嶈兘瓒呰繃7澶�')
+            // 鑷姩璋冩暣涓�7澶╄寖鍥�
+            const adjustedEndDate = startDate.clone().add(6, 'days')
+            this.dateRange = [startDate, adjustedEndDate]
+            this.queryParam.startDate = startDate.format('YYYY-MM-DD')
+            this.queryParam.endDate = adjustedEndDate.format('YYYY-MM-DD')
+          } else {
+            this.queryParam.startDate = dateStrings[0]
+            this.queryParam.endDate = dateStrings[1]
+          }
+          // 閲嶇疆涓存椂鐘舵��
+          this.tempStartDate = null
+          this.hoveredDate = null
+        }
+
+        // 鏇存柊鏃ュ巻鏄剧ず涓洪�変腑鏃ユ湡鎵�鍦ㄥ懆
+        if (dates[0]) {
+          this.calendarStartDate = dates[0].clone().startOf('week');
+        }
+      } else {
+        // 娓呴櫎浜嗛�夋嫨
+        this.queryParam.startDate = null
+        this.queryParam.endDate = null
+        this.tempStartDate = null
+        this.hoveredDate = null
+        // 閲嶇疆鏃ュ巻涓哄綋鍓嶅懆
+        this.calendarStartDate = moment().startOf('week');
+      }
+    },
+    // 澶勭悊鏃ユ湡閫夋嫨浜嬩欢 - 鏇存柊鏃ュ巻鏄剧ず涓洪�変腑鏃ユ湡鎵�鍦ㄥ懆
+    onDateSelect(date) {
+      console.log('Selected date:', date.format('YYYY-MM-DD'))
+      // 鏇存柊鏃ュ巻鏄剧ず涓洪�変腑鏃ユ湡鎵�鍦ㄥ懆
+      this.calendarStartDate = date.clone().startOf('week');
+    },
+
+    // 澶勭悊鏃ュ巻鍛ㄥ彉鍖栦簨浠�
+    onCalendarChange(date) {
+      console.log('Calendar week changed:', date.format('YYYY-MM-DD'))
+      // 鏃ュ巻鍛ㄥ彉鍖栨椂鏇存柊 startDate
+      this.calendarStartDate = date.clone().startOf('week');
+    },
+    // 鏇存柊鏃ュ巻涓殑宸ュ崟鏁版嵁
+    updateCalendarWorkOrders() {
+      const workOrdersByDate = {}
+      this.dataSource.forEach(workOrder => {
+        const workOrderDate = workOrder.workOrderDate
+        if (workOrderDate) {
+          if (!workOrdersByDate[workOrderDate]) {
+            workOrdersByDate[workOrderDate] = []
+          }
+          workOrdersByDate[workOrderDate].push({
+            id: workOrder.id,
+            shiftCode: workOrder.shiftCode || '',
+            materialName: workOrder.materialName || '',
+            planQuantity: workOrder.planQuantity || 0
+          })
+        }
+      })
+      this.workOrdersByDate = workOrdersByDate
+    },
+    // 鑾峰彇鎸囧畾鏃ユ湡鐨勫伐鍗�
+    getWorkOrdersForDate(date) {
+      const dateStr = date.format('YYYY-MM-DD')
+      return this.workOrdersByDate[dateStr] || []
+    }
+  }
+}
+</script>
+<style scoped>
+@import '~@assets/less/common.less';
+
+.placeholder-text {
+  font-style: italic;
+  color: #999;
+}
+
+.work-order-item {
+  font-size: 12px;
+  padding: 2px 4px;
+  margin-bottom: 2px;
+  background-color: #f0f0f0;
+  border-radius: 2px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.work-order-item.blue-item {
+  background-color: #e6f7ff;
+  border-left: 2px solid #1890ff;
+}
+
+.work-order-item.red-item {
+  background-color: #fff1f0;
+  border-left: 2px solid #f5222d;
+}
+
+.work-order-shift {
+  font-weight: bold;
+  margin-right: 4px;
+}
+
+.work-order-material {
+  margin-right: 4px;
+}
+
+.work-order-quantity {
+  float: right;
+}
+</style>
\ No newline at end of file

--
Gitblit v1.9.3