Sidebar.vue 4.3 KB
<template>
  <nav
    class="max-[768px]:flex-[0] flex-shrink-0 scroll-container w-56 bg-white dark:bg-[#272929] shadow-lg h-[calc(100vh-4rem)] sticky top-16 overflow-y-auto transition-colors duration-300"
  >
    <div class="md:p-4 p-2">
      <h2 class="text-lg font-semibold mb-4 text-gray-700 dark:text-[#c6c9cf]">
        工具分类
      </h2>
      <ul id="menu" class="space-y-1">
        <li
          :id="`menu-${category.id}`"
          v-for="(category, index) in sortList"
          :key="index"
          class="menu-item"
        >
          <div
            class="w-full flex items-center justify-between p-3 text-[#515c6b] dark:text-[#c6c9cf] hover:text-[#5961f9] dark:hover:text-white rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors cursor-pointer"
          >
            <a
              :href="`#term-${category.id}`"
              @click="handleCategoryClick($event, category.id, category)"
              class="flex items-center space-x-2 flex-1"
            >
              <i
                class="iconfont text-sm"
                :class="[`icon-${category.icon}`]"
              ></i>
              <span class="text-sm">{{ category.label }}</span>
            </a>
            <button
              v-if="category.children && category.children.length > 0"
              @click.stop="toggleExpand(index)"
              class="p-1 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors"
              :aria-label="activeCategory === index ? '收起分类' : '展开分类'"
            >
              <el-icon
                size="14px"
                class="text-[#515c6b] dark:text-[#c6d9df] transition-transform duration-300"
                :class="{ 'rotate-90': activeCategory === index }"
              >
                <ArrowRightBold />
              </el-icon>
            </button>
          </div>

          <transition name="slide">
            <ul v-show="activeCategory === index" class="ml-4 space-y-0.5">
              <li
                v-for="(subItem, subIndex) in category.children"
                :key="subItem.id"
                :id="`menu-${category.id}-${subItem.id}`"
              >
                <a
                  :href="`#term-${category.id}-${subItem.id}`"
                  class="block text-sm py-2 px-3 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-[#515c6b] dark:text-[#c6c9cf] hover:text-[#5961f9] dark:hover:text-white transition-colors"
                  @click.stop="
                    handleSubCategoryClick($event, category.id, subItem.id)
                  "
                >
                  {{ subItem.label }}
                </a>
              </li>
            </ul>
          </transition>
        </li>
      </ul>
    </div>
  </nav>
</template>

<script setup lang="ts">
import type { classifyType } from "~/api/types/classify";
import { ArrowRightBold } from "@element-plus/icons-vue";
const sortList = useState<classifyType[]>("sortTree");
const activeCategory = ref<number | null>(0);
const route = useRoute();
const router = useRouter();
const config = useRuntimeConfig();
const { setActiveSubCategory } = useActiveSubCategory();

const toggleExpand = (index: number) => {
  if (activeCategory.value === index) {
    activeCategory.value = null;
  } else {
    activeCategory.value = index;
  }
};

const scrollToElement = (elementId: string) => {
  const element = document.getElementById(elementId);
  if (element) {
    element.scrollIntoView({
      behavior: "smooth",
      block: "center",
    });
    return true;
  }
  return false;
};

const handleCategoryClick = (
  event: Event,
  id: number,
  SubCategory: classifyType
) => {
  event.preventDefault();
  if (SubCategory.children && SubCategory.children.length > 0) {
    const targetChildId = `term-${id}-${SubCategory.children[0].id}`;
    setActiveSubCategory(targetChildId);
  } else {
    setActiveSubCategory(null);
  }
  const targetId = `term-${id}`;

  if (route.path === "/") {
    scrollToElement(targetId);
  } else {
    router.push(`/#${targetId}`);
  }
};

const handleSubCategoryClick = (
  event: Event,
  parentId: number,
  subId: number
) => {
  event.preventDefault();
  const targetId = `term-${parentId}-${subId}`;
  setActiveSubCategory(targetId);

  if (route.path === "/") {
    scrollToElement(targetId);
  } else {
    router.push(`/#${targetId}`);
  }
};
</script>