<template>
  <div class="card filter">
    <h4 v-if="title" class="title sle">
      {{ title }}
    </h4>
    <el-input v-model="filterText" placeholder="输入关键字进行过滤" clearable />
    <el-scrollbar :style="{ height: title ? `calc(100% - 95px)` : `calc(100% - 56px)` }">
      <el-tree
        ref="treeRef"
        :node-key="id"
        :data="multiple ? treeData : treeAllData"
        :show-checkbox="multiple"
        :check-strictly="true"
        :current-node-key="!multiple ? selected : ''"
        :highlight-current="!multiple"
        :expand-on-click-node="false"
        :check-on-click-node="multiple"
        :props="defaultProps"
        :filter-node-method="filterNode"
        :default-checked-keys="multiple ? selected : []"
        @node-click="handleNodeClick"
        @check="handleCheck"
        @check-change="handleCheckChange"
      >
        <template #default="scope">
          <span class="el-tree-node__label">
            <slot :row="scope">
              {{ scope.node.label }}
            </slot>
          </span>
        </template>
      </el-tree>
    </el-scrollbar>
  </div>
</template>

<script setup lang="ts" name="TreeFilter">
import { ref, watch, onBeforeMount, nextTick } from "vue";
import { ElTree } from "element-plus";
import { other } from "@/api/interface";

// 接收父组件参数并设置默认值
interface TreeFilterProps {
  requestApi?: (data?: any) => Promise<any>; // 请求分类数据的 api ==> 非必传
  data?: { [key: string]: any }[]; // 分类数据，如果有分类数据，则不会执行 api 请求 ==> 非必传
  title?: string; // treeFilter 标题 ==> 非必传
  id?: string; // 选择的id ==> 非必传，默认为 “id”
  label?: string; // 显示的label ==> 非必传，默认为 “label”
  multiple?: boolean; // 是否为多选 ==> 非必传，默认为 false
  defaultValue?: any; // 默认选中的值 ==> 非必传
}
const props = withDefaults(defineProps<TreeFilterProps>(), {
  id: "id",
  label: "label",
  multiple: false
});

const defaultProps = {
  children: "children",
  label: props.label
};

const treeRef = ref<InstanceType<typeof ElTree>>();
const treeData = ref<{ [key: string]: any }[]>([]);
const treeAllData = ref<{ [key: string]: any }[]>([]);

const selected = ref();
const setSelected = () => {
  if (props.multiple) selected.value = Array.isArray(props.defaultValue) ? props.defaultValue : [props.defaultValue];
  else selected.value = typeof props.defaultValue === "string" ? props.defaultValue : "";
};

onBeforeMount(async () => {
  setSelected();
  if (props.requestApi) {
    const { data } = await props.requestApi!();
    treeData.value = buildTree(data);

    treeAllData.value = [{ id: "", [props.label]: "全部" }, ...data];
  }
});
const buildTree = (data: other.tableData1[]) => {
  const tree: other.tableData1[] = [];
  const map: { [key: string]: other.tableData1 } = {};

  // 创建一个map以便快速找到item
  data.forEach((item: other.tableData1) => {
    map[item.id] = { ...item, children: [] };
  });

  data.forEach((item: { pId: string; id: string | number }) => {
    if (item.pId === "0") {
      // 根节点，假设根节点的 pId 是空字符串或者其他标识
      tree.push(map[item.id]);
    } else {
      // 子节点
      if (map[item.pId]) {
        map[item.pId].children.push(map[item.id]);
      }
    }
  });

  return tree;
};
// 使用 nextTick 防止打包后赋值不生效，开发环境是正常的
watch(
  () => props.defaultValue,
  () => nextTick(() => setSelected()),
  { deep: true, immediate: true }
);

watch(
  () => props.data,
  () => {
    if (props.data?.length) {
      treeData.value = props.data;
      treeAllData.value = [{ id: "", [props.label]: "全部" }, ...props.data];
    }
  },
  { deep: true, immediate: true }
);

const filterText = ref("");
watch(filterText, val => {
  treeRef.value!.filter(val);
});

// 过滤
const filterNode = (value: string, data: { [key: string]: any }, node: any) => {
  if (!value) return true;
  let parentNode = node.parent,
    labels = [node.label],
    level = 1;
  while (level < node.level) {
    labels = [...labels, parentNode.label];
    parentNode = parentNode.parent;
    level++;
  }
  return labels.some(label => label.indexOf(value) !== -1);
};

// emit
const emit = defineEmits<{
  change: [value: any];
}>();

// 单选
const handleNodeClick = (data: { [key: string]: any }) => {
  if (props.multiple) return;
  emit("change", data[props.id]);
};

// 多选
// const handleCheckChange = () => {
//   emit("change", treeRef.value?.getCheckedKeys());
// };
const selectChildren = (data: { id?: string | undefined; children?: any[] | undefined }) => {
  data &&
    data.children &&
    data.children.map((item: { id: string; children?: any[] }) => {
      treeRef.value?.setChecked(item.id, true, false);
      if (data.children) {
        selectChildren(item);
      }
    });
};
const handleCheck = (data: { id?: string; children?: any[] }, { checkedKeys }: any) => {
  // 节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点
  //如果为取消
  console.log(data);
  console.log(checkedKeys);
  console.log(checkedKeys.includes(data.id));

  if (checkedKeys.includes(data.id)) {
    //如果当前节点有子集
    selectChildren(data);
  }
};
const handleCheckChange = (data: { children: any[]; pId: number }, checked: boolean) => {
  // 节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点
  //如果为取消
  if (checked === false) {
    //如果当前节点有子集
    if (data.children) {
      //循环子集将他们的选中取消
      data.children.map(item => {
        treeRef.value?.setChecked(item.id, false, false);
      });
    }
  } else {
    //否则(为选中状态)
    //判断父节点id是否为空
    if (data.pId !== 0) {
      //如果不为空则将其选中
      treeRef.value?.setChecked(data.pId, true, false);
    }
  }

  // var check treeRef: { value: { setChecked: (arg0: number, arg1: boolean) => void; }; },treeRef.value?.getCheckedNodes();
  // let check = treeRef.value?.getCheckedNodes();
  // let checks = treeRef.value?.getCheckedKeys();
  // console.log(checks);
  emit("change", treeRef.value?.getCheckedKeys());
};
// 暴露给父组件使用
defineExpose({ treeData, treeAllData, treeRef });
</script>

<style scoped lang="scss">
@import "./index.scss";
</style>
