<template>
  <div class="tb-container" ref="tbContainerRef">
    <el-table
      :data="datas"
      border
      style="width: 100%; margin-top: 10px"
      @cell-dblclick="cellDblclick"
      @header-contextmenu="(column, $event) => rightClick(null, column, $event)"
      @row-contextmenu="rightClick"
      :row-class-name="tableRowClassName"
    >
      <el-table-column v-for="(col, idx) in columnList" :key="col.prop" :prop="col.prop" :label="col.label" :index="idx" />
    </el-table>
    <!-- 右键菜单框 -->
    <div v-show="showMenu" id="contextmenu" @mouseleave="showMenu = false">
      <el-icon class="el-icon-circle-close hideContextMenu" @click="showMenu = false"><CircleClose /></el-icon>
      <el-button size="small" type="primary" @click="addColumn()">前方插入一列</el-button>
      <el-button size="small" type="primary" @click="addColumn(true)">后方插入一列</el-button>
      <el-popconfirm title="确定删除该列吗？" @confirm="delColumn" width="160px">
        <template #reference>
          <el-button size="small" type="primary">删除当前列</el-button>
        </template>
      </el-popconfirm>
      <el-button size="small" type="primary" @click="renameCol($event)">更改列名</el-button>
      <div v-show="!curTarget.isHead">
        <hr />
        <el-button size="small" type="primary" @click="addRow()">上方插入一行</el-button>
        <el-button size="small" type="primary" @click="addRow(true)">下方插入一行</el-button>
        <el-popconfirm title="确定删除该行吗？" @confirm="delRow" width="160px">
          <template #reference>
            <el-button size="small" type="primary">删除当前行</el-button>
          </template>
        </el-popconfirm>
      </div>
    </div>
    <!-- 单元格/表头内容编辑框 -->
    <div v-show="showEditInput" id="editInput" @mouseleave="showEditInput = false">
      <el-input placeholder="请输入内容" ref="inputRef" v-model="curTarget.val" @keyup.enter="showEditInput = false" clearable @change="updTbCellOrHeader">
        <template #prepend>{{ curColumn.label || curColumn.prop }}</template>
      </el-input>
    </div>
  </div>
</template>

<script setup>
import { computed, nextTick, ref } from 'vue';
const props = defineProps({
  //列
  columnList: {
    type: Array,
    default: [],
  },
  datas: {
    type: Array,
    default: [],
  },
});

const tbContainerRef = ref(null);
const showMenu = ref(false); //右键显示菜单
const showEditInput = ref(false); // 显示单元格/表头内容编辑输入框
const inputRef = ref(null);

const curTarget = ref({
  // 当前目标信息
  rowIdx: null, // 行下标
  colIdx: null, // 列下标
  val: null, // 单元格内容/列名
  isHead: undefined, // 当前目标是表头？
});
const countCol = ref(0); // 新建列计数
const curColumn = computed(() => {
  return props.columnList[curTarget.value.colIdx] ?? {};
});

// 单元格双击事件 - 更改单元格数值
function cellDblclick(row, column, cell, $event) {
  showEditInput.value = false;
  if (column.index == null) return;
  locateMenuOrEditInput('editInput', 200, $event); // 编辑框定位
  showEditInput.value = true;
  inputRef.value.focus();
  // 当前目标
  curTarget.value = {
    rowIdx: row.row_index,
    colIdx: column.index,
    val: row[column.property],
    isHead: false,
  };
}
function tableRowClassName({ row, rowIndex }) {
  row.row_index = rowIndex;
}
function rightClick(row, column, $event) {
  // 阻止浏览器自带的右键菜单弹出
  $event.preventDefault();
  showMenu.value = false;
  if (column.index == null) return;
  locateMenuOrEditInput('contextmenu', 140, $event); // 菜单定位
  showMenu.value = true;
  // 当前目标
  curTarget.value = {
    rowIdx: row ? row.row_index : null, // 目标行下标，表头无 row_index
    colIdx: column.index, // 目标项下标
    val: row ? row[column.property] : column.label, // 目标值，表头记录名称
    isHead: !row,
  };
}
function locateMenuOrEditInput(eleId, eleWidth, $event) {
  // 表格容器的位置
  const { x: tbX, y: tbY } = tbContainerRef.value.getBoundingClientRect();
  // 当前鼠标位置
  const { x: pX, y: pY } = $event;
  const ele = document.getElementById(eleId);
  ele.style.top = pY - tbY - 16 + 'px';
  ele.style.left = pX - tbX - 6 + 'px';
  if (window.innerWidth - eleWidth < pX - tbX) {
    ele.style.left = 'unset';
    ele.style.right = 0;
  }
}
// 去更改列名
function renameCol($event) {
  showEditInput.value = false;
  showMenu.value = false;
  if (curTarget.value.colIdx === null) return;
  locateMenuOrEditInput('editInput', 200, $event); // 编辑框定位
  showEditInput.value = true;
  inputRef.value.focus();
}
// 更改单元格内容/列名
function updTbCellOrHeader(val) {
  if (!curTarget.value.isHead)
    // 更改单元格内容
    props.datas[curTarget.value.rowIdx][curColumn.value.prop] = val;
  else {
    // 更改列名
    if (!val) return;
    props.columnList[curTarget.value.colIdx].label = val;
    if (curTarget.value.colIdx != 0) {
      let valueProp = props.columnList[curTarget.value.colIdx].prop;
      props.datas.map((item) => {
        item[val] = item[valueProp];
        delete item[valueProp];
      });
      props.columnList[curTarget.value.colIdx].prop = val;
    }
  }
}
// 新增列
function addColumn(later) {
  showMenu.value = false;
  const idx = later ? curTarget.value.colIdx + 1 : curTarget.value.colIdx;
  const colStr = { prop: 'col_' + ++countCol.value, label: '' };
  props.columnList.splice(idx, 0, colStr);
  props.datas.forEach((p) => (p[colStr.prop] = ''));
}
// 删除行
function delRow() {
  showMenu.value = false;
  curTarget.value.rowIdx !== null && props.datas.splice(curTarget.value.rowIdx, 1);
}
// 新增行
function addRow(later) {
  showMenu.value = false;
  const idx = later ? curTarget.value.rowIdx + 1 : curTarget.value.rowIdx;
  let obj = {};
  props.columnList.forEach((p) => (obj[p.prop] = ''));
  props.datas.splice(idx, 0, obj);
}
// 删除列
function delColumn() {
  showMenu.value = false;
  props.datas.forEach((p) => {
    delete p[curColumn.value.prop];
  });
  props.columnList.splice(curTarget.value.colIdx, 1);
}
</script>

<style lang="scss" scoped>
.tb-container {
  position: relative;
}
#contextmenu {
  position: absolute;
  top: 0;
  left: 0;
  height: auto;
  width: 135px;
  border-radius: 3px;
  border: 1px solid #999999;
  background-color: #f4f4f4;
  padding: 10px;
  z-index: 12;
  button {
    display: block;
    margin: 0 0 5px;
  }
}
.hideContextMenu {
  position: absolute;
  top: 5px;
  right: 5px;
}
#editInput,
#headereditInput {
  position: absolute;
  top: 0;
  left: 0;
  height: auto;
  min-width: 200px;
  max-width: 400px;
  padding: 0;
  z-index: 12;
}
#editInput .el-input,
#headereditInput .el-input {
  outline: 0;
  border: 1px solid #c0f2f9;
  border-radius: 5px;
  box-shadow: 0px 0px 10px 0px #c0f2f9;
}
</style>
