小小儁爺
2026-01-20 798ff0bede63be7b7645d92acec5b449a0376c73
src/views/gantt/index.vue
@@ -2,10 +2,19 @@
  <div style="padding: 0 10px">
    <div style="padding: 10px 0;display: flex;">
      <el-button type="primary" size="mini" @click="ganttUndo">回退拖动操作</el-button>
      <el-button type="primary" size="mini" @click="ganttRedo">前进拖动操作</el-button>
      <el-button type="primary" size="mini" @click="ganttZoomIn">放大</el-button>
      <el-button type="primary" size="mini" @click="ganttZoomOut">缩小</el-button>
      <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-date-picker
        v-model="ganttDateRange"
        style="margin-left: 10px;"
@@ -24,6 +33,7 @@
      <el-button size="mini" @click="handleClearSelection">
        清空复选框选择
      </el-button>
    </div>
    <div id="gantt_here" style="width:100%; height:calc(90vh - 50px);" />
@@ -51,15 +61,23 @@
export default {
  data() {
    return {
      value: 'default',
      ganttDateRange: ['2025-04-01', '2025-05-10'],
      scaleArr: [
        { code: '30min', name: '30min' },
        { code: '60min', name: '60min' },
        { code: '240min', name: '240min' },
        { code: '360min', name: '360min' }
      ],
      scaleValue: '240min',
      ganttDateRange: ['2026-01-20', '2026-01-25'],
      selectedIds: [],
      // 分页相关数据
      currentPage: 1,
      pageSize: 10,
      pageSize: 100,
      totalTasks: 0,
      allTasks: [], // 存储所有任务数据
      paginatedTasks: [] // 当前页的任务数据
      paginatedTasks: [], // 当前页的任务数据
      fivePeriodsTimeName: ['OneStartDate', 'TwoStartDate', 'ThreeStartDate', 'FourStartDate', 'FiveStartDate']// 五个时间段的键名
    }
  },
  mounted() {
@@ -93,7 +111,8 @@
      // 不再对齐时间轴刻度(比如天格子),而是按“小时”对齐
      gantt.config.round_dnd_dates = false
      // 最小步长还是 1 小时,但你已经从“天格子”变成“小时格子”了
      gantt.config.time_step = 60 // 60 分钟 = 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高
@@ -102,7 +121,7 @@
      gantt.config.drag_progress = false // 禁止通过拖动进度条改变任务进度
      // gantt.config.readonly = true  //只读模式
      gantt.config.readonly = true // 只读模式
      /* ↓↓↓ Group configuration ↓↓↓ */
      gantt.serverList('task_priority', [
@@ -129,115 +148,30 @@
      /* ↑↑↑ Group configuration ↑↑↑ */
      // 放大缩小属性
      /* ↓↓↓ Zoom configuration ↓↓↓ */
      const zoomConfig = {
        levels: [
          {
            name: 'hour',
            scale_height: 27,
            min_column_width: 50,
            scales: [
              { unit: 'day', format: '%Y年%M%d号' },
              { unit: 'hour', format: '%H时' }
            ]
          },
          {
            name: 'day',
            scale_height: 27,
            min_column_width: 80,
            scales: [
              // { unit: 'day', step: 1, format: '%d %M' }
              { unit: 'day', step: 1, format: '%M%d号' }
            ]
          },
          {
            name: 'week',
            scale_height: 50,
            min_column_width: 70,
            scales: [
              // {
              //   unit: 'week', step: 1, format: function(date) {
              //     const dateToStr = gantt.date.date_to_str('%d %M')
              //     const endDate = gantt.date.add(date, -6, 'day')
              //     const weekNum = gantt.date.date_to_str('%W')(date)
              //     return '第' + weekNum + '周, ' + dateToStr(date) + ' - ' + dateToStr(endDate)
              //   }
              // },
              // %M
              { unit: 'week', format: '%Y年第%W周' },
              { unit: 'day', step: 1, format: '%M%d号' }
              // { unit: 'minute', step: 60, format: '%H:%i' }
              // { unit: 'day', step: 1, format: '%j %D' }
              // { unit: 'day', step: 1, format: '星期%D' }
            ]
          },
          {
            name: 'month',
            scale_height: 50,
            min_column_width: 120,
            scales: [
              // { unit: 'month', format: '%Y年%F' },
              { unit: 'month', format: '%Y年%M' },
              { unit: 'week', format: '第%W周' }
            ]
          },
          {
            name: 'quarter',
            height: 50,
            min_column_width: 90,
            scales: [
              // {
              //   unit: 'quarter', step: 1, format: function(date) {
              //     const dateToStr = gantt.date.date_to_str('%M')
              //     const endDate = gantt.date.add(gantt.date.add(date, 3, 'month'), -1, 'day')
              //     return dateToStr(date) + ' - ' + dateToStr(endDate)
              //   }
              // },
              { unit: 'month', step: 1, format: '%Y年%M' }
            ]
          },
          {
            name: 'year',
            scale_height: 50,
            min_column_width: 30,
            scales: [
              { unit: 'year', step: 1, format: '%Y年' }
            ]
          }
        ],
        useKey: 'ctrlKey',
        trigger: 'wheel',
        element: function() {
          return gantt.$root.querySelector('.gantt_task')
        }
      }
      gantt.ext.zoom.init(zoomConfig)
      gantt.ext.zoom.setLevel('week')
      /* ↑↑↑ Zoom configuration ↑↑↑ */
      // 刻度值改变
      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.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: 'custom1',
      //   id: 'customCalendar1',
      //   worktime: {
      //     hours: ['8:00-12:30', '13:00-17:30'], // global work hours for weekdays
      //     days: [0, 1, 1, 1, 1, 1, 1]
      //     hours: ['00:00-24:00'], // global work hours for weekdays
      //     days: [1, 1, 1, 1, 1, 1, 1]
      //   }
      // })
@@ -268,14 +202,23 @@
            return `<input type="checkbox" class="taskCheckBox" data-action="check-row" ${checked} />`
          }
        },
        { name: 'wbs', label: '节点', width: 80, template: gantt.getWBSCode },
        { name: 'text', tree: true, label: '任务名称', width: 200, resize: true, editor: textEditor },
        // { 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: '工期(天)',
          label: '时长(分钟)',
          resize: true,
          editor: durationEditor,
          template: function(task) {
@@ -283,44 +226,8 @@
            // return (task.duration || 0) + 1 // 在当前duration的基础上加1
            return task.duration
          }
        },
        {
          name: 'owner', align: 'center', width: 75, label: '负责人', template: function(task) {
            // if (task.type == gantt.config.types.project) {
            //   return ''
            // }
            //
            // const store = gantt.getDatastore('resource')
            // const assignments = task[gantt.config.resource_property]
            //
            // if (!assignments || !assignments.length) {
            //   return 'Unassigned'
            // }
            //
            // if (assignments.length === 1) {
            //   return store.getItem(assignments[0].resource_id).text
            // }
            //
            // let result = ''
            // assignments.forEach(function(assignment) {
            //   const owner = store.getItem(assignment.resource_id)
            //   if (!owner) {
            //     return
            //   }
            //   result += '<div class=\'owner-label\' title=\'' + owner.text + '\'>' + owner.text.substr(0, 1) + '</div>'
            // })
            //
            // return result
            return '张三'
          },
          resize: true
        },
        {
          name: 'priority', width: 60, label: '优先级', align: 'center', resize: true, template: function(task) {
            return byId(gantt.serverList('task_priority'), task.priority)
          }
        },
        { name: 'add', width: 44 }
        }
        // { name: 'add', width: 44 }
      ]
      /* ↑↑↑ Grid Columns configuration ↑↑↑ */
@@ -382,66 +289,10 @@
        years: '年'
      }
      gantt.config.lightbox.sections = [
        { name: 'description', height: 38, map_to: 'text', type: 'textarea', focus: true },
        {
          name: 'resources', type: 'resources', map_to: 'owner', options: gantt.serverList('people'), default_value: 8
        },
        {
          name: 'calendar', height: 25, map_to: 'calendar_id', type: 'select', options: [
            { key: '', label: '默认' },
            { key: 'custom1', label: '选项一' },
            { key: 'custom2', label: '选项二' }
          ]
        },
        { name: 'time', type: 'duration', map_to: 'auto' }
      ]
      gantt.config.resource_store = 'resource'
      gantt.config.resource_property = 'owner'
      gantt.config.order_branch = true
      gantt.config.open_tree_initially = true
      // gantt.config.order_branch = true
      // gantt.config.open_tree_initially = true
      gantt.config.show_errors = false // 发生异常时,不允许弹出警告到 UI 界面
      const resourcesStore = gantt.createDatastore({
        name: gantt.config.resource_store,
        type: 'treeDatastore',
        initItem: function(item) {
          item.parent = item.parent || gantt.config.root_id
          item[gantt.config.resource_property] = item.parent
          item.open = true
          return item
        }
      })
      resourcesStore.attachEvent('onParse', function() {
        const people = []
        resourcesStore.eachItem(function(res) {
          if (!resourcesStore.hasChild(res.id)) {
            const copy = gantt.copy(res)
            copy.key = res.id
            copy.label = res.text
            people.push(copy)
          }
        })
        gantt.updateCollection('people', people)
      })
      resourcesStore.parse([
        { id: 1, text: 'QA', parent: null },
        { id: 2, text: 'Development', parent: null },
        { id: 3, text: 'Sales', parent: null },
        { id: 4, text: 'Other', parent: null },
        { id: 5, text: 'Unassigned', parent: 4 },
        { id: 6, text: 'John', parent: 1, unit: 'hours/day' },
        { id: 7, text: 'Mike', parent: 2, unit: 'hours/day' },
        { id: 8, text: 'Anna', parent: 2, unit: 'hours/day' },
        { id: 9, text: 'Bill', parent: 3, unit: 'hours/day' },
        { id: 10, text: 'Floe', parent: 3, unit: 'hours/day' }
      ])
      /* ↑↑↑ Resource configuration ↑↑↑ */
      gantt.config.grid_elastic_columns = true
@@ -449,9 +300,32 @@
      gantt.templates.tooltip_text = function(start, end, task) {
        // console.log(JSON.parse(JSON.stringify(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>开始时间:</b> ' + handleDatetime2(start) + '<br/><b>结束时间:</b> ' + handleDateReduceOneDay(end) + '<br/><b>进度:</b> ' + task.progress * 100 + '%'
        return '<b>任务:</b> ' + task.text + '<br/><b>开始时间:</b> ' + handleDatetime2(start) + '<br/><b>结束时间:</b> ' + handleDatetime2(end) + '<br/><b>进度:</b> ' + task.progress * 100 + '%'
      }
      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">`
        }
        if (task.type === 'task3') {
          return `<div  class="task3Css">`
        }
        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被调用
@@ -487,72 +361,469 @@
    // 加载任务数据
    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',
              'AdvaDevicRhythm': '5.0',
              'OneStartDate': '08:00~11:30',
              'TwoStartDate': '13:00~18:00',
              'ThreeStartDate': '',
              'FourStartDate': '',
              'FiveStartDate': ''
            }
          ]
        }
      ]
      // {
      //   'id': 3,
      //   'text': '设备:金工车间1号设备',
      //   saleOrder: 'SO-2025-05001',
      //   'calendar_id': 'customCalendar1',
      //   partName: '跑步机',
      //   partCode: 'Run01',
      //   description: '排产数量:500 报工数量:100 进度:20%',
      //   'type': 'task',
      //   'start_date': '2025-04-07 00:00',
      //   'parent': '2',
      //   'duration': 4,
      //   'progress': 0,
      //   checked: false
      // },
      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()) {
          // console.log(JSON.stringify(item))
          item.children.forEach((it, ind) => {
            // console.log(JSON.stringify(it))
            // 这里应该要生成一个以设备维度为基础的数组   不重不漏
            if (!this.allTasks.map(i => i.partCode).includes(it.AdvaDevicNumber)) {
              newArr.push({
                id: it.AdvaDevicNumber,
                type: 'task',
                text: '任务名称预留',
                partName: it.AdvaDevicName,
                partCode: it.AdvaDevicNumber,
                // start_date: item.YearDate + ' ' + it.OneStartDate.split('~')[0],
                // duration: this.calculateTimeRangeInMinutes(it.OneStartDate),
                render: 'split', // 用于在一个工作时间段内显示不下,需要进行分割显示
                checked: false,
                progress: 0,
                parent: 0,
                saleOrder: 'SO-2026-01001'
              })
              // console.log(this.allTasks, 'allTasks')
            }
            // 因为是五个时间段,所有要有个循环次数为5的循环
            for (let i = 0; i < 5; i++) {
              // console.log(item.YearDate + ' ' + it[this.fivePeriodsTimeName[i]].split('~')[0])
              if (it[this.fivePeriodsTimeName[i]]) {
                newArr.push({
                  id: index.toString() + ind.toString() + i.toString(),
                  type: 'task',
                  text: '任务名称预留',
                  partName: it.AdvaDevicName,
                  partCode: it.AdvaDevicNumber,
                  start_date: item.YearDate + ' ' + it[this.fivePeriodsTimeName[i]].split('~')[0],
                  duration: this.calculateTimeRangeInMinutes(it[this.fivePeriodsTimeName[i]]),
                  checked: false,
                  progress: 0,
                  parent: it.AdvaDevicNumber,
                  saleOrder: 'SO-2026-01001'
                })
              }
            }
          })
        }
      })
      // 使用原有的示例数据作为基础
      this.allTasks = [
      this.allTasks = newArr
      const dataBack = [
        {
          'id': 1,
          'text': '项目1',
          'text': '工单:MO-2025-05-001',
          saleOrder: 'SO-2025-05001',
          'calendar_id': 'customCalendar1',
          partName: '跑步机',
          partCode: 'Run01',
          description: '排产数量:500 报工数量:100 进度:20%',
          'type': 'project',
          'start_date': '2025-04-02 00:00',
          // 'duration': 5,
          'progress': 0.4,
          'owner': [{ 'resource_id': '5', 'value': 3 }],
          'progress': 0.2,
          'parent': 0,
          'checked': false,
          render: 'split'
          'checked': false
          // render: 'split'
        },
        {
          'id': 2,
          'text': '任务2',
          'type': 'task',
          'text': '工序:切割',
          saleOrder: 'SO-2025-05001',
          'calendar_id': 'customCalendar1',
          partName: '跑步机',
          partCode: 'Run01',
          description: '排产数量:500 报工数量:100 进度:20%',
          'type': 'project',
          'start_date': '2025-04-02 00:00',
          'duration': 2,
          'progress': 0.6,
          'owner': [{ 'resource_id': '5', 'value': 4 }],
          'progress': 0.2,
          'parent': '1',
          checked: false
        },
        {
          'id': 3,
          'text': '任务3',
          'text': '设备:金工车间1号设备',
          saleOrder: 'SO-2025-05001',
          'calendar_id': 'customCalendar1',
          partName: '跑步机',
          partCode: 'Run01',
          description: '排产数量:500 报工数量:100 进度:20%',
          'type': 'task',
          'start_date': '2025-04-07 00:00',
          'parent': '1',
          'duration': 3,
          'progress': 0.6,
          'owner': [{ 'resource_id': '5', 'value': 2 }],
          'parent': '2',
          'duration': 4,
          'progress': 0,
          checked: false
        },
        {
          'id': 4,
          'text': '任务4',
          'text': '设备:金工车间2号设备',
          saleOrder: 'SO-2025-05001',
          'calendar_id': 'customCalendar1',
          partName: '跑步机',
          partCode: 'Run01',
          description: '排产数量:500 报工数量:100 进度:20%',
          'type': 'task',
          'start_date': '2025-04-15 00:00',
          'parent': '1',
          'parent': '2',
          'duration': 3,
          'progress': 0.2,
          'owner': [{ 'resource_id': '5', 'value': 2 }],
          'progress': 0,
          checked: false
        },
        {
          'id': 5,
          'text': '工单:MO-2025-05-002',
          saleOrder: 'SO-2025-05002',
          'calendar_id': 'customCalendar1',
          partName: '走步机',
          partCode: 'W01',
          description: '排产数量:1000 报工数量:500 进度:50%',
          'type': 'project',
          'start_date': '2025-04-02 00:00',
          // 'duration': 5,
          'progress': 0.5,
          'parent': 0,
          'checked': false
          // render: 'split'
        },
        {
          'id': 6,
          'text': '工序:切割',
          saleOrder: 'SO-2025-05002',
          'calendar_id': 'customCalendar1',
          partName: '走步机',
          partCode: 'W01',
          description: '排产数量:1000 报工数量:500 进度:50%',
          'type': 'project',
          'start_date': '2025-04-02 00:00',
          'duration': 5,
          'progress': 0.5,
          'parent': '5',
          checked: false
        },
        {
          'id': 7,
          'text': '设备:金工车间3号设备',
          saleOrder: 'SO-2025-05002',
          partName: '走步机',
          'calendar_id': 'customCalendar1',
          partCode: 'W01',
          description: '排产数量:1000 报工数量:500 进度:50%',
          'type': 'task',
          'start_date': '2025-04-07 06:23',
          'parent': '6',
          'duration': 3,
          'progress': 0,
          checked: false
        },
        {
          'id': 8,
          'text': '设备:金工车间4号设备',
          saleOrder: 'SO-2025-05002',
          partName: '走步机',
          partCode: 'W01',
          'calendar_id': 'customCalendar1',
          description: '排产数量:1000 报工数量:600 进度:60%',
          'type': 'task',
          render: 'split', // 用于在一个工作时间段内显示不下,需要进行分割显示
          'start_date': '2025-04-12 00:00',
          'parent': '6',
          'duration': 4,
          'progress': 0,
          checked: false
        },
        {
          'id': 11,
          'text': '设备:金工车间4号设备',
          saleOrder: 'SO-2025-05002',
          partName: '走步机',
          partCode: 'W01',
          'calendar_id': 'customCalendar1',
          description: '排产数量:1000 报工数量:600 进度:60%',
          'type': 'task',
          'start_date': '2025-04-14 00:00',
          'parent': '8',
          'duration': 3,
          'progress': 0,
          checked: false
        },
        {
          'id': 12,
          'text': '设备:金工车间4号设备',
          saleOrder: 'SO-2025-05002',
          partName: '走步机',
          partCode: 'W01',
          'calendar_id': 'customCalendar1',
          description: '排产数量:1000 报工数量:600 进度:60%',
          'type': 'task',
          'start_date': '2025-04-18 00:00',
          'parent': '8',
          'duration': 1,
          'progress': 0,
          checked: false
        },
        {
          'id': 13,
          'text': '设备:金工车间4号设备',
          saleOrder: 'SO-2025-05002',
          partName: '走步机',
          partCode: 'W01',
          'calendar_id': 'customCalendar1',
          description: '排产数量:1000 报工数量:600 进度:60%',
          'type': 'task',
          'start_date': '2025-04-10 02:50',
          'parent': '8',
          'duration': 2,
          'progress': 0,
          checked: false
        },
        {
          'id': 10,
          'text': '设备:金工车间4号设备',
          saleOrder: 'SO-2025-05002',
          partName: '走步机',
          partCode: 'W01',
          'calendar_id': 'customCalendar1',
          description: '排产数量:1000 报工数量:600 进度:60%',
          'type': 'task3',
          'start_date': '2025-04-10 06:24',
          'parent': '8',
          'duration': 1,
          'progress': 0,
          checked: false
        },
        {
          'id': 14,
          'text': '设备:金工车间4号设备',
          saleOrder: 'SO-2025-05002',
          partName: '走步机',
          partCode: 'W01',
          'calendar_id': 'customCalendar1',
          description: '排产数量:1000 报工数量:600 进度:60%',
          'type': 'task2',
          'start_date': '2025-04-11 06:24',
          'parent': '8',
          'duration': 1,
          'progress': 0,
          checked: false
        },
        {
          'id': 9,
          'text': '设备:金工车间5号设备',
          saleOrder: 'SO-2025-05002',
          partName: '走步机',
          partCode: 'W01',
          'calendar_id': 'customCalendar1',
          description: '排产数量:1000 报工数量:400 进度:40%',
          'type': 'task',
          'start_date': '2025-04-10 00:00',
          'parent': '6',
          'duration': 3,
          'progress': 0,
          checked: false
        }
      ]
      // 添加更多示例数据,使分页效果更明显
      // for (let i = 25; i <= 100; i++) {
      //   this.allTasks.push({
      //     'id': i,
      //     'text': '任务' + i,
      //     'type': 'task',
      //     // 'start_date': handleDatetime(`0${Math.floor(Math.random() * 9 + 1)}-04-2025 00:00`),
      //     'start_date': `0${Math.floor(Math.random() * 9 + 1)}-04-2025 00:00`,
      //     'duration': Math.floor(Math.random() * 5) + 1,
      //     'parent': Math.floor(Math.random() * 10) + 1,
      //     'progress': Math.random(),
      //     'owner': [{ 'resource_id': '5', 'value': 3 }],
      //     'priority': Math.floor(Math.random() * 3) + 1,
      //     'checked': false
      //   })
      // }
      this.totalTasks = this.allTasks.length
      this.updatePaginatedTasks()
      this.renderGanttChart()
@@ -563,6 +834,68 @@
      const startIndex = (this.currentPage - 1) * this.pageSize
      const endIndex = Math.min(startIndex + this.pageSize, this.allTasks.length)
      this.paginatedTasks = 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重绘
      }
    },
    // 渲染甘特图
@@ -603,38 +936,23 @@
    },
    // 甘特图日期改变
    ganttDateRangeChange(val) {
      gantt.config.start_date = val[0]
      gantt.config.end_date = val[1]
      gantt.config.start_date = new Date(val[0] + ' 00:00')
      gantt.config.end_date = new Date(val[1] + ' 24:00')
      gantt.render()
    },
    // 回退拖动操作
    ganttUndo() {
      gantt.undo()
    },
    // 前进拖动操作
    ganttRedo() {
      gantt.redo()
    },
    // 放大
    ganttZoomIn() {
      gantt.ext.zoom.zoomIn()
    },
    // 缩小
    ganttZoomOut() {
      gantt.ext.zoom.zoomOut()
    },
    // 从甘特图中同步选中的 id 到 Vue data
    syncSelected() {
      // 同步当前页面任务到全局数据
      gantt.eachTask((task) => {
        const globalTask = this.allTasks.find(t => t.id === task.id)
        if (globalTask) {
          // console.log(globalTask, '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)
    },
@@ -642,7 +960,8 @@
    // 获取选中任务(示例)
    handleGetSelected() {
      const selected = this.allTasks.filter(t => t.checked)
      this.$notify.success(`当前已选中${selected.length} 条任务`)
      // this.$notify.success(`当前已选中${selected.length} 条任务`)
      this.$notify.success(`点击了`)
    },
    // 清空所有选择
@@ -678,27 +997,38 @@
  margin: unset;
}
.gantt_task_cell {
  background: rgba(5, 185, 100, .1);
}
/*.gantt_task_cell {*/
/*  background: rgba(5, 185, 100, .1);*/
/*}*/
/*非工作日*/
.weekend {
  background: rgba(255, 255, 255, 0.5);
}
/*!*非工作日*!*/
/*.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_task_progress {*/
/*border-radius: 10px !important; !* 建议与任务条半径一致 *!*/
/*}*/
.gantt_bar_task {
  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;
}
/*.gantt_bar_project {*/
/*border-radius: 10px !important;*/
/*}*/
.taskCheckBox {
  cursor: pointer;
@@ -713,4 +1043,60 @@
  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>