first commit
This commit is contained in:
189
src/components/CategoryBar/index.vue
Normal file
189
src/components/CategoryBar/index.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<view class="category-bar" :style="{ backgroundColor: bgColor }">
|
||||
<scroll-view
|
||||
scroll-x
|
||||
class="category-scroll"
|
||||
:scroll-left="scrollLeft"
|
||||
scroll-with-animation
|
||||
:show-scrollbar="false"
|
||||
:enhanced="true"
|
||||
>
|
||||
<view class="category-list">
|
||||
<!-- 最热 -->
|
||||
<view
|
||||
class="category-item"
|
||||
:class="{ active: activeType === 'hot' && !activeId }"
|
||||
@click="handleTypeClick('hot')"
|
||||
>
|
||||
<text class="category-text" :style="getTextStyle('hot', null)">最热</text>
|
||||
</view>
|
||||
<!-- 最新 -->
|
||||
<view
|
||||
class="category-item"
|
||||
:class="{ active: activeType === 'new' && !activeId }"
|
||||
@click="handleTypeClick('new')"
|
||||
>
|
||||
<text class="category-text" :style="getTextStyle('new', null)">最新</text>
|
||||
</view>
|
||||
<!-- 分类列表 -->
|
||||
<view
|
||||
v-for="item in categories.filter(cat => cat.id !== 1 && cat.name !== '全部')"
|
||||
:key="item.id"
|
||||
class="category-item"
|
||||
:class="{
|
||||
active: activeId === item.id
|
||||
}"
|
||||
@click="handleCategoryClick(item)"
|
||||
>
|
||||
<text class="category-text" :style="getTextStyle(null, item.id)">{{ item.name }}</text>
|
||||
</view>
|
||||
<!-- 右侧占位,防止被搜索按钮遮挡 -->
|
||||
<view class="category-placeholder" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<!-- 搜索区域 -->
|
||||
<view class="search-area" @click="handleSearch">
|
||||
<view class="search-gradient" />
|
||||
<view class="search-btn">
|
||||
<image class="search-icon" src="/static/icons/search.png" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getCategoryList } from '@/api/category'
|
||||
|
||||
const props = defineProps({
|
||||
bgColor: { type: String, default: 'transparent' },
|
||||
textColor: { type: String, default: 'rgba(241, 245, 249, 1)' },
|
||||
activeColor: { type: String, default: '#ffffff' }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['change', 'search'])
|
||||
|
||||
const categories = ref([])
|
||||
const activeId = ref(null)
|
||||
const activeType = ref('hot')
|
||||
const scrollLeft = ref(0)
|
||||
|
||||
const getTextStyle = (type, id) => {
|
||||
// 其他分类的激活判断
|
||||
const isActive = (type && activeType.value === type && !activeId.value) ||
|
||||
(id && activeId.value === id)
|
||||
return {
|
||||
color: isActive ? props.activeColor : props.textColor,
|
||||
fontWeight: isActive ? '600' : '400'
|
||||
}
|
||||
}
|
||||
|
||||
const handleTypeClick = (type) => {
|
||||
activeType.value = type
|
||||
activeId.value = null
|
||||
emit('change', { type, categoryId: null })
|
||||
}
|
||||
|
||||
const handleCategoryClick = (item) => {
|
||||
activeId.value = item.id
|
||||
activeType.value = null
|
||||
emit('change', { type: null, categoryId: item.id })
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
emit('search')
|
||||
}
|
||||
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
const res = await getCategoryList()
|
||||
console.log('分类数据:', res)
|
||||
// request已处理,成功时直接返回data.data
|
||||
categories.value = res || []
|
||||
console.log('categories.value:', categories.value)
|
||||
} catch (e) {
|
||||
console.error('获取分类失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchCategories()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.category-bar {
|
||||
width: 100%;
|
||||
padding: 12px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.category-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 隐藏滚动条 - 小程序兼容写法 */
|
||||
.category-scroll {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.category-list {
|
||||
display: inline-flex;
|
||||
padding: 0 16px;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
flex-shrink: 0;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.category-text {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.category-item.active .category-text {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.category-placeholder {
|
||||
width: 50px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-area {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 16px;
|
||||
padding-left: 16px;
|
||||
background: #09090b;
|
||||
}
|
||||
|
||||
.search-gradient {
|
||||
position: absolute;
|
||||
left: -30px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 30px;
|
||||
background: linear-gradient(to right, transparent, #09090b);
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user