<template>
  <div class="gl-page-content gantt">
    <div class="page-filter-box">
      <el-form :model="filterFprmData" inline>
        <slot name="filter-form"></slot>
        <el-form-item v-if="dimension === 'person'" label="用户组：">
          <el-select
            v-model="filterFprmData.userGroupId"
            class="mw200"
            clearable
            @change="filterWorkLoadFun('group')"
          >
            <el-option
              v-for="item in groupList"
              :key="item.id"
              :label="item.groupName"
              :value="item.id"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item v-if="dimension === 'person'" label="用户：">
          <el-select
            v-model="filterFprmData.userIds"
            collapse-tags
            multiple
            clearable
            class="mw200"
            @change="userChangeFun"
            @focus="focusUserFun"
          >
            <el-option
              v-for="item in userList"
              :key="item.userId"
              :label="item.nickname"
              :value="item.userId"
            >
            </el-option>
          </el-select>
        </el-form-item>

        <el-form-item label="开始时间：">
          <el-date-picker
            v-model="filterFprmData.beginTime"
            type="datetime"
            :clearable="false"
            placeholder="请选择开始时间"
            format="YYYY-MM-DD HH:mm:ss"
            @change="filterWorkLoadFun"
          />
        </el-form-item>
        <el-form-item label="结束时间：">
          <el-date-picker
            v-model="filterFprmData.endTime"
            type="datetime"
            :clearable="false"
            placeholder="请选择开始时间"
            format="YYYY-MM-DD HH:mm:ss"
            @change="filterWorkLoadFun"
          />
        </el-form-item>
        <el-form-item>
          <el-radio-group
            class="group-width"
            v-model="filterFprmData.dateType"
            @change="changeDateTypeFun"
          >
            <el-radio value="month">月</el-radio>
            <el-radio value="week">周</el-radio>
            <el-radio value="day">日</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>
    </div>
    <div class="page-content">
      <div ref="ganttRef" class="gant-page" v-loading="loading"></div>
    </div>
    <div class="page-footer">
      <template v-for="(item, index) in colorList" :key="index">
        <div class="color-item" v-if="index > 0">
          <div :style="{ background: item }"></div>
          <div>{{ index >= 5 ? '大于或等于5个任务' : index + '个任务' }}</div>
        </div>
      </template>
    </div>
    <!-- 点击单元格的弹窗 -->
    <DetailDia v-model="visible" :list="tableData"></DetailDia>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted, nextTick, onBeforeUnmount, watch } from 'vue';
import { gantt } from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
import dayjs from 'dayjs';
import isBetween from 'dayjs/plugin/isBetween';
import isoWeek from 'dayjs/plugin/isoWeek';
import weekOfYear from 'dayjs/plugin/weekOfYear';
import { userQueryGroupApi, userQueryGroupDetailApi } from '@/api/system/user';
import { getListUserWorkloadsApi } from '@/api/project/task';
import { ElMessage } from 'element-plus';
import DetailDia from '@/views/task/workLoad/detailDia.vue';
import { debounce } from 'lodash-es';

const props = defineProps({
  // 维度：工作负载以人person为维度， 人力负载以项目project为维度
  dimension: {
    type: String,
    default: 'person',
  },
  // 筛选条件
  filterData: {
    type: Object,
    default: () => {
      return {};
    },
  },
  // 左侧配置列
  configColumns: {
    type: Array,
    default: () => [],
  },
  // 是否需要展示汇总行
  showTotal: {
    type: Boolean,
    default: false,
  },
  // 字段映射方法
  fieldMapFun: {
    type: Function,
    default: (data: any) => data,
  },
  api: {
    type: Function,
    default: getListUserWorkloadsApi,
  },
});

dayjs.extend(isBetween);
dayjs.extend(weekOfYear);
dayjs.extend(isoWeek);

let Gantt: any = gantt;
const ganttEvents = ref<any>([]);
const colorList: string[] = ['#ffffff', '#d9ecff', '#c6e2ff', '#c6e2ff', '#79bbff', '#409eff']; // 格子颜色
const taskOriginData = ref<any>([]); // 所有用户的任务数据
const loading = ref<boolean>(false);
const visible = ref(false);

const filterFprmData = reactive<any>({
  userGroupId: '',
  userIds: [],
  beginTime: '',
  endTime: '',
  dateType: 'week',
});

const ganttRef = ref();

const changeDateTypeFun = () => {
  loading.value = true;
  // 加载动画
  setTimeout(() => {
    setUserDayWork(filterFprmData.beginTime, filterFprmData.endTime);
    Gantt.ext.zoom.setLevel(filterFprmData.dateType);
    loading.value = false;
  }, 200);
};

const groupList = ref<any>([]);
const getUserGroupFun = async () => {
  if (props.dimension !== 'person') {
    return;
  }
  const res: any = await userQueryGroupApi({
    current: 1,
    size: 999,
  });
  if (res && res.code === 200) {
    groupList.value = res.data.data;
    if (groupList.value?.length) {
      // filterFprmData.userGroupId = groupList.value[0].id;
      // filterFprmData.userIds = await queryGroupUserListFun(filterFprmData.userGroupId);
    }
  }
};

const userList = ref<any>([]);
// 获取用户组内成员
const queryGroupUserListFun = async (id: any) => {
  if (!id) {
    userList.value = [];
    return;
  }
  const res: any = await userQueryGroupDetailApi({
    id,
    current: 1,
    size: 999,
  });

  if (res && res.code === 200) {
    const usrids = res.data.users.data.map((item: any) => {
      return item.userId;
    });

    userList.value = res.data.users.data;

    return usrids;
  } else {
    return [];
  }
};
const getWorkLoadDataFun = async () => {
  loading.value = true;
  Gantt.clearAll();
  taskOriginData.value = [];
  const param: any = {
    ...filterFprmData,
    ...props.filterData,
  };
  // 根据页面类型去掉没用的参数
  if (props.dimension === 'project') {
    delete param.userGroupId;
    delete param.userIds;
    if (param.projectIdList.length === 0) {
      return (loading.value = false);
    }
  } else {
    delete param.projectIdList;
  }
  const res: any = await props.api(param);
  if (res && res.code === 200) {
    if (props.dimension === 'project') {
      const list = res.data?.length ? props.fieldMapFun(res.data) : [];
      taskOriginData.value = list;
    } else {
      taskOriginData.value = res.data;
    }
    Gantt.config.start_date = filterFprmData.beginTime;
    Gantt.config.end_date = filterFprmData.endTime;
    Gantt.ext.zoom.setLevel(filterFprmData.dateType);
    if (taskOriginData.value.length > 0) {
      formatUserTask(taskOriginData.value, [filterFprmData.beginTime, filterFprmData.endTime]);
    }
    setUserDayWork(filterFprmData.beginTime, filterFprmData.endTime);
    nextTick(() => {
      Gantt.render();
    });
    loading.value = false;
  } else {
    loading.value = false;
  }
};

const formatUserTask = (data: any, time: any) => {
  const ganttData = data.map((item: any, index: any) => {
    return {
      id: index + 1,
      userName: item.userName,
      userId: item.userId,
      taskNum: item.taskNum ? item.taskNum : 0,
      workNum: item.workNum ? item.workNum : 0,
      relateProjectName: item.relateProjectName,
      start_date: time[0],
      duration: dayjs(time[1]).diff(dayjs(time[0]), 'day'),
    };
  });

  if (props.showTotal) {
    // 计算汇总行的数据
    const totalTaskNum = data.reduce((sum: number, item: any) => sum + (item.taskNum || 0), 0);
    const totalWorkNum = data.reduce((sum: number, item: any) => sum + (item.workNum || 0), 0);
    // 添加汇总行
    ganttData.push({
      id: 'total',
      type: 'total',
      userName: '汇总',
      taskNum: totalTaskNum,
      workNum: totalWorkNum,
      unscheduled: true,
      start_date: time[0],
      duration: dayjs(time[1]).diff(dayjs(time[0]), 'day'),
    });
  }

  Gantt.clearAll();
  Gantt.parse({
    data: ganttData,
  });
};

// 获取用户工作负载信息
const filterWorkLoadFun = async (flag?: any) => {
  // 判断开始时间和截止时间
  const begin = new Date(filterFprmData.beginTime).getTime();
  const end = new Date(filterFprmData.endTime).getTime();
  if (end < begin) {
    ElMessage.warning('结束时间无法小于开始时间！');
    filterFprmData.endTime = dayjs(new Date(filterFprmData.beginTime))
      .add(3, 'months')
      .format('YYYY-MM-DD HH:mm:ss');
  }

  if (flag === 'group') {
    filterFprmData.userIds = [];
    await queryGroupUserListFun(filterFprmData.userGroupId);
  }
  await refreshGanttFun();
};

// 生成日期列表的函数
const generateDateList = (startDate: any, endDate: any, mode = 'day') => {
  const result = [];
  let formatStart = null;
  let formatEnd = null;
  if (mode === 'day') {
    formatStart = dayjs(startDate);
    formatEnd = dayjs(endDate);
  } else if (mode === 'week') {
    formatStart = dayjs(startDate).startOf('isoWeek');
    formatEnd = dayjs(endDate).startOf('isoWeek');
  } else if (mode === 'month') {
    formatStart = dayjs(startDate).startOf('month');
    formatEnd = dayjs(endDate).startOf('month');
  }
  if (!formatStart || !formatEnd) {
    return [];
  }
  // 根据模式生成不同的日期列表
  while (formatStart.isBefore(endDate) || formatStart.isSame(formatEnd)) {
    result.push(formatStart.format('YYYY-MM-DD')); // 将日期加入结果列表
    if (mode === 'day') {
      formatStart = formatStart.add(1, 'day'); // 每天
    } else if (mode === 'week') {
      formatStart = formatStart.startOf('isoWeek').add(1, 'week'); // 每周第一天（星期一）
    } else if (mode === 'month') {
      formatStart = formatStart.startOf('month').add(1, 'month'); // 每月第一天
    }
  }
  return result;
};

// 每个用户gantt单元格的任务
const userTimeItemWorkList: any = new Map();
const columnTotals = ref<any>({});
const setUserDayWork: any = (startDate: any, endDate: any) => {
  userTimeItemWorkList.clear();
  const dateList = generateDateList(startDate, endDate, filterFprmData.dateType);
  // 初始化每列的总数
  dateList.forEach((date) => {
    columnTotals.value[date] = 0;
  });
  taskOriginData.value.forEach((item: any) => {
    const dateWorkMap: any = new Map();
    dateList.forEach((item_1: any) => {
      const tasks = filterUserTask(item.taskList, item_1);
      dateWorkMap.set(item_1, tasks);
      // 累加每列的任务数
      columnTotals.value[item_1] = (columnTotals.value[item_1] || 0) + tasks?.length;
    });
    userTimeItemWorkList.set(item.userName, dateWorkMap);
  });
};

// 筛选某一用户在某一周或者某月的任务
const filterUserTask = (taskList: any, date: any) => {
  let filterTask: any = [];
  filterTask = taskList
    ?.filter((item: any) => {
      const startTime = dayjs(item.beginTime);
      // const endTime = startTime.add(item.days - 1, 'day');
      const endTime = dayjs(item.endTime);
      const weekOrMonthStart = dayjs(date);
      const addDays =
        filterFprmData.dateType === 'day'
          ? 0
          : filterFprmData.dateType === 'week'
            ? 6
            : weekOrMonthStart.daysInMonth() - 1;
      const weekOrMonthEnd = weekOrMonthStart.add(addDays, 'day');
      // 判断当前任务的时间段是否在这周
      // 开始时间在这周||结束时间在这周||这周整个在任务的时间段中
      return (
        startTime.isBetween(weekOrMonthStart, weekOrMonthEnd, 'day', '[]') ||
        endTime.isBetween(weekOrMonthStart, weekOrMonthEnd, 'day', '[]') ||
        (weekOrMonthStart.isBetween(startTime, endTime, 'day', '[]') &&
          weekOrMonthEnd.isBetween(startTime, endTime, 'day', '[]'))
      );
    })
    ?.map((item: any) => item.id);
  return filterTask;
};

// 初始化配置甘特图
const initGantt = () => {
  const zoomConfig: any = {
    levels: [
      {
        name: 'day',
        scales: [
          { unit: 'year', step: 1, format: '%Y' },
          { unit: 'month', step: 1, format: '%M' },
          { unit: 'week', step: 1, format: '%W周' },
          { unit: 'day', format: '%d' },
        ],
      },
      {
        name: 'week',
        min_column_width: 50,
        scales: [
          { unit: 'year', step: 1, format: '%Y' },
          { unit: 'month', step: 1, format: '%M' },
          { unit: 'week', step: 1, format: '%W周' },
        ],
      },
      {
        name: 'month',
        scales: [
          { unit: 'year', step: 1, format: '%Y' },
          { unit: 'month', step: 1, format: '%M' },
        ],
      },
    ],
  };
  Gantt.ext.zoom.init(zoomConfig);
  Gantt.ext.zoom.setLevel('week');
  Gantt.config.autosize = false;
  Gantt.config.show_grid = true;
  // 只读模式：打开后不可以操作甘特图
  Gantt.config.readonly = true;
  // 设置空数据
  Gantt.config.show_empty_state = false;
  Gantt.config.fit_tasks = true;
  Gantt.config.min_column_width = 30;
  Gantt.config.date_format = '%Y-%m-%d';
  Gantt.config.scale_height = 28 * 3;
  Gantt.config.select_task = false;
  Gantt.i18n.setLocale('cn');
  const defaultColumns = [
    {
      name: 'userName',
      label: '姓名',
      width: 160,
      align: 'left',
      template: function (task: any) {
        return `<div class="gantt-cell-ellipsis" title="${task.userName}">
        <svg class="icon" viewBox="0 0 1024 1024">
          <path fill="currentColor" d="M512 512a192 192 0 1 0 0-384 192 192 0 0 0 0 384zm0 64a256 256 0 1 1 0-512 256 256 0 0 1 0 512zm320 320v-32a160 160 0 0 0-160-160H352a160 160 0 0 0-160 160v32a32 32 0 0 1-64 0v-32a224 224 0 0 1 224-224h320a224 224 0 0 1 224 224v32a32 32 0 0 1-64 0z"/>
        </svg>
        ${task.userName}</div>`;
      },
    },
    {
      name: 'taskNum',
      label: '任务数量',
      align: 'left',
      width: 70,
      onrender: (task: any) => {
        return `${task.taskNum}`;
      },
    },
    {
      name: 'workNum',
      label: '当前任务量',
      align: 'left',
      width: 70,
      onrender: (task: any) => {
        return `${task.workNum || '0'}`;
      },
    },
  ];
  Gantt.config.columns = defaultColumns.map((item, index) => {
    return { ...item, ...(props.configColumns[index] || {}) };
  });
  Gantt.templates.timeline_cell_content = function (task: any, date: any) {
    // 汇总的样式
    if (task.type === 'total') {
      const dateStr = dayjs(date).format('YYYY-MM-DD');
      const total = columnTotals.value[dateStr] || 0;
      return `<div class="total-cell">${total}</div>`;
    }
    let tasks: any = [];
    if (task.userName && userTimeItemWorkList && userTimeItemWorkList.has(task.userName)) {
      const currentNameMap = userTimeItemWorkList.get(task.userName);
      const dateFormat = dayjs(date).format('YYYY-MM-DD');
      if (currentNameMap && currentNameMap.has(dateFormat)) {
        tasks = currentNameMap.get(dateFormat);
      }
    }
    return `<div class='${tasks?.length > 0 ? 'cell-style' : ''}' style='background:${
      colorList[tasks?.length >= 5 ? 5 : tasks?.length]
    };cursor:${tasks?.length > 0 ? 'pointer' : 'default'}' userName=${task?.userName} userId=${
      task?.userId
    }  tasks=${JSON.stringify(tasks)} date=${dayjs(date).format('YYYY-MM-DD')}></div>`;
  };
  Gantt.templates.grid_row_class = function (start: any, end: any, task: any) {
    console.warn(start, end);
    if (task.type === 'total') {
      return 'gantt_total_row';
    }
    return '';
  };

  Gantt.templates.task_row_class = function (start: any, end: any, task: any) {
    console.warn(start, end);
    if (task.type === 'total') {
      return 'gantt_total_row';
    }
    return '';
  };
  // gantt渲染结束添加监听单元格点击事件
  const onGanttRender = Gantt.attachEvent('onGanttRender', function () {
    nextTick(() => {
      monitorCellClickFun();
    });
  });
  ganttEvents.value.push(onGanttRender);
  const onLoadStart = Gantt.attachEvent('onGanttScroll', function () {
    nextTick(() => {
      monitorCellClickFun();
    });
  });
  ganttEvents.value.push(onLoadStart);

  // 任务条单击事件
  const onloadClick = Gantt.attachEvent('onTaskClick', () => {});
  ganttEvents.value.push(onloadClick);

  Gantt.init(ganttRef.value as HTMLElement);
  Gantt.parse({
    data: [],
  });
};

const tableData = ref<any>([]);

// 监听单元格点击事件
const monitorCellClickFun = () => {
  const oList: any = document.getElementsByClassName('cell-style');

  for (let i = 0; i < oList.length; i++) {
    if (oList[i].onclick) {
      continue;
    }
    oList[i].onclick = async (e: any) => {
      const obj = {
        currentIdTasksId: JSON.parse(e.target.getAttribute('tasks')) || [],
        currentDate: dayjs(e.target.getAttribute('date')).week(),
        currentUserName: e.target.getAttribute('userName'),
      };

      const tasks = taskOriginData.value.find((item: any) => {
        return item.userName === obj.currentUserName;
      })?.taskList;

      tableData.value = tasks.filter((item: any) => {
        return obj.currentIdTasksId.includes(item.id);
      });
      visible.value = true;
    };
  }
};

const refreshGanttFun = async () => {
  Gantt.clearAll();
  initGantt();
  await getWorkLoadDataFun();
};
// 切换用户
const userChangeFun = debounce(async () => {
  await filterWorkLoadFun();
}, 500);
// 用户下拉框获取焦点
const focusUserFun = () => {
  if (!filterFprmData.userGroupId) {
    ElMessage.warning('请选择用户组后再选择用户进行筛选!');
  }
};

watch(
  () => props.filterData,
  async () => {
    await refreshGanttFun();
  },
  { deep: true }
);

onMounted(async () => {
  await getUserGroupFun();
  filterFprmData.beginTime = dayjs().subtract(3, 'month').format('YYYY-MM-DD HH:mm:ss');
  filterFprmData.endTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
  await refreshGanttFun();
});

onBeforeUnmount(() => {
  ganttEvents.value?.forEach((item: any) => {
    Gantt.detachEvent(item);
  });
  Gantt.templates.timeline_cell_content = function () {
    return '';
  };
  Gantt.templates.grid_row_class = function () {
    return '';
  };
  Gantt.templates.task_row_class = function () {
    return '';
  };
  Gantt.clearAll();
  Gantt = null;
});
</script>

<style lang="scss" scoped>
.gl-page-content {
  width: 100%;
  height: 100%;
  .page-filter-box {
    width: 100%;
    display: flex;
    align-items: center;
    .mw200 {
      width: 200px;
    }
    .group-width {
      width: 200px;
    }
    .el-form {
      width: 100%;
      display: flex;
      flex-wrap: wrap;
    }
    .mr0 {
      margin-right: 0px;
    }
  }
  .page-content {
    width: 100%;
    height: calc(100% - 120px);
    .gant-page {
      width: 100%;
      height: 100%;
    }
  }
  .page-footer {
    height: 30px;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    .color-item {
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: flex-end;
      margin-left: 12px;
      & > div:first-child {
        height: 16px;
        width: 16px;
        border-radius: 50%;
        margin-right: 3px;
      }
    }
  }
}
</style>

<!-- 甘特图相关样式 -->
<style lang="scss">
.gantt {
  .gantt_total_row {
    font-weight: bold;
    .total-cell {
      height: 100%;
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #606266;
    }
  }
  .gantt_cell {
    .gantt-cell-ellipsis {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      width: 100%;
      display: inline-block;
      .icon {
        width: 16px;
        height: 20px;
        margin-right: 4px;
        transform: translateY(3px);
      }
    }
  }

  .gantt_grid_data .gantt_row:hover {
    background-color: #ecf7ff;
  }
  .gantt_bars_area {
    display: none;
  }
  .cell-style {
    height: 100%;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  // 深色主题适配
  .gantt_grid div,
  .gantt_task_cell {
    background-color: var(--el-bg-color);
    border-color: var(--el-border-color);
  }
  .gantt_grid_data .gantt_cell {
    color: var(--el-text-color-primary);
  }
  .gantt_total_row .total-cell {
    color: var(--el-text-color-primary);
  }
  .gantt_data_area,
  .gantt_task_scale {
    background-color: var(--el-bg-color);
  }
  .gantt_task_row,
  .gantt_task .gantt_task_scale .gantt_scale_cell,
  .gantt_scale_line {
    border-color: var(--el-border-color);
  }
}
</style>
