Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

362 lines
7.3 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="category-tree-container">
<!-- 搜索栏 -->
<view class="search-section" v-if="showSearch">
<view class="search-input" @click="handleSearchClick">
<text class="search-icon">🔍</text>
<text class="search-placeholder">搜索体系分类...</text>
</view>
</view>
<!-- 分类树 -->
<scroll-view
class="category-scroll"
scroll-y
:scroll-with-animation="true"
:show-scrollbar="false"
>
<!-- 空状态 -->
<empty-state
v-if="!loading && categories.length === 0"
:title="'暂无分类'"
:description="'暂无体系分类数据'"
:show-action="true"
:action-text="'刷新'"
@action="$emit('refresh')"
/>
<!-- 分类列表 -->
<view v-else class="category-list">
<view
v-for="category in categories"
:key="category.id"
class="category-item"
:class="{
'category-level-1': true,
'expanded': category.expanded,
'selected': selectedCategory?.id === category.id
}"
>
<!-- 一级分类 -->
<view
class="category-header"
@click="handleCategoryClick(category)"
>
<view class="category-info">
<text class="category-icon">📚</text>
<text class="category-name">{{ category.name }}</text>
<text class="category-count" v-if="category.children.length > 0">
({{ category.children.length }})
</text>
</view>
<view class="category-actions">
<!-- 展开/收起箭头 -->
<text
class="expand-icon"
v-if="category.children.length > 0"
@click.stop="toggleCategoryExpand(category)"
>
{{ category.expanded ? '▼' : '▶' }}
</text>
<!-- 选中标记 -->
<text
class="selected-icon"
v-if="selectedCategory?.id === category.id"
>
✓
</text>
</view>
</view>
<!-- 二级分类(子分类) -->
<view
class="subcategory-list"
v-if="category.expanded && category.children.length > 0"
>
<view
v-for="child in category.children"
:key="child.id"
class="subcategory-item"
:class="{
'category-level-2': true,
'selected': selectedCategory?.id === child.id
}"
@click="handleSubcategoryClick(child, category)"
>
<view class="subcategory-info">
<text class="subcategory-icon">📖</text>
<text class="subcategory-name">{{ child.name }}</text>
</view>
<text
class="selected-icon"
v-if="selectedCategory?.id === child.id"
>
✓
</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import EmptyState from '@/components/common/empty-state.vue'
export default {
name: 'CategoryTree',
components: {
EmptyState
},
props: {
// 分类数据
categories: {
type: Array,
default: () => []
},
// 选中的分类
selectedCategory: {
type: Object,
default: null
},
// 是否显示搜索
showSearch: {
type: Boolean,
default: true
},
// 加载状态
loading: {
type: Boolean,
default: false
}
},
data() {
return {
// 本地状态
}
},
methods: {
// 处理分类点击
handleCategoryClick(category) {
this.$emit('category-click', category)
},
// 处理子分类点击
handleSubcategoryClick(childCategory, parentCategory) {
this.$emit('subcategory-click', {
...childCategory,
parentCategory: {
id: parentCategory.id,
name: parentCategory.name
}
})
},
// 切换分类展开状态
toggleCategoryExpand(category) {
this.$emit('toggle-expand', {
...category,
expanded: !category.expanded
})
},
// 处理搜索点击
handleSearchClick() {
this.$emit('search-click')
}
}
}
</script>
<style scoped>
.category-tree-container {
display: flex;
flex-direction: column;
height: 100%;
background-color: #f8f8f8;
}
/* 搜索栏 */
.search-section {
padding: 20rpx 30rpx;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
}
.search-input {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
background-color: #f8f8f8;
border-radius: 50rpx;
border: 1rpx solid #e5e5e5;
}
.search-icon {
font-size: 32rpx;
margin-right: 16rpx;
color: #999;
}
.search-placeholder {
font-size: 28rpx;
color: #999;
flex: 1;
}
/* 分类滚动区域 */
.category-scroll {
flex: 1;
}
/* 分类列表 */
.category-list {
padding: 20rpx 0;
}
/* 一级分类 */
.category-item {
margin-bottom: 10rpx;
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
transition: all 0.3s ease;
}
.category-item.selected {
background-color: #f0f9ff;
border-left: 6rpx solid #3cc51f;
}
.category-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
cursor: pointer;
transition: all 0.3s ease;
}
.category-header:active {
background-color: #f8f8f8;
}
.category-info {
display: flex;
align-items: center;
flex: 1;
}
.category-icon {
font-size: 36rpx;
margin-right: 20rpx;
color: #3cc51f;
}
.category-name {
font-size: 32rpx;
font-weight: 600;
color: #333;
flex: 1;
}
.category-count {
font-size: 24rpx;
color: #999;
margin-left: 12rpx;
}
.category-actions {
display: flex;
align-items: center;
gap: 20rpx;
}
.expand-icon {
font-size: 24rpx;
color: #999;
padding: 10rpx;
transition: transform 0.3s ease;
}
.expand-icon:active {
color: #3cc51f;
}
.selected-icon {
font-size: 28rpx;
color: #3cc51f;
font-weight: bold;
}
/* 二级分类列表 */
.subcategory-list {
background-color: #fafafa;
border-top: 1rpx solid #f0f0f0;
padding: 0 30rpx;
}
.subcategory-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f0f0f0;
cursor: pointer;
transition: all 0.3s ease;
}
.subcategory-item:last-child {
border-bottom: none;
}
.subcategory-item:active {
background-color: #f5f5f5;
}
.subcategory-item.selected {
background-color: #f0f9ff;
}
.subcategory-info {
display: flex;
align-items: center;
flex: 1;
}
.subcategory-icon {
font-size: 28rpx;
margin-right: 20rpx;
color: #666;
}
.subcategory-name {
font-size: 28rpx;
color: #666;
flex: 1;
}
/* 动画效果 */
.category-item.expanded .expand-icon {
transform: rotate(90deg);
}
/* 悬停效果(在支持hover的设备上) */
@media (hover: hover) {
.category-header:hover {
background-color: #f8f8f8;
}
.subcategory-item:hover {
background-color: #f5f5f5;
}
.expand-icon:hover {
color: #3cc51f;
}
}
</style>