cuilei
2 天以前 4dfa438cbd4f3f475c97a2233688d1fe3628099c
生产管控 排产工单页面调整
已添加1个文件
已修改3个文件
934 ■■■■■ 文件已修改
src/views/base/ShiftManager.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/MesProductionWorkOrderListView.vue 84 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/modules/MesProductionWeekCalendar.vue 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mes/modules/MesProductionWorkOrderScheduleModal.vue 793 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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){
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})
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;
}
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>