小小儁爺
2026-01-22 44c5e6a49450e698b49bf8dfe34481ef9618f983
1.递交
已添加1个文件
已重命名1个文件
1079 ■■■■■ 文件已修改
src/views/gantt/甘特图做到显示已排部分.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/views/gantt/目前最新20260122.vue 1079 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/gantt/¸ÊÌØÍ¼×öµ½ÏÔʾÒÑÅŲ¿·Ö.vue
src/views/gantt/Ŀǰ×îÐÂ20260122.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1079 @@
<template>
  <div style="padding: 0 10px">
    <div style="padding: 10px 0;display: flex;">
      <el-select
        v-model="scaleValue"
        size="mini"
        placeholder="请选择"
        @change="val=>changeTimeScale(val,true)"
      >
        <el-option
          v-for="item in scaleArr"
          :key="item.code"
          :label="item.name"
          :value="item.code"
        />
      </el-select>
      <el-select
        v-model="priorityMethod"
        size="mini"
        style="margin-left: 10px;"
        placeholder="请选择"
        @change="priorityMethodChange"
      >
        <el-option
          v-for="item in priorityMethodArr"
          :key="item.code"
          :label="item.name"
          :value="item.code"
        />
      </el-select>
      <el-date-picker
        v-model="ganttDateRange"
        style="margin-left: 10px;"
        size="mini"
        value-format="yyyy-MM-dd"
        type="daterange"
        :clearable="false"
        :picker-options="pickerOptions"
        range-separator="至"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
        @change="ganttDateRangeChange"
      />
      <el-button type="primary" style="margin-left: 10px;" size="mini" @click="handleGetSelected">
        èŽ·å–å¤é€‰æ¡†é€‰ä¸­ä»»åŠ¡
      </el-button>
      <el-button size="mini" @click="handleClearSelection">
        æ¸…空复选框选择
      </el-button>
      <el-button size="mini" type="primary" @click="prepareArrange">
        é¢„排
      </el-button>
      <el-button size="mini" disabled>
        é¢„排进度:{{ canArrangeNumber }}/{{ needArrangeNumber }}
      </el-button>
    </div>
    <div id="gantt_here" style="width:100%; height:calc(90vh - 50px);" />
    <!-- åˆ†é¡µç»„ä»¶ -->
    <div class="pagination-container">
      <el-pagination
        :current-page="currentPage"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="totalTasks"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>
<script>
import { gantt } from '@/components/dhtmlxGantt'
import '@/components/dhtmlxGantt/codebase/dhtmlxgantt.css'
import { handleDateReduceOneDay, handleDatetime, handleDatetime2 } from '@/utils/global'
import { nanoid } from 'nanoid'
export default {
  data() {
    return {
      scaleArr: [
        { code: '30min', name: '30min' },
        { code: '60min', name: '60min' },
        { code: '240min', name: '240min' },
        { code: '360min', name: '360min' }
      ],
      scaleValue: '240min',
      ganttDateRange: ['2026-01-22', '2026-01-25'], // '2026-01-20', '2026-01-25'
      selectedIds: [],
      // åˆ†é¡µç›¸å…³æ•°æ®
      currentPage: 1,
      pageSize: 100,
      totalTasks: 0,
      allTasks: [], // å­˜å‚¨æ‰€æœ‰ä»»åŠ¡æ•°æ®
      paginatedTasks: [], // å½“前页的任务数据
      fivePeriodsTimeName: ['OneStartDate', 'TwoStartDate', 'ThreeStartDate', 'FourStartDate', 'FiveStartDate'], // äº”个时间段的键名
      needArrangeNumber: 5000, // å‡è®¾éœ€è¦æŽ’产数量5000
      canArrangeNumber: 0, // èƒ½æŽ’数量默认为 0
      priorityMethod: 'device', // device è®¾å¤‡   time  æ—¶é—´
      priorityMethodArr: [
        { code: 'device', name: '设备优先' },
        { code: 'time', name: '时间优先' }
      ],
      pickerOptions: {
        disabledDate(time) {
          return time.getTime() <= Date.now() - 24 * 60 * 60 * 1000
        }
      }
    }
  },
  mounted() {
    // åˆå§‹åŒ–甘特图配置
    this.initGantt()
    // æ”¹å˜æ—¥æœŸèŒƒå›´é…ç½®
    this.ganttDateRangeChange(this.ganttDateRange)
    // ç„¶åŽåŠ è½½ä»»åŠ¡æ•°æ®ï¼ˆä¼šè‡ªåŠ¨æ¸²æŸ“å½“å‰é¡µï¼‰
    // this.loadTasks()
  },
  methods: {
    initGantt() {
      gantt.plugins({
        critical_path: true,
        drag_timeline: true,
        grouping: true,
        keyboard_navigation: true,
        marker: true,
        multiselect: true,
        tooltip: true,
        undo: true
      })
      gantt.i18n.setLocale('cn')
      gantt.config.multiselect = true // å¼€å¯å¤šä»»åŠ¡é€‰æ‹©
      gantt.config.show_links = false // ä¸æ˜¾ç¤ºè¿žæŽ¥çº¿
      // ä¸å†å¯¹é½æ—¶é—´è½´åˆ»åº¦ï¼ˆæ¯”如天格子),而是按“小时”对齐
      gantt.config.round_dnd_dates = false
      // æœ€å°æ­¥é•¿è¿˜æ˜¯ 1 å°æ—¶ï¼Œä½†ä½ å·²ç»ä»Žâ€œå¤©æ ¼å­â€å˜æˆâ€œå°æ—¶æ ¼å­â€äº†
      // gantt.config.time_step = 60 // 60 åˆ†é’Ÿ = 1 å°æ—¶
      gantt.config.time_step = 1 //  1分钟
      gantt.config.row_height = 32 // è¡Œé«˜
      gantt.config.bar_height = 20 // bar高
      gantt.config.xml_date = '%Y-%m-%d %H:%i' // gantt的日期格式
      gantt.config.drag_progress = false // ç¦æ­¢é€šè¿‡æ‹–动进度条改变任务进度
      gantt.config.readonly = true // åªè¯»æ¨¡å¼
      // åˆ»åº¦å€¼æ”¹å˜
      this.changeTimeScale()
      // æ˜¯å¦æ˜¯å·¥ä½œæ—¶é—´
      /* â†“↓↓ Working Time configuration â†“↓↓ */
      // gantt.templates.scale_cell_class = function(date) {
      //   if (!gantt.isWorkTime(date)) {
      //     return 'weekend'
      //   }
      // }
      //
      // gantt.templates.timeline_cell_class = function(item, date) {
      //   if (!gantt.isWorkTime({ date: date, task: item })) {
      //     return 'weekend'
      //   }
      // }
      //
      // gantt.config.work_time = true
      //
      // gantt.addCalendar({
      //   id: 'customCalendar1',
      //   worktime: {
      //     hours: ['00:00-24:00'], // global work hours for weekdays
      //     days: [1, 1, 1, 1, 1, 1, 1]
      //   }
      // })
      // gantt.addCalendar({
      //   id: 'custom2',
      //   hours: ['12:00-21:00'],
      //   days: [1, 0, 1, 0, 1, 0, 1]
      // })
      /* â†‘↑↑ Working Time configuration â†‘↑↑ */
      // ç”˜ç‰¹å›¾åˆ—参数设置
      /* â†“↓↓ Grid Columns configuration â†“↓↓ */
      gantt.config.reorder_grid_columns = true
      const textEditor = { type: 'text', map_to: 'text' }
      const startDateEditor = { type: 'date', map_to: 'start_date' }
      const durationEditor = { type: 'number', map_to: 'duration', min: 1, max: 100 }
      gantt.config.columns = [
        {
          name: 'checked',
          label: '选择',
          align: 'center',
          width: 35,
          resize: false,
          // å…³é”®ï¼šç”¨ template è¿”回一个复选框
          template: (task) => {
            const checked = task.checked ? 'checked' : ''
            // data-action ç”¨äºŽåœ¨äº‹ä»¶å§”托时识别是复选框
            return `<input type="checkbox" class="taskCheckBox" data-action="check-row" ${checked} />`
          }
        },
        // { name: 'wbs', label: '节点', width: 80, template: gantt.getWBSCode },
        { name: 'text', tree: true, align: 'center', label: '任务名称', width: 240, resize: true, editor: textEditor },
        { name: 'saleOrder', align: 'center', label: '销售订单', width: 100, resize: true },
        { name: 'partName', align: 'center', label: '产品名称', width: 80, resize: true },
        { name: 'partCode', align: 'center', label: '产品编码', width: 80, resize: true },
        {
          name: 'progress', align: 'center', label: '进度', width: 120, resize: true, template: function(task) {
            return `<input type="range"  onmousedown="event.preventDefault()" onmouseup="event.preventDefault()" id="taskRange" value="${task.progress * 100}"/> ${task.progress * 100}%`
            // return `<el-progress :percentage='${task.progress * 100}'></el-progress> ${task.progress * 100}%`
          }
        },
        { name: 'start_date', align: 'center', label: '开始日期', width: 80, resize: true, editor: startDateEditor },
        {
          name: 'duration',
          width: 60,
          align: 'center',
          label: '时长(分钟)',
          resize: true,
          editor: durationEditor,
          template: function(task) {
            // å¦‚æžœduration是null或undefined,返回0
            // return (task.duration || 0) + 1 // åœ¨å½“前duration的基础上加1
            return task.duration
          }
        }
        // { name: 'add', width: 44 }
      ]
      /* â†‘↑↑ Grid Columns configuration â†‘↑↑ */
      gantt.config.show_errors = false // å‘生异常时,不允许弹出警告到 UI ç•Œé¢
      gantt.config.grid_elastic_columns = true
      // è‡ªå®šä¹‰æµ®åŠ¨æ¡†çš„æ˜¾ç¤ºå†…å®¹   tooltip浮动框显示的End Date被追加1的问题修复(应该显示数据库的原始值)
      gantt.templates.tooltip_text = function(start, end, task) {
        // return '<b>任务:</b> ' + task.text + '<br/><b>开始时间:</b> ' + `${gantt.date.date_to_str('%Y-%m-%d')(start)}` + '<br/><b>结束时间:</b> ' + handleDateReduceOneDay(end)
        // return '<b>任务:</b> ' + task.text + '<br/><b>开始时间:</b> ' + handleDatetime2(start) + '<br/><b>结束时间:</b> ' + handleDateReduceOneDay(end) + '<br/><b>进度:</b> ' + task.progress * 100 + '%'
        return '<b>任务:</b> ' + task.text +
          '<br/><b>' + `${task.type === 'task' ? '产能' : '生产数量'}` + ' :</b> ' + task.producedCount +
          '<br/><b>进度:</b> ' + task.progress * 100 + '%' +
          '<br/><b>开始时间:</b> ' + handleDatetime2(start) +
          '<br/><b>结束时间:</b> ' + handleDatetime2(end)
      }
      gantt.templates.task_text = function(start, end, task) {
        // return '<span style="color: white; font-weight: bold; text-shadow: 1px 1px 1px #000;">' +
        //   task.description + ' - ' +
        //   '</span>'
        // return task.description
        // return task.progress * 100 + '%'
        if (task.type === 'task2') {
          return `<div  class="task2Css">${task.producedCount}</div>`
          // return task.producedCount
        }
        if (task.type === 'task3') {
          return `<div  class="task3Css">${task.producedCount}</div>`
        }
        return ''
      }
      // è®¾ç½®æŒç»­æ—¶é—´å•位为小时
      // gantt.config.duration_unit = 'hour'
      gantt.config.duration_unit = 'minute'
      gantt.config.duration_step = 1
      // gantt.config.show_task_cells = false  //隐藏甘特图内部刻度线
      gantt.init('gantt_here')
      // æ³¨æ„ï¼šè¿™é‡Œä¸ç«‹å³åŠ è½½æ•°æ®ï¼Œè€Œæ˜¯ç­‰å¾…loadTasks被调用
      // ç»‘定甘特图点击事件(官方推荐的事件委托用法)
      gantt.attachEvent('onTaskClick', (id, e) => {
        // æ‰¾åˆ°ç‚¹å‡»çš„æ˜¯å¦æ˜¯å¤é€‰æ¡†
        const checkbox = e.target.closest('[data-action="check-row"]')
        if (!checkbox) {
          // ä¸æ˜¯ç‚¹å¤é€‰æ¡†ï¼Œå°±ä¿æŒé»˜è®¤è¡Œä¸º
          return true
        }
        // æ˜¯å¤é€‰æ¡†ï¼šåˆ‡æ¢é€‰ä¸­çŠ¶æ€
        const task = gantt.getTask(id)
        if (task) {
          task.checked = !task.checked
          // åŒæ—¶æ›´æ–°å…¨å±€æ•°æ®
          const globalTask = this.allTasks.find(t => t.id === id)
          if (globalTask) {
            globalTask.checked = task.checked
          }
          gantt.updateTask(id) // åªåˆ·æ–°è¿™ä¸€è¡Œï¼Œæ€§èƒ½æ›´å¥½
          this.syncSelected() // åŒæ­¥åˆ° Vue çš„ selectedIds
        }
        // é˜»æ­¢é»˜è®¤ç‚¹å‡»è¡Œè¡Œä¸ºï¼ˆé¿å…è¯¯è§¦å‘其他逻辑)
        return false
      })
      // åˆå§‹åŒ–完成后同步一次选中状态
      this.syncSelected()
      // this.loadTasks()
    },
    // åŠ è½½ä»»åŠ¡æ•°æ®
    loadTasks() {
      // æŽ¥å£èŽ·å–åˆ°çš„æ•°æ®  //这是待排数据
      const rows = [
        {
          'wo_code': null,
          'YearDate': '2026-01-20',
          'children': [
            {
              'AdvaDevicNumber': 'JG010',
              'AdvaDevicName': '精工设备10#',
              'AdvaDevicCropMob': '10',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            },
            {
              'AdvaDevicNumber': 'SB001',
              'AdvaDevicName': '设备001',
              'AdvaDevicCropMob': '30',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            }
          ]
        },
        {
          'wo_code': null,
          'YearDate': '2026-01-21',
          'children': [
            {
              'AdvaDevicNumber': 'JG010',
              'AdvaDevicName': '精工设备10#',
              'AdvaDevicCropMob': '10',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            },
            {
              'AdvaDevicNumber': 'SB001',
              'AdvaDevicName': '设备001',
              'AdvaDevicCropMob': '30',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            }
          ]
        },
        {
          'wo_code': null,
          'YearDate': '2026-01-22',
          'children': [
            {
              'AdvaDevicNumber': 'JG010',
              'AdvaDevicName': '精工设备10#',
              'AdvaDevicCropMob': '10',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            },
            {
              'AdvaDevicNumber': 'SB001',
              'AdvaDevicName': '设备001',
              'AdvaDevicCropMob': '30',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            }
          ]
        },
        {
          'wo_code': null,
          'YearDate': '2026-01-23',
          'children': [
            {
              'AdvaDevicNumber': 'JG010',
              'AdvaDevicName': '精工设备10#',
              'AdvaDevicCropMob': '10',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            },
            {
              'AdvaDevicNumber': 'SB001',
              'AdvaDevicName': '设备001',
              'AdvaDevicCropMob': '30',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            }
          ]
        },
        {
          'wo_code': null,
          'YearDate': '2026-01-24',
          'children': [
            {
              'AdvaDevicNumber': 'JG010',
              'AdvaDevicName': '精工设备10#',
              'AdvaDevicCropMob': '10',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            },
            {
              'AdvaDevicNumber': 'SB001',
              'AdvaDevicName': '设备001',
              'AdvaDevicCropMob': '30',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            }
          ]
        },
        {
          'wo_code': null,
          'YearDate': '2026-01-25',
          'children': [
            {
              'AdvaDevicNumber': 'JG010',
              'AdvaDevicName': '精工设备10#',
              'AdvaDevicCropMob': '10',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            },
            {
              'AdvaDevicNumber': 'SB001',
              'AdvaDevicName': '设备001',
              'AdvaDevicCropMob': '30', // ç¨¼åŠ¨çŽ‡    éœ€è¦é™¤100
              'AdvaDevicRhythm': '5.0', // ç”Ÿäº§èŠ‚æ‹
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            }
          ]
        }
      ]
      // è¿™æ˜¯å·²æŽ’数据
      const Cont = [
        {
          'wo_code': 'MO-2023-06-0007_1',
          'eqp_code': 'JG010',
          'time_start': '2026-01-21 13:51:55',
          'time_end': '2026-01-21 18:00:00',
          'status': 'S',
          'alloc_qty': 298.00,
          'part_code': '302',
          'part_name': '8504光机',
          'uom_name': '只'
        },
        {
          'wo_code': 'MO-2023-06-0007_1',
          'eqp_code': 'JG010',
          'time_start': '2026-01-22 08:00:00',
          'time_end': '2026-01-22 11:30:00',
          'status': 'S',
          'alloc_qty': 252.00,
          'part_code': '302',
          'part_name': '8504光机',
          'uom_name': '只'
        },
        {
          'wo_code': 'MO-2023-06-0007_1',
          'eqp_code': 'JG010',
          'time_start': '2026-01-22 13:00:00',
          'time_end': '2026-01-22 18:00:00',
          'status': 'S',
          'alloc_qty': 360,
          'part_code': '302',
          'part_name': '8504光机',
          'uom_name': '只'
        },
        {
          'wo_code': 'MO-2023-06-0007_1',
          'eqp_code': 'JG010',
          'time_start': '2026-01-23 08:00:00',
          'time_end': '2026-01-23 11:30:00',
          'status': 'S',
          'alloc_qty': 252.00,
          'part_code': '302',
          'part_name': '8504光机',
          'uom_name': '只'
        },
        {
          'wo_code': 'MO-2023-06-0007_1',
          'eqp_code': 'JG010',
          'time_start': '2026-01-23 13:00:00',
          'time_end': '2026-01-23 15:00:00',
          'status': 'S',
          'alloc_qty': 144.00,
          'part_code': '302',
          'part_name': '8504光机',
          'uom_name': '只'
        }
        // {
        //   'wo_code': 'MO-2023-06-0007_1',
        //   'eqp_code': 'JG010',
        //   'time_start': '2026-01-24 08:00:00',
        //   'time_end': '2026-01-24 11:30:00',
        //   'status': 'S',
        //   'alloc_qty': 252.00,
        //   'part_code': '302',
        //   'part_name': '8504光机',
        //   'uom_name': '只'
        // },
        // {
        //   'wo_code': 'MO-2023-06-0007_1',
        //   'eqp_code': 'JG010',
        //   'time_start': '2026-01-24 13:00:00',
        //   'time_end': '2026-01-24 18:00:00',
        //   'status': 'S',
        //   'alloc_qty': 360.00,
        //   'part_code': '302',
        //   'part_name': '8504光机',
        //   'uom_name': '只'
        // },
        // {
        //   'wo_code': 'MO-2023-06-0007_1',
        //   'eqp_code': 'JG010',
        //   'time_start': '2026-01-25 08:00:00',
        //   'time_end': '2026-01-25 11:30:00',
        //   'status': 'S',
        //   'alloc_qty': 252.00,
        //   'part_code': '302',
        //   'part_name': '8504光机',
        //   'uom_name': '只'
        // },
        // {
        //   'wo_code': 'MO-2023-06-0007_1',
        //   'eqp_code': 'JG010',
        //   'time_start': '2026-01-25 13:00:00',
        //   'time_end': '2026-01-25 18:00:00',
        //   'status': 'S',
        //   'alloc_qty': 360.00,
        //   'part_code': '302',
        //   'part_name': '8504光机',
        //   'uom_name': '只'
        // }
      ]
      const newArr = []
      // è¿™ä¸€æ­¥çš„æ“ä½œä¸»è¦æ˜¯è¦åšäº§èƒ½èƒŒæ™¯çš„æ˜¾ç¤º
      rows.forEach((item, index) => {
        // æ•°æ®æŽ¥å£è¿”回的时间范围要在日期选择范围之内
        if (new Date(item.YearDate).getTime() >= new Date(this.ganttDateRange[0]).getTime() && new Date(item.YearDate).getTime() <= new Date(this.ganttDateRange[1]).getTime()) {
          item.children.forEach((it, ind) => {
            // è¿™é‡Œåº”该要生成一个以设备维度为基础的数组   ä¸é‡ä¸æ¼
            if (!newArr.map(i => i.partCode).includes(it.AdvaDevicNumber)) {
              console.log(' it.AdvaDevicNumber,', it.AdvaDevicNumber)
              newArr.push({
                id: it.AdvaDevicNumber,
                type: 'project',
                text: '任务名称预留',
                partName: it.AdvaDevicName,
                partCode: it.AdvaDevicNumber,
                start_date: handleDatetime2(item.YearDate + ' ' + it.OneStartDate.split('~')[0]), // è¿™ä¸ªæ˜¯æ— æ•ˆçš„,只是为了预排prepareArrange方法里面不报错
                end_date: handleDatetime2(item.YearDate + ' ' + it.OneStartDate.split('~')[1]), // è¿™ä¸ªæ˜¯æ— æ•ˆçš„,只是为了预排prepareArrange方法里面不报错
                // duration: this.calculateTimeRangeInMinutes(it.OneStartDate),
                render: 'split', // ç”¨äºŽåœ¨ä¸€ä¸ªå·¥ä½œæ—¶é—´æ®µå†…显示不下,需要进行分割显示
                checked: false,
                progress: 0,
                parent: 0,
                saleOrder: 'SO-2026-01001'
              })
            }
            // å› ä¸ºæ˜¯äº”个时间段,所有要有个循环次数为5的循环
            for (let i = 0; i < 5; i++) { // è¿™æ¬¡å¾ªçŽ¯æ˜¯ä¸ºäº†æ˜¾ç¤ºäº§èƒ½
              if (it[this.fivePeriodsTimeName[i]]) {
                const duration = this.calculateTimeRangeInMinutes(it[this.fivePeriodsTimeName[i]]) // å·¥æœŸ å•位 åˆ†é’Ÿ
                newArr.push({
                  // id:  index.toString() + ind.toString() + i.toString(),
                  id: nanoid(),
                  type: 'task',
                  text: '任务名称预留',
                  partName: it.AdvaDevicName,
                  partCode: it.AdvaDevicNumber,
                  start_date: handleDatetime2(item.YearDate + ' ' + it[this.fivePeriodsTimeName[i]].split('~')[0]),
                  // start_date: new Date(handleDatetime2(item.YearDate + ' ' + it[this.fivePeriodsTimeName[i]].split('~')[0])).getTime() < new Date().getTime() ? handleDatetime2(new Date()) : handleDatetime2(item.YearDate + ' ' + it[this.fivePeriodsTimeName[i]].split('~')[0]),
                  // end_date: new Date(handleDatetime2(item.YearDate + ' ' + it[this.fivePeriodsTimeName[i]].split('~')[1])).getTime() < new Date().getTime() ? handleDatetime2(new Date()) : handleDatetime2(item.YearDate + ' ' + it[this.fivePeriodsTimeName[i]].split('~')[1]),
                  end_date: handleDatetime2(item.YearDate + ' ' + it[this.fivePeriodsTimeName[i]].split('~')[1]),
                  duration,
                  checked: false,
                  progress: 0,
                  parent: it.AdvaDevicNumber,
                  saleOrder: 'SO-2026-01001',
                  //  è¦åœ¨æ¯ä¸€ä¸ªæ—¶é—´æ®µå†…算出能生产多少个     å·¥æœŸï¼ˆåˆ†é’Ÿï¼‰ä¹˜ä»¥60 é™¤ä»¥ç”Ÿäº§èŠ‚æ‹ * ç¨¼åŠ¨çŽ‡
                  producedCount: (duration * 60 / it.AdvaDevicRhythm) * (it.AdvaDevicCropMob / 100),
                  AdvaDevicRhythm: it.AdvaDevicRhythm, // ç”Ÿäº§èŠ‚æ‹
                  AdvaDevicCropMob: it.AdvaDevicCropMob // ç¨¼åŠ¨çŽ‡    éœ€è¦é™¤100
                })
              }
            }
          })
        }
      })
      // è¿™ä¸€æ­¥çš„æ“ä½œæ˜¯åšå·²æŽ’的显示
      const scheduledDevices = [...new Set(Cont.map(i => i.eqp_code))]// è¿™æ˜¯å·²æŽ’的设备编码
      Cont.forEach(item => {
        if (scheduledDevices.includes(item.eqp_code)) {
          newArr.push({
            id: nanoid(),
            type: 'task3',
            text: '任务名称预留',
            partName: item.part_name,
            partCode: item.part_code,
            start_date: item.time_start,
            end_date: item.time_end,
            // duration: this.calculateTimeRangeInMinutes(it.OneStartDate),
            duration: this.calculateTimeRangeInMinutes(item.time_start.split(' ')[1] + '~' + item.time_end.split(' ')[1]),
            checked: false,
            progress: 0,
            parent: item.eqp_code,
            saleOrder: 'SO-2026-01001',
            producedCount: item.alloc_qty
          })
        }
      })
      // task ä»£è¡¨çš„æ˜¯äº§èƒ½  task2 ä»£è¡¨çš„æ˜¯å¯ä»¥æŽ’产的值   task3 ä»£è¡¨çš„æ˜¯å·²æŽ’产的值
      // task2 çš„值得从 task减去task3的时间  ä»£è¡¨å¯æŽ’产时间
      // è‹¥åŒä¸€çˆ¶èŠ‚ç‚¹çš„å€¼ç›¸åŒæ—¶,当task的开始时间和结束时间与task3相等时,代表此段不能再排产
      // å½“task的开始时间等于task3的开始时间,但task的结束时间大于task3的结束时间时,这时候,可排产时间为:task3的结束时间到task的结束时间
      const task = newArr.filter(item => item.type === 'task')
      const task3 = newArr.filter(item => item.type === 'task3')
      task.forEach(item => { // æ€»äº§èƒ½
        task3.forEach(it => { // å·²æŽ’数据
          if (item.parent === it.parent) { // è¯´æ˜Žæ˜¯åœ¨åŒä¸€ä¸ªè®¾å¤‡ä¸‹
            // å½“两个时间相等时说明肯定不能排产了
            if (new Date(item.start_date).getTime() === new Date(it.start_date).getTime() && new Date(item.end_date).getTime() === new Date(it.end_date).getTime()) {
              item.schedulingPossible = false
            }
            if (new Date(item.start_date).getTime() === new Date(it.start_date).getTime() && new Date(item.end_date).getTime() > new Date(it.end_date).getTime()) {
              item.start_date2 = it.end_date
            }
            // ä¸çŸ¥é“要不要注释掉  å¾…验证
            // if (new Date(item.start_date).getTime() < new Date().getTime() && item.producedCount !== it.producedCount) {
            //   item.start_date2 = handleDatetime2(new Date())
            // }
          }
        })
      })
      // ä½¿ç”¨åŽŸæœ‰çš„ç¤ºä¾‹æ•°æ®ä½œä¸ºåŸºç¡€
      this.allTasks = newArr.filter(i => i.schedulingPossible !== false)
      // this.allTasks = newArr
      this.totalTasks = this.allTasks.length
      this.updatePaginatedTasks()
      this.renderGanttChart()
      // this.prepareArrange()
    },
    // æ›´æ–°åˆ†é¡µåŽçš„任务数据
    updatePaginatedTasks() {
      const startIndex = (this.currentPage - 1) * this.pageSize
      const endIndex = Math.min(startIndex + this.pageSize, this.allTasks.length)
      this.paginatedTasks = this.allTasks.slice(startIndex, endIndex)
      // this.paginatedTasks = JSON.parse(JSON.stringify(this.allTasks.slice(startIndex, endIndex)))
    },
    // æ‹†åˆ†æ—¶é—´å­—符串并分别计算分钟值
    calculateTimeRangeInMinutes(timeRangeStr) {
      // åˆ†å‰²å­—符串,获取开始时间和结束时间
      const [startTimeStr, endTimeStr] = timeRangeStr.split('~')
      // å°†æ—¶é—´å­—符串(HH:MM)转换为总分钟数
      const timeStringToMinutes = (timeStr) => {
        const [hours, minutes] = timeStr.split(':').map(Number)
        return hours * 60 + (minutes || 0)
      }
      const startMinutes = timeStringToMinutes(startTimeStr)
      const endMinutes = timeStringToMinutes(endTimeStr)
      // è¿”回时间差(分钟)
      return endMinutes - startMinutes
    },
    // åˆ»åº¦å€¼æ”¹å˜
    changeTimeScale(val, boolean) {
      let scaleConfig
      switch (this.scaleValue) {
        case '30min':
          scaleConfig = [
            { unit: 'day', step: 1, format: '%Y-%m-%d æ˜ŸæœŸ%D' },
            { unit: 'minute', step: 30, format: '%H:%i' } // å­å°ºåº¦
          ]
          break
        case '60min':
          scaleConfig = [
            { unit: 'day', step: 1, format: '%Y-%m-%d æ˜ŸæœŸ%D' },
            // { unit: 'hour', step: 1, format: '%H:%i' },
            { unit: 'minute', step: 60, format: '%H:%i' }
          ]
          break
        case '240min':
          scaleConfig = [
            { unit: 'day', step: 1, format: '%Y-%m-%d æ˜ŸæœŸ%D' },
            { unit: 'minute', step: 240, format: '%H:%i' } // æ¯4小时一个刻度
          ]
          break
        case '360min':
          scaleConfig = [
            { unit: 'day', step: 1, format: '%Y-%m-%d æ˜ŸæœŸ%D' },
            { unit: 'minute', step: 360, format: '%H:%i' } // æ¯6小时一个刻度
            // { unit: 'hour', step: 6, format: '%H:%i' } // æ¯6小时一个刻度
          ]
          break
        default:
          scaleConfig = [
            { unit: 'hour', step: 1, format: '%Y-%m-%d æ˜ŸæœŸ%D' },
            { unit: 'minute', step: 60, format: '%H:%i' }
          ]
      }
      gantt.config.start_date = new Date(this.ganttDateRange[0] + ' 00:00')
      gantt.config.end_date = new Date(this.ganttDateRange[1] + ' 24:00')
      gantt.config.scales = scaleConfig
      if (boolean) {
        // gantt.render()// gantt重绘
        this.renderGanttChart()
      }
    },
    // æ¸²æŸ“甘特图
    renderGanttChart() {
      gantt.clearAll()
      console.log(JSON.parse(JSON.stringify(this.paginatedTasks)))
      gantt.parse({
        'data': this.paginatedTasks
      })
      // ç¡®ä¿ç”˜ç‰¹å›¾é‡æ–°æ¸²æŸ“
      // gantt.render()
    },
    // é¡µå¤§å°æ”¹å˜
    handleSizeChange(newSize) {
      console.log('执行2')
      this.pageSize = newSize
      this.currentPage = 1 // é‡ç½®åˆ°ç¬¬ä¸€é¡µ
      this.updatePaginatedTasks()
      this.renderGanttChart()
      this.syncSelected()
    },
    // å½“前页改变
    handleCurrentChange(newPage) {
      console.log('执行1')
      // è®¡ç®—最大页数,防止超出范围
      const maxPage = Math.ceil(this.totalTasks / this.pageSize)
      if (newPage > maxPage) {
        this.currentPage = maxPage
      } else if (newPage < 1) {
        this.currentPage = 1
      } else {
        this.currentPage = newPage
      }
      this.updatePaginatedTasks()
      this.renderGanttChart()
      this.syncSelected()
    },
    // ç”˜ç‰¹å›¾æ—¥æœŸæ”¹å˜
    ganttDateRangeChange(val) {
      this.priorityMethodChange()// æ¸…空已排值
      gantt.config.start_date = new Date(val[0] + ' 00:00')
      gantt.config.end_date = new Date(val[1] + ' 24:00')
      this.loadTasks()
      // gantt.render()
    },
    // ä»Žç”˜ç‰¹å›¾ä¸­åŒæ­¥é€‰ä¸­çš„ id åˆ° Vue data
    syncSelected() {
      // åŒæ­¥å½“前页面任务到全局数据
      gantt.eachTask((task) => {
        const globalTask = this.allTasks.find(t => t.id === task.id)
        if (globalTask) {
          globalTask.checked = task.checked
        }
      })
      // èŽ·å–æ‰€æœ‰é€‰ä¸­çš„ä»»åŠ¡ID
      // this.selectedIds = [...new Set(this.allTasks.filter(t => t.checked).map(t => t.id))]//数组去重
      this.selectedIds = this.allTasks.filter(t => t.checked).map(t => t.id)
      console.log(this.selectedIds)
    },
    // èŽ·å–é€‰ä¸­ä»»åŠ¡ï¼ˆç¤ºä¾‹ï¼‰
    handleGetSelected() {
      const selected = this.allTasks.filter(t => t.checked)
      // this.$notify.success(`当前已选中${selected.length} æ¡ä»»åŠ¡`)
      this.$notify.success(`点击了`)
    },
    // æ¸…空所有选择
    handleClearSelection() {
      // éåŽ†æ‰€æœ‰ä»»åŠ¡ï¼Œå°† checked å±žæ€§è®¾ç½®ä¸º false
      this.allTasks.forEach(task => {
        task.checked = false
      })
      // æ›´æ–°å½“前页面显示的任务
      gantt.eachTask((task) => {
        task.checked = false
        gantt.updateTask(task.id)
      })
      // åŒæ­¥åˆ° Vue ç»„件数据
      this.syncSelected()
      // æ˜¾ç¤ºæç¤ºä¿¡æ¯
      this.$notify.success('已清空所有选择')
    },
    // é¢„排
    prepareArrange() {
      this.priorityMethodChange()
      this.loadTasks()
      // ä¼˜å…ˆæ–¹å¼  time  device
      if (this.priorityMethod === 'time') {
        this.allTasks.sort((a, b) => a.start_date - b.start_date)
      }
      if (this.priorityMethod === 'device') {
        this.allTasks.sort((a, b) => parseFloat(a.AdvaDevicCropMob) - parseFloat(b.AdvaDevicCropMob))
      }
      // åœ¨è¿™ä¸ªå¾ªçŽ¯é‡Œé¢è¿˜å¾—è€ƒè™‘ä¸€ä¸ªç‚¹ï¼Œåœ¨å·²æŽ’çš„æ•°æ®ä¸Šä¸èƒ½å†æŽ’äº†
      // ç›¸å½“于在task å’Œtask3中   è¦åœ¨task里面剔除掉task3的时间段
      const newArr = []
      // this.canArrangeNumber = 0
      let needArrangeNumber = this.needArrangeNumber
      this.allTasks.forEach(item => {
        // if (item.type === 'task') { // è¿™é‡Œçš„判断条件还得加个日期判断
        if (item.type === 'task' &&
          new Date(item.start_date).getTime() >= new Date(this.ganttDateRange[0] + ' 00:00:00').getTime() &&
          new Date(item.end_date).getTime() >= new Date().getTime()) { // è¿™é‡Œçš„判断条件还得加个日期判断 ç»“束时间要大于目前时间
          let ratio = 1 // é»˜è®¤ç³»æ•° 1
          if (item.start_date2) {
            const d = this.calculateTimeRangeInMinutes(item.start_date2.split(' ')[1] + '~' + handleDatetime2(item.end_date).split(' ')[1])
            ratio = Math.round((d / item.duration) * 100) / 100
          }
          if (
            new Date(item.start_date).getTime() < new Date().getTime() &&
            new Date(item.end_date).getTime() >= new Date().getTime()
          ) {
            const d = this.calculateTimeRangeInMinutes(handleDatetime2(new Date()).split(' ')[1] + '~' + handleDatetime2(item.end_date).split(' ')[1])
            ratio = Math.round((d / item.duration) * 100) / 100
          }
          // è¿™ä¸ªåœ°æ–¹çš„count值 å¾—变更  item.producedCount  å¾—乘以个系数  é»˜è®¤ç³»æ•° 1
          const count = needArrangeNumber > 0 && needArrangeNumber <= item.producedCount * ratio ? needArrangeNumber : item.producedCount * ratio
          needArrangeNumber = needArrangeNumber - item.producedCount * ratio // å‰©ä½™å¾…排值
          if (count > 0 && (needArrangeNumber > 0 || Math.abs(needArrangeNumber) < item.producedCount * ratio)) { // ä¸€å®šæ˜¯å¤§äºŽé›¶ä¸”小于整条的生产值的
            // duration   å•位 åˆ†é’Ÿ
            const duration = (count / (item.AdvaDevicCropMob / 100)) * item.AdvaDevicRhythm / 60
            // if (count < item.producedCount && new Date().getTime() <= new Date(item.start_date).getTime()) {
            // duration = duration * (count / item.producedCount)   //好像注释掉就对了  å¾…验证
            // }
            const obj = {
              id: nanoid(),
              type: 'task2',
              text: '任务名称111',
              partName: item.partName,
              partCode: item.partCode,
              start_date: new Date(item.start_date).getTime() < new Date().getTime() ? handleDatetime2(new Date()) : (item.start_date2 ? item.start_date2 : handleDatetime2(item.start_date)),
              // end_date: handleDatetime2(item.end_date),
              // end_date: item.end_date,
              duration, // ä»£è¡¨çš„æ˜¯è¿›åº¦æ¡
              checked: false,
              progress: 0,
              parent: item.parent,
              saleOrder: item.saleOrder,
              producedCount: count <= item.producedCount ? Math.round(count) : item.producedCount
              // producedCount: count <= item.producedCount ? count : item.producedCount
            }
            this.canArrangeNumber += parseFloat(obj.producedCount)
            if (Math.abs(this.canArrangeNumber - this.needArrangeNumber) === 1) {
              obj.producedCount = obj.producedCount + (this.needArrangeNumber - this.canArrangeNumber)
            }
            newArr.push({ ...obj })
          }
        }
      })
      // for (let i = 0; i < this.allTasks.length; i++) {
      //
      // }
      this.allTasks = [...this.allTasks, ...newArr]
      console.log(JSON.parse(JSON.stringify(this.allTasks)), '888')
      this.totalTasks = this.allTasks.length
      this.updatePaginatedTasks()
      this.renderGanttChart()
    },
    // æ¸…空已排值
    priorityMethodChange() {
      this.canArrangeNumber = 0
      this.allTasks = this.allTasks.filter(i => i.type !== 'task2')
      this.totalTasks = this.allTasks.length
      this.updatePaginatedTasks()
      this.renderGanttChart()
    }
  }
}
</script>
<style>
body,
html {
  width: 100%;
  height: 100%;
  margin: unset;
}
/*.gantt_task_cell {*/
/*  background: rgba(5, 185, 100, .1);*/
/*}*/
/*!*非工作日*!*/
/*.weekend {*/
/*  background: rgba(255, 255, 255, 0.1);*/
/*}*/
/*.row-completed {*/
/*  background-color: #bee4be !important; !* æµ…绿色 *!*/
/*}*/
/*.gantt_grid_head_cell[data-column-id="duration"],*/
/*.gantt_row .gantt_cell[data-column-name="duration"] {*/
/*  background-color: #f5f5f5 !important;*/
/*}*/
/* ä¸ºä»»åŠ¡æ¡å†…éƒ¨çš„è¿›åº¦æ¡æ·»åŠ åœ†è§’ï¼Œä¿æŒè§†è§‰ç»Ÿä¸€ */
/*.gantt_task_progress {*/
/*border-radius: 10px !important; !* å»ºè®®ä¸Žä»»åŠ¡æ¡åŠå¾„ä¸€è‡´ *!*/
/*}*/
/*.gantt_bar_task {*/
/*border-radius: 10px !important;*/
/*padding: 3px !important;*/
/*transform: scaleX(-1) !important;*/
/*}*/
/*.gantt_bar_project {*/
/*border-radius: 10px !important;*/
/*}*/
.taskCheckBox {
  cursor: pointer;
}
/* åˆ†é¡µå®¹å™¨æ ·å¼ */
.pagination-container {
  margin-top: 10px;
  display: flex;
  justify-content: end;
  padding: 10px 0;
  background-color: #fff;
  border-top: 1px solid #ebeef5;
}
#taskRange {
  width: 80px;
  height: 6px;
  border-radius: 2px;
}
/* éšè—æ»‘块 */
#taskRange::-webkit-slider-thumb {
  opacity: 0;
  -webkit-appearance: none !important;
  width: 1px !important;
  height: 1px !important;
}
#taskRange::-moz-range-thumb {
  opacity: 0;
  width: 1px !important;
  height: 1px !important;
}
#taskRange::-ms-thumb {
  opacity: 0;
  width: 1px !important;
  height: 1px !important;
}
.task2Css {
  width: 100%;
  height: 100%;
  margin-left: 0;
  background-color: #ac96ff;
}
.task3Css {
  width: 100%;
  height: 100%;
  margin-left: 0;
  background-color: rgb(255, 145, 0);
}
.gantt_scale_cell {
  border: none !important;
}
.gantt_task_scale {
  margin-left: -35px;
}
.gantt_scale_line:last-child {
  width: 105%;
}
/*.gantt_task_bg:first-child > .gantt_task_row:first-child > .gantt_last_cell {*/
/*  border-top: 1px solid rgb(166, 166, 166);*/
/*}*/
</style>