forked from zhongyidian/super_video
430 lines
12 KiB
Markdown
430 lines
12 KiB
Markdown
# 🔄 技能树功能逻辑关系完整梳理
|
||
|
||
## 📊 核心状态管理
|
||
|
||
### 状态变量
|
||
|
||
```typescript
|
||
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. 文本搜索(叠加过滤)
|
||
```
|
||
|
||
#### 详细流程
|
||
|
||
```typescript
|
||
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 交互状态
|
||
|
||
### 标签高亮状态
|
||
|
||
```typescript
|
||
isTagSelected(moduleId, tagId) {
|
||
return selectedModuleId === moduleId && selectedTagId === tagId
|
||
}
|
||
```
|
||
|
||
### 标签展开状态
|
||
|
||
```typescript
|
||
isTagExpanded(tagId) {
|
||
return expandedTags.has(tagId)
|
||
}
|
||
```
|
||
|
||
### 子标签选中状态
|
||
|
||
```typescript
|
||
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,用户的每个操作都会立即反映在界面上。
|