SettingsSidebar.vue 5.14 KB
<template>
  <nav :class="['settings-nav', { 'is-collapsed': isCollapsed }]">
    <div class="nav-group" v-for="(group, index) in navGroups" :key="group.key">
      <div class="nav-group-title" v-if="!isCollapsed">
        <span>{{ $t(`settingsSidebar.${group.key}`) }}</span>
        <!-- 在第一个分组标题显示切换按钮 -->
        <div
          v-if="index === 0"
          class="settings-toggle"
          @click.stop="$emit('toggle')"
        >
          <el-icon>
            <DArrowLeft />
          </el-icon>
        </div>
      </div>
      <div
        v-else
        :class="['nav-group-divider', { 'has-toggle': index === 0 }]"
      >
        <!-- 折叠状态下,在第一个分组位置显示展开按钮 -->
        <div
          v-if="index === 0"
          class="settings-toggle is-collapsed-toggle"
          @click.stop="$emit('toggle')"
        >
          <el-icon>
            <DArrowRight />
          </el-icon>
        </div>
      </div>
      <div
        v-for="item in group.items"
        :key="item.key"
        :class="['nav-item', { active: activeKey === item.key }]"
        @click="$emit('select', item.key)"
        :title="isCollapsed ? $t(`settingsSidebar.${item.key}`) : ''"
      >
        <img :src="getIconPath(item.key)" :alt="item.key" class="nav-icon" />
        <span v-if="!isCollapsed">{{ $t(`settingsSidebar.${item.key}`) }}</span>
      </div>
    </div>
  </nav>
</template>

<script setup lang="ts">
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { DArrowLeft, DArrowRight } from "@element-plus/icons-vue";

interface NavItem {
  key: string;
}

interface NavGroup {
  key: string;
  items: NavItem[];
}

defineProps<{
  activeKey: string;
  isCollapsed?: boolean;
}>();

defineEmits<{
  (e: "select", key: string): void;
  (e: "toggle"): void;
}>();

const { t: _t } = useI18n();

const navGroups = computed<NavGroup[]>(() => [
  {
    key: "workspaceGroup",
    items: [{ key: "general" }, { key: "shortcut" }], // 暂时隐藏 knowledge 和 ai
  },
  {
    key: "accountGroup",
    items: [
      { key: "profile" },
      { key: "authentication" },
      { key: "membersRecords" },
      { key: "memberReferral" },
      // { key: "accountBind" }, // 暂时隐藏 账号绑定
    ],
  },
]);

const getIconPath = (key: string): string => {
  const iconMap: Record<string, string> = {
    general: "/tongyongshezhi.svg",
    shortcut: "/kuaijiejianshezhi.svg",
    profile: "/personal-information.svg",
    authentication: "/anquan.svg",
    membersRecords: "/huiyuan1.svg",
    memberReferral: "/huiyuan2.svg",
    // apikey: "/miyue.svg",
    accountBind: "/shezhi.svg",
    // knowledge: "/zhishiku1.svg",
    // ai: "/AIzhushou.svg",
  };
  return iconMap[key] || "/shezhi.svg";
};
</script>

<style scoped lang="scss">
.settings-nav {
  width: 100%;
  height: 100%;
  background: var(--color-card);
  padding: 8px 0;
  box-sizing: border-box;
  overflow-y: auto;
}

.nav-group {
  margin-bottom: 32px;
}

.nav-group-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--color-secondary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 12px;
  padding: 0 16px 0 20px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 32px;
}

.settings-toggle {
  width: 32px;
  height: 31px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.2s;
  color: var(--color-text-secondary);
  flex-shrink: 0;

  &:hover {
    background: rgba(255, 255, 255, 0.05);
    color: var(--color-text);
  }
}

.nav-group-divider {
  height: 1px;
  background: var(--color-border);
  margin: 12px 16px;
  opacity: 0.5;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;

  &.has-toggle {
    height: 32px;
    background: transparent;
  }
}

.is-collapsed-toggle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 22px;
  cursor: pointer;
  transition: all 0.2s;
  margin-bottom: 4px;
  font-size: 14px;
  color: var(--color-text);
  white-space: nowrap;

  &:hover {
    background: var(--color-bag-hover);
  }

  &.active {
    background: var(--tab-selected);
    color: var(--color-text);
  }

  .nav-icon {
    width: 18px;
    height: 18px;
    object-fit: contain;
    flex-shrink: 0;
  }
}

.settings-nav.is-collapsed {
  .nav-group:first-child {
    margin-bottom: 12px;
  }

  .nav-group-divider.has-toggle {
    margin-top: 0;
    margin-bottom: 8px;
  }

  .nav-item {
    padding: 10px 0;
    justify-content: center;
    gap: 0;

    .nav-icon {
      width: 20px;
      height: 20px;
    }
  }
}

@media (max-width: 1024px) {
  .settings-nav {
    padding: 8px 0;
  }

  .nav-group {
    margin-bottom: 18px;
    padding: 0 8px;
  }

  .nav-group-title {
    font-size: 11px;
    margin-bottom: 6px;
    padding-left: 4px;
  }

  .nav-item {
    font-size: 12px;
    padding: 7px 8px;
    border-radius: 4px;
    gap: 8px;

    .nav-icon {
      width: 13px;
      height: 13px;
    }
  }
}
</style>