<template>
  <view>
    <view style="padding: 10px 10px 0 10px" v-if="showSearch">
      <u-search
        v-model="state.value"
        :placeholder="placeholder"
        :clearable="false"
        :showAction="false"
        :input-style="{ background: 'transparent' }"
        margin="20rpx 0rpx 0"
        height="40"
        @change="changeEvt"
        bg-color="rgb(184, 207, 240, .1)"
      >
      </u-search>
    </view>
    <view style="padding: 10px" v-if="filteredTreeData.length">
      <scroll-view :style="{ height: treeHeight }" scroll-y="true">
        <view class="w-tree">
          <w-tree-item
            v-model:treeData="filteredTreeData"
            :propsConfig="propsConfig"
            @clickNode="clickNode"
          ></w-tree-item>
        </view>
      </scroll-view>
    </view>
    <view v-else class="center my--20 c999"> 未查到数据 </view>
  </view>
</template>

<script setup lang="ts">
import { reactive, ref, watch, onMounted } from "vue";

// 定义树节点的类型
interface TreeNode {
  [key: string]: any; // 允许任意属性
  children?: TreeNode[];
  _isOpen?: boolean;
  _isCurrent?: boolean; // 新增：表示节点是否被选中
}

// 定义组件的 Props 类型
interface Props {
  treeData: TreeNode[];
  placeholder?: string;
  activeColor?: string;
  treeHeight?: string;
  showSearch?: boolean;
  propsConfig?: {
    children: string;
    label: string;
    value: string;
  };
}

// 定义默认的 Props
const props = withDefaults(defineProps<Props>(), {
  treeData: () => [],
  placeholder: "输入搜索",
  activeColor: "#1a74dc",
  treeHeight: "600px",
  showSearch: true,
  propsConfig: () => ({
    children: "children",
    label: "name",
    value: "code",
  }),
});

// 定义 Emits 事件
const emit = defineEmits<{
  (event: "clickNode", item: TreeNode): void;
}>();

// 定义组件的状态
const state = reactive<{
  value: string;
}>({
  value: "",
});

// 根据搜索值过滤树数据
const filteredTreeData = ref<TreeNode[]>([...props.treeData]);

// 点击树节点事件
function clickNode(item: TreeNode) {
  clearCurrent(filteredTreeData.value); // 清除所有节点的选中状态
  item._isCurrent = true; // 设置当前节点为选中状态
  emit("clickNode", item); // 触发点击事件
}

// 递归过滤组成新树
function multiFilterTree(arr: TreeNode[], val: string): TreeNode[] {
  if (!val) return arr;
  // 深拷贝数组
  const clonedArr: TreeNode[] = JSON.parse(JSON.stringify(arr));
  return clonedArr.filter((item) => {
    if (item[props.propsConfig.children]?.length) {
      item[props.propsConfig.children] = multiFilterTree(
        item[props.propsConfig.children],
        val
      );
      return (
        item[props.propsConfig.children].length ||
        item[props.propsConfig.label].includes(val)
      );
    }
    return item[props.propsConfig.label].includes(val);
  });
}

// 递归设置树的展开/关闭状态(展开时非叶节点加_isOpen属性，关闭时_isOpen属性都为false)
function toggleTreeState(tree: TreeNode[], _isOpen: boolean) {
  tree.forEach((item) => {
    !_isOpen && (item._isOpen = _isOpen);
    if (item[props.propsConfig.children]?.length) {
      _isOpen && (item._isOpen = _isOpen);
      toggleTreeState(item[props.propsConfig.children], _isOpen);
    }
  });
}

// 关闭所有
function collapseAll() {
  toggleTreeState(filteredTreeData.value, false);
}
// 展开所有
function expandAll() {
  toggleTreeState(filteredTreeData.value, true);
}

// 搜索框值变化事件
function changeEvt(val: string) {
  state.value = val;
}

// 清除所有节点的选中状态
function clearCurrent(tree: TreeNode[]) {
  tree.forEach((item) => {
    item._isCurrent = false; // 取消当前节点的选中状态
    if (item[props.propsConfig.children]?.length) {
      clearCurrent(item[props.propsConfig.children]); // 递归处理子节点
    }
  });
}

watch(filteredTreeData, (newVal) => {
  expandAll();
});

watch(
  [() => props.treeData, state],
  ([newVal, newState], [oldVal, oldState]) => {
    filteredTreeData.value = multiFilterTree(newVal, newState.value);
  },
  { immediate: true }
);

// 组件挂载时展开所有节点
onMounted(() => {
  expandAll();
});

// 暴露方法给父组件
defineExpose({
  collapseAll,
  expandAll,
});
</script>

<style lang="scss" scoped>
.w-tree {
  :deep(.w-tree-node) {
    position: relative;
    padding: 3px 0px;
    padding-left: 10px; // 缩进量
    color: black;
  }

  :deep(.w-tree-node__children) {
    padding-left: 10px; // 缩进量
  }
  // 竖线
  :deep(.w-tree-node::before) {
    content: "";
    height: 100%;
    width: 1px;
    position: absolute;
    left: -4px;
    top: -10px;
    border-width: 1px;
    border-left: 1px dashed #88909c;
  }
  // 当前层最后一个节点的竖线高度固定
  :deep(.w-tree-node:last-child::before) {
    height: 28px; // 可以自己调节到合适数值
  }
  // 横线
  :deep(.w-tree-node::after) {
    content: "";
    width: 15px;
    height: 20px;
    position: absolute;
    left: -4px;
    top: 17px;
    border-width: 1px;
    border-top: 1px dashed #88909c;
  }
  // 去掉最顶层的虚线，放最下面样式才不会被上面的覆盖了
  & > :deep(.w-tree-node::after) {
    border-top: none;
  }
  & > :deep(.w-tree-node::before) {
    border-left: none;
  }

  :deep(.w-tree-node__content) {
    display: flex;
    line-height: 28px;
    padding-left: 0 !important;
    color: #444;
    // 树展开图标样式
    .w-tree-node__expand-icon.expanded::before {
      content: "";
      width: 14px;
      height: 14px;
      background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24'><path fill='gray' d='M12 20c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2M7 13h10v-2H7'/></svg>");
      background-size: 100% 100%;
      border-radius: 3px;

      display: inline-block;
    }
    &.is-current {
      color: v-bind(activeColor);
      text-shadow: 2px 2px 4px rgb(121 133 165 / 68%);
    }
    // 树收缩图标样式
    .w-tree-node__expand-icon::before {
      content: "";
      width: 14px;
      height: 14px;
      background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24'><path fill='gray' d='M12 20c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m1 5h-2v4H7v2h4v4h2v-4h4v-2h-4V7Z'/></svg>");
      background-size: 100% 100%;
      border-radius: 3px;
      display: inline-block;
    }
  }
}
</style>
