<template>
  <div class="main">
    <!--头部-->
    <div class="header">
      <a-button style="margin-left: 10px" shape="circle" @click.stop="gotoUpLevel">
        <left-outlined />
      </a-button>

      <a-space style="margin-right: 10px">
        <a-button class="border-radius" @click="previewClick">预览</a-button>
        <a-button type="primary" class="border-radius" :loading="saveBtnLoading" @click="save">保存</a-button>
      </a-space>
    </div>

    <div class="body">
      <!--左侧组件列表-->
      <div class="left">
        <left-widgets @dbclick="leftWidgetDbClick" @data-done="gridStackSetupDragIn"></left-widgets>
      </div>

      <div class="right" id="right" ref="rightRef">
        <!--背景格子-->
        <canvas class="canvas" id="canvas"></canvas>

        <div class="grid-stack" ref="gridStackRef">
          <div
            v-for="w in layout"
            class="grid-stack-item"
            :gs-x="w.x"
            :gs-y="w.y"
            :gs-w="w.w"
            :gs-h="w.h"
            :gs-id="w.id"
            :id="w.id"
            :key="w.id"
            @click.stop="e => handleWidgetClick(e, w)"
          >
            <div class="grid-stack-item-content">
              <div class="masks">
                <div class="item-toolbar" v-if="showElementToolbar(w)">
                  <div class="item-toolbar-button" @click.stop="removeWidget(w)">
                    <close-circle-outlined />
                    删除组件
                  </div>
                  <div class="item-toolbar-button" @click.stop="copyWidget(w)">
                    <copy-outlined />
                    复制组件
                  </div>
                </div>
              </div>

              <!--轮播图组件-->
              <RotationWidget
                :ref="e => (widgetRefs[w.widget.id] = e)"
                v-if="w.widget.componentCode === 'CAROUSEL'"
                :preview="true"
                :designId="designId"
                :widget="w.widget"
                :widget-id="w.widget.id"
                :widget-name="w.widget.componentName"
              >
              </RotationWidget>

              <!--新闻通知-->
              <NewsWidget
                :ref="e => (widgetRefs[w.widget.id] = e)"
                v-if="w.widget.componentCode === 'NEWS'"
                :preview="true"
                :designId="designId"
                :widget="w.widget"
                :widget-id="w.widget.id"
                :widget-name="w.widget.componentName"
              >
              </NewsWidget>

              <!--流程中心-->
              <FlowTaskWidget
                :ref="e => (widgetRefs[w.widget.id] = e)"
                :designId="designId"
                :widget="w.widget"
                v-if="w.widget.componentCode === 'FLOW_CENTER'"
                :preview="true"
              >
              </FlowTaskWidget>

              <!--应用入口-->
              <AppEnterWidget
                :ref="e => (widgetRefs[w.widget.id] = e)"
                v-if="w.widget.componentCode === 'APPLICATION_ENTRANCE'"
                :preview="true"
                :designId="designId"
                :widget="w.widget"
                :widget-id="w.widget.id"
                :widget-name="w.widget.componentName"
              >
              </AppEnterWidget>

              <!--日历-->
              <CalendarWidget
                :ref="e => (widgetRefs[w.widget.id] = e)"
                :designId="designId"
                :widget="w.widget"
                v-if="w.widget.componentCode === 'CALENDAR'"
              >
              </CalendarWidget>

              <!--本月工作-->
              <MonthWorkWidget
                :ref="e => (widgetRefs[w.widget.id] = e)"
                :designId="designId"
                :widget="w.widget"
                v-if="w.widget.componentCode === 'MONTH_WORK'"
              >
              </MonthWorkWidget>

              <!--快捷入口-->
              <QuickEnterWidget
                :ref="e => (widgetRefs[w.widget.id] = e)"
                v-if="w.widget.componentCode === 'QUICK_ENTRANCE'"
                :preview="true"
                :designId="designId"
                :widget="w.widget"
                :widget-id="w.widget.id"
                :widget-name="w.widget.componentName"
              >
              </QuickEnterWidget>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!--widget配置面板-->
    <widget-panel v-if="showWidgetPanel" @closed="closedWidgetPanel" @update-widget="updateWidget"></widget-panel>
  </div>
</template>

<script setup>
import { useRoute, useRouter } from 'vue-router';
import { nextTick, onMounted, onUnmounted, provide, reactive, ref, defineAsyncComponent } from 'vue';
import { PortalDesignApi } from './api/PortalDesignApi';
import { useEventListener } from '@vueuse/core';
import { GridStack } from 'gridstack';
import { message } from 'ant-design-vue';
import { constant } from '@/views/portal/constant/constant';
import { deepClone, getUUID, arrayToObject } from '@/utils/common/util';
import { rotationDefaultData, newsDefaultData, appEnterDefaultData, defaultData } from './data/default-data';

const LeftWidgets = defineAsyncComponent(() => import('./components/left-widgets.vue'));
const WidgetPanel = defineAsyncComponent(() => import('./widget-panel.vue'));
const RotationWidget = defineAsyncComponent(() => import('@/views/portal/components/widget/rotation_widget.vue'));
const NewsWidget = defineAsyncComponent(() => import('@/views/portal/components/widget/news_widget.vue'));
const FlowTaskWidget = defineAsyncComponent(() => import('@/views/portal/components/widget/flow_task_widget.vue'));
const AppEnterWidget = defineAsyncComponent(() => import('@/views/portal/components/widget/app_enter_widget.vue'));
const CalendarWidget = defineAsyncComponent(() => import('@/views/portal/components/widget/calendar_widget.vue'));
const MonthWorkWidget = defineAsyncComponent(() => import('@/views/portal/components/widget/month_work_widget.vue'));
const QuickEnterWidget = defineAsyncComponent(() => import('@/views/portal/components/widget/quick_enter_widget.vue'));

const route = useRoute();
const router = useRouter();

// 保存按钮加载状态
const saveBtnLoading = ref(false);

// widget dom
const widgetRefs = ref({});

// 门户设计id
const designId = ref('');
provide('designId', designId);

// 是否显示widget配置面板
const showWidgetPanel = ref(false);

// 当前点击的widget
const currentWidget = reactive({});
provide('currentWidget', currentWidget);

// 右侧dom
const rightRef = ref(null);

// grid dom
const gridStackRef = ref(null);

// 页面设计布局
const layout = ref([]);

// grid
let grid = null;

let gridOptions = {
  auto: false,
  float: true,
  margin: '0px',
  column: 12,
  minRow: 100,
  cellHeight: '70px',
  alwaysShowResizeHandle: true,
  acceptWidgets: function (el) {
    // // 判断需要拖拽的组件是否存在白名单中
    // let whiteList = ['UNREAD_EMAIL', '1663426323302117383'];
    // let componentId = el.dataset.componentid;
    // if (whiteList.filter(item => item === componentId).length === 1) {
    //   return true;
    // } else {
    //   return false;
    // }
    return true;
  },
  children: []
};

// 门户设计详情
const portalDesignDetail = ref({});
provide('portalDesignDetail', portalDesignDetail);

onMounted(() => {
  const { id } = route.query;
  if (!id) return gotoUpLevel();
  designId.value = id;
  // 初始化 grid
  grid = GridStack.init(gridOptions);

  nextTick().then(() => {
    // 画背景格子
    canvasRect(gridOptions.column);

    // 监听grid事件
    onEvent();
  });

  // 如果传入了id则表示是编辑，调用查询接口
  if (designId.value) {
    PortalDesignApi.getDesignConfig({ designId: designId.value }).then(res => {
      let data = res.data;

      portalDesignDetail.value = data;

      if (data.designLayoutJson) {
        let _layout = data.designLayoutJson.widgets || [];
        if (_layout.length > 0) {
          layout.value = _layout;

          nextTick(() => {
            layout.value.forEach(item => {
              if (grid) {
                grid.makeWidget(`#${item.id}`);
              }
            });
          });
        }
      }
    });
  }
});

/**
 * clone
 * @param event
 * @returns {*}
 */
const clone = event => {
  let cloneNode = event.target.cloneNode(true);
  return cloneNode;
};

/**
 * 绘画矩形
 * @param column 列数
 */
const canvasRect = (column = 12) => {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');

  // 设置画布尺寸为窗口宽度和高度
  let rightDom = gridStackRef.value;
  if (!rightDom) {
    rightDom = rightRef.value;
  }
  canvas.width = rightDom.clientWidth;
  canvas.height = rightDom.clientHeight;

  // 定义矩形属性
  const rectHeight = 70;
  const rectMargin = 0;
  const rectFillColor = '#F8F8F8';
  const rectBorderColor = '#C5CFD1';
  const rectBorderDash = [0, 0]; // 边框虚线样式

  // 计算每行和每列的矩形数量
  const cols = column;
  const rows = Math.floor(canvas.height / (rectHeight + rectMargin));

  // 计算矩形宽度
  const rectWidth = (canvas.width - (cols - 1) * rectMargin) / cols;

  // 绘制多个矩形
  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
      const rectX = j * (rectWidth + rectMargin);
      const rectY = i * (rectHeight + rectMargin);

      ctx.beginPath();
      ctx.setLineDash(rectBorderDash); // 设置边框虚线样式
      ctx.rect(rectX, rectY, rectWidth, rectHeight);
      ctx.fillStyle = rectFillColor;
      ctx.fill();
      ctx.lineWidth = 1;
      ctx.strokeStyle = rectBorderColor;
      ctx.stroke();
    }
  }
};

// 监听窗口改变 重画背景
let onResizeListener = useEventListener(window, 'resize', () => {
  nextTick(() => {
    if (grid) {
      canvasRect(gridOptions.column);
    }
  });
});

/**
 * 复制组件
 * @param widget
 */
const copyWidget = async widget => {
  if (widget.widget) {
    const widgetDom = document.getElementById(widget.id);
    let w = widgetDom.getAttribute('gs-w');
    let h = widgetDom.getAttribute('gs-h');
    let x = widgetDom.getAttribute('gs-x');
    let y = widgetDom.getAttribute('gs-y');

    let elementIdResId = getUUID();
    let node = {
      x: x - 1,
      y: y,
      w: w,
      h: h,
      widget: {
        componentCode: widget.widget.componentCode,
        componentId: widget.widget.componentId,
        componentName: widget.widget.componentName
      }
    };
    setParam(node);
    node.id = 'w_' + elementIdResId;
    node.widget.id = elementIdResId;
    layout.value.push(node);
    nextTick().then(() => {
      grid.makeWidget(node.id);
    });
  }
};

const setParam = node => {
  const { componentCode } = node.widget;
  switch (componentCode) {
    case 'CAROUSEL':
      node.widget.param = deepClone(rotationDefaultData);
      break;
    case 'NEWS':
      node.widget.param = deepClone(newsDefaultData);
      break;
    case 'APPLICATION_ENTRANCE':
      node.widget.param = deepClone(appEnterDefaultData);
      break;
    default:
      node.widget.param = deepClone(defaultData);
      node.widget.param.elementName = constant[componentCode];
      break;
  }
};

/**
 * 移除组件
 * @param widget
 */
const removeWidget = widget => {
  // 如果存在则移除并删除widget
  const dom = document.getElementById(widget.id);
  for (let i = layout.value.length - 1; i >= 0; i--) {
    let item = layout.value[i];
    if (item.id === widget.id) {
      layout.value.splice(i, 1);
    }
  }
  grid.removeWidget(dom, false);
};

/**
 * 左侧组件列表项双击事件
 * @param w
 */
const leftWidgetDbClick = async widget => {
  let elementIdResId = getUUID();
  let { w, h } = handleWidgetWH(widget.componentCode);
  let node = {
    x: 0,
    y: 0,
    w: w,
    h: h,
    widget: {
      componentCode: widget.componentCode,
      componentId: widget.componentId,
      componentName: widget.componentName
    }
  };
  setParam(node);
  node.id = 'w_' + elementIdResId;
  node.widget.id = elementIdResId;
  layout.value.push(node);
  nextTick().then(() => {
    if (grid) {
      grid.makeWidget(node.id);
    }
  });
};

/**
 * 注册拖拽
 */
const gridStackSetupDragIn = () => {
  nextTick(() => {
    GridStack.setupDragIn('.left-content-group-content-item', {
      appendTo: 'body',
      helper: clone
    });
  });
};

/**
 * 返回上级
 */
const gotoUpLevel = () => {
  router.push({
    path: '/portal/backend/config'
  });
};

/**
 * 预览
 */
const previewClick = () => {
  // 跳转到预览页面
  const { href } = router.resolve({
    path: '/portal/design/preview',
    query: {
      id: designId.value
    }
  });
  window.open(href, '_blank');
};

// 保存
const save = () => {
  if (grid) {
    // 构建请求参数
    let params = { designId: designId.value };

    let _layout = deepClone(layout.value);
    let _obj = arrayToObject(_layout, 'id');
    let _saveLayout = deepClone(grid.save(false, false));

    _saveLayout.forEach(item => {
      let objElement = _obj[item.id];
      if (objElement) {
        delete objElement.widget.componentName;
        item.widget = objElement.widget;
      }
    });
    params.designLayoutJson = { widgets: _saveLayout };

    saveBtnLoading.value = true;
    PortalDesignApi.updateDesign(params)
      .then(res => {
        message.success(res.message);
      })
      .finally(() => {
        saveBtnLoading.value = false;
      });
  }
};

/**
 * 监听grid
 */
const onEvent = () => {
  if (grid) {
    grid.on('added', async (event, items) => {
      if (items.length === 1) {
        let item = items[0];
        if (!item.id) {
          let { x, y, el } = item;
          grid.removeWidget(el, true, false);

          let componentId = el.dataset.componentid;
          let componentName = el.dataset.componentname;
          let componentCode = el.dataset.componentcode;

          let { w, h } = handleWidgetWH(componentCode);
          let node = {
            x,
            y,
            w: w,
            h: h,
            widget: {
              componentId: componentId,
              componentCode: componentCode,
              componentName: componentName
            }
          };
          setParam(node);
          let elementIdResId = getUUID();
          node.id = 'w_' + elementIdResId;
          node.widget.id = elementIdResId;
          layout.value.push(node);
          nextTick().then(() => {
            grid.makeWidget(node.id);
          });
        }
      }
    });
  }
};

/**
 * 组件点击事件
 * @param widget
 */
const handleWidgetClick = (e, widget) => {
  if (e.target) {
    let className = e.target.className;
    if (className && className === 'masks') {
      currentWidget.value = widget;
      if (showElementToolbar(widget)) {
        showWidgetPanel.value = true;
      }
    }
  }
};

/**
 * 根据组件code返回组件宽高
 * @param componentCode
 */
const handleWidgetWH = componentCode => {
  let res = { w: 4, h: 3 };
  if (componentCode === 'CALENDAR' || componentCode === 'UNREAD_MAIL' || componentCode === 'NEWS' || componentCode === 'CAROUSEL') {
    res.w = 4;
    res.h = 5;
  } else if (componentCode === 'APPLICATION_ENTRANCE') {
    res.w = 4;
    res.h = 5;
  } else if (componentCode === 'FLOW_CENTER') {
    res.w = 8;
    res.h = 6;
  } else if (componentCode === 'MONTH_WORK') {
    res.w = 7;
    res.h = 2;
  } else if (componentCode === 'QUICK_ENTRANCE') {
    res.w = 9;
    res.h = 3;
  }
  return res;
};

/**
 * 关闭widget配置面板
 * @param val
 */
const closedWidgetPanel = val => {
  showWidgetPanel.value = val;
};

/**
 * 更新组件 调用各个组件里的方法进行更新
 * @param val
 */
const updateWidget = val => {
  if (val) {
    let element = widgetRefs.value[val];
    if (element) {
      element.updateWidget();
    }
  }
};

/**
 * 是否显示元素工具栏 复制/删除 按钮
 * @param widget
 * @returns {boolean}
 */
const showElementToolbar = widget => {
  let res = true;
  return res;
};

onUnmounted(() => {
  // 销毁监听器
  if (onResizeListener) {
    onResizeListener();
  }
  // 销毁画布
  if (grid) {
    grid.destroy();
  }
});
</script>

<style scoped lang="less">
.main {
  width: 100%;
  height: 100%;
  background-color: #fff;
}

.header {
  width: 100%;
  height: 50px;
  border: 1px #d9d9d9 solid;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.body {
  width: 100%;
  height: calc(100vh - 50px);
  display: flex;
}

.left {
  width: 260px;
  height: 100%;
  border-right: 1px #d9d9d9 solid;
}

.right {
  width: calc(100% - 260px);
  height: 100%;
  box-sizing: border-box;
  padding: 5px 0 0 5px;
  overflow: auto;
  position: relative;
}

.grid-stack {
  width: 100%;
  height: 100%;
}

.canvas {
  position: absolute;
}

.item-toolbar {
  display: none;
}

.item-toolbar-button {
  cursor: pointer;
  padding-right: 4px;
}

.grid-stack-item-content:hover {
  box-shadow: 0 0 10px 0 #ecbb8a;

  .item-toolbar {
    cursor: move;
    display: flex;
    align-items: center;
    flex-direction: row-reverse;
    width: 100%;
    height: 30px;
    color: #fff;
    background: rgba(0, 0, 0, 0.5);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: center;
  }
}
.masks {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 99;
  height: 100%;
  background-color: rgba(0, 0, 0, 0);
}
</style>
