Files
super_video/SKILL_LOGIC_FLOW.md

12 KiB
Raw Permalink Blame History

🔄 技能树功能逻辑关系完整梳理

📊 核心状态管理

状态变量

selectedModuleId: string | null // 当前选中的模块ID(null = 总览模式)
selectedTagId: string | null // 当前选中的标签ID
selectedSubTagId: string | null // 当前选中的子标签ID
expandedTags: Set<string> // 展开的标签集合
search: string // 搜索文本

🎯 功能模块详解

1. 模块切换与技能树标题

逻辑流程

用户点击模块图标
  ↓
handleModuleListItemClick(moduleId)
  ↓
如果点击的是已选中的模块 → 取消选择(回到总览)
如果点击的是新模块 → 选中该模块
  ↓
更新 selectedModuleId
清空 selectedTagId、selectedSubTagId
清空 expandedTags
  ↓
技能树标题更新:
  - selectedModuleId = null → "技能树总览"
  - selectedModuleId = "module-1" → "新手导航"
  - selectedModuleId = "module-2" → "vlog发现与记录"
  - selectedModuleId = "module-3" → "访谈聆听与对话"
  - selectedModuleId = "module-4" → "电影创作与表达"

实现位置

  • SkillTree.vue: getTreeTitle() 函数
  • index.vue: handleModuleListItemClick() 函数

2. 标签点击逻辑(核心)

逻辑分支

用户点击技能树标签
  ↓
handleTagClick(moduleId, tagId)
  ↓
检查是否有子标签
  ↓
┌─────────────────────────────────────────────────────────┐
│ 分支A: 有子标签                                          │
├─────────────────────────────────────────────────────────┤
│ 如果点击的是已选中的标签:                                │
│   - 已展开 → 折叠(删除 tagId from expandedTags)        │
│   - 未展开 → 展开(添加 tagId to expandedTags)          │
│                                                          │
│ 如果点击的是新标签:                                      │
│   - 设置 selectedModuleId = moduleId                    │
│   - 设置 selectedTagId = tagId                          │
│   - 清空 selectedSubTagId = null                        │
│   - 自动展开(添加 tagId to expandedTags)               │
│                                                          │
│ 结果:展开/折叠子节点,不触发搜索                          │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ 分支B: 无子标签                                          │
├─────────────────────────────────────────────────────────┤
│ - 设置 selectedModuleId = moduleId                      │
│ - 设置 selectedTagId = tagId                            │
│ - 清空 selectedSubTagId = null                          │
│ - 清空 expandedTags                                     │
│                                                          │
│ 结果:高亮标签 + 触发搜索,其他标签保持可见                │
└─────────────────────────────────────────────────────────┘

关键特性

  • ✅ 有子标签:只展开/折叠,不搜索
  • ✅ 无子标签:高亮并搜索,不隐藏其他标签
  • ✅ 其他标签始终可见(不会因为选择而隐藏)

实现位置

  • index.vue: handleTagClick() 函数

3. 子标签点击逻辑

逻辑流程

用户点击子标签
  ↓
handleSubTagClick(moduleId, tagId, subTagId)
  ↓
如果点击的是已选中的子标签 → 取消选择
如果点击的是新子标签 → 选中该子标签
  ↓
更新状态:
  - selectedModuleId = moduleId
  - selectedTagId = tagId
  - selectedSubTagId = subTagId
  - 确保父标签展开(添加 tagId to expandedTags)
  ↓
触发资源过滤(显示该子标签的资源)

实现位置

  • index.vue: handleSubTagClick() 函数

4. 资源过滤逻辑

过滤优先级

1. 子标签过滤(最精确)
   ↓
2. 标签过滤
   ↓
3. 模块过滤
   ↓
4. 全部资源(默认)
   ↓
5. 文本搜索(叠加过滤)

详细流程

filteredResources = computed(() => {
  let resources = []

  // 第一步:根据选择获取资源
  if (selectedSubTagId) {
    resources = getResourcesBySubTag(moduleId, tagId, subTagId)
  } else if (selectedTagId) {
    resources = getResourcesByTag(moduleId, tagId)
  } else if (selectedModuleId) {
    resources = getResourcesByModule(moduleId)
  } else {
    resources = getAllResources()
  }

  // 第二步:文本搜索(如果有搜索文本)
  if (search.trim()) {
    resources = resources.filter(
      r =>
        r.title.includes(search) ||
        r.moduleName.includes(search) ||
        r.tagName.includes(search) ||
        r.description?.includes(search)
    )
  }

  return resources
})

搜索字段

  • ✅ title - 资源标题
  • ✅ moduleName - 模块名称
  • ✅ tagName - 标签名称
  • ✅ description - 资源描述

实现位置

  • index.vue: filteredResources 计算属性

5. 搜索标签(面包屑)

显示逻辑

searchTags = computed(() => {
  const tags = []

  if (selectedModuleId) {
    tags.push({ type: 'module', name: moduleName })
  }

  if (selectedTagId) {
    tags.push({ type: 'tag', name: tagName })
  }

  if (selectedSubTagId) {
    tags.push({ type: 'subTag', name: subTagName })
  }

  return tags
})

标签样式

  • 模块标签:蓝色(bg-blue-500/80)
  • 标签标签:橙色(bg-orange-500/80)
  • 子标签标签:紫色(bg-purple-500/80)

删除功能

用户点击标签的 × 按钮
  ↓
handleRemoveTag(tagType)
  ↓
根据 tagType 清除对应状态:
  - 'subTag' → 清除 selectedSubTagId
  - 'tag' → 清除 selectedTagId + selectedSubTagId + expandedTags
  - 'module' → 清除所有选择状态
  ↓
资源列表自动更新

实现位置

  • index.vue: searchTags 计算属性, handleRemoveTag() 函数
  • SearchTags.vue: 标签显示和删除按钮

6. 文本搜索

搜索框行为

用户在搜索框输入文本
  ↓
v-model 绑定到 search 变量
  ↓
filteredResources 自动重新计算
  ↓
资源列表实时更新

搜索特性

  • ✅ 实时搜索(无需点击搜索按钮)
  • ✅ 多字段搜索(title、moduleName、tagName、description)
  • ✅ 大小写不敏感
  • ✅ 叠加过滤(在已选择的基础上进一步过滤)
  • ✅ 搜索文本为空时,不影响选择过滤

实现位置

  • index.vue: 搜索框 v-model="search"

🎨 UI 交互状态

标签高亮状态

isTagSelected(moduleId, tagId) {
  return selectedModuleId === moduleId && selectedTagId === tagId
}

标签展开状态

isTagExpanded(tagId) {
  return expandedTags.has(tagId)
}

子标签选中状态

isSubTagSelected(moduleId, tagId, subTagId) {
  return selectedModuleId === moduleId &&
         selectedTagId === tagId &&
         selectedSubTagId === subTagId
}

📈 数据流向图

用户操作
  ↓
┌─────────────────────────────────────────────────────────┐
│ 点击模块图标 → handleModuleListItemClick                 │
│ 点击标签 → handleTagClick                                │
│ 点击子标签 → handleSubTagClick                           │
│ 输入搜索文本 → v-model:search                            │
│ 删除面包屑标签 → handleRemoveTag                         │
└─────────────────────────────────────────────────────────┘
  ↓
更新状态变量
  - selectedModuleId
  - selectedTagId
  - selectedSubTagId
  - expandedTags
  - search
  ↓
触发计算属性重新计算
  - searchTags(面包屑)
  - filteredResources(资源列表)
  - displayedTags(技能树标签)
  - displayedModules(显示的模块)
  ↓
UI 自动更新
  - 技能树标题
  - 标签高亮状态
  - 子节点展开/折叠
  - 面包屑标签
  - 资源列表

🔍 关键场景示例

场景1:从总览到具体资源

1. 初始状态:总览模式
   - selectedModuleId = null
   - 显示 "技能树总览"
   - 显示所有模块的标签(去重)

2. 点击 "新手导航" 模块
   - selectedModuleId = "module-1"
   - 显示 "新手导航"
   - 只显示新手导航的标签

3. 点击 "视觉素养" 标签(有子标签)
   - selectedTagId = "tag-1-1"
   - 展开子标签:构图、光线、运镜
   - 资源列表显示视觉素养的所有资源

4. 点击 "构图" 子标签
   - selectedSubTagId = "subtag-1-1-1"
   - 资源列表只显示构图相关资源
   - 面包屑:新手导航 > 视觉素养 > 构图

场景2:搜索功能

1. 在搜索框输入 "构图"
   - search = "构图"
   - 资源列表实时过滤,显示所有包含"构图"的资源

2. 点击面包屑标签的删除按钮
   - 清除对应的选择
   - 资源列表更新

3. 清空搜索框
   - search = ""
   - 资源列表恢复到选择过滤的结果

场景3:标签点击行为

有子标签的标签(如"视觉素养"):
  - 第一次点击 → 展开子节点
  - 第二次点击 → 折叠子节点
  - 不触发搜索,其他标签保持可见

无子标签的标签(如"摄影基础"):
  - 点击 → 高亮标签 + 过滤资源
  - 其他标签保持可见
  - 可以继续点击其他标签

✅ 功能清单

已实现功能

  • ✅ 模块切换(总览 ↔ 具体模块)
  • ✅ 技能树标题动态更新
  • ✅ 标签点击逻辑(有/无子标签分支)
  • ✅ 子标签展开/折叠
  • ✅ 标签高亮显示
  • ✅ 资源多级过滤(模块 → 标签 → 子标签)
  • ✅ 文本搜索(多字段)
  • ✅ 面包屑导航
  • ✅ 标签删除功能
  • ✅ 实时搜索
  • ✅ 标签去重(总览模式)

核心特性

  • 🎯 智能标签点击:根据是否有子标签自动选择行为
  • 🔍 多维度搜索:选择过滤 + 文本搜索
  • 🏷️ 面包屑导航:清晰显示当前位置
  • 🗑️ 灵活删除:可以删除任意层级的选择
  • 👁️ 保持可见:选择标签后其他标签不隐藏

🎯 总结

这是一个多层级、多维度的资源浏览和搜索系统,核心特点:

  1. 灵活的导航:支持模块 → 标签 → 子标签的层级导航
  2. 智能的交互:根据标签类型自动选择展开或搜索
  3. 强大的搜索:选择过滤 + 文本搜索双重过滤
  4. 清晰的反馈:面包屑、高亮、展开状态等多种视觉反馈
  5. 用户友好:可以随时删除选择,标签始终可见

整个系统通过 Vue 的响应式系统自动同步状态和 UI,用户的每个操作都会立即反映在界面上。