338 lines
8.7 KiB
Vue
338 lines
8.7 KiB
Vue
<!-- components/CategoryDropdown.vue -->
|
||
<template>
|
||
<view class="category-dropdown">
|
||
<view class="select-box" @click="toggleDropdown">
|
||
<text class="selected-text">{{ displayText }}</text>
|
||
<view class="arrow" :class="{ 'arrow-up': showDropdown }"></view>
|
||
</view>
|
||
|
||
<view class="dropdown-list" v-if="showDropdown">
|
||
<scroll-view scroll-y style="max-height: 400rpx;">
|
||
<!-- 调试信息 -->
|
||
<view v-if="categories.length === 0" class="dropdown-item">
|
||
<text>加载中或无数据({{loading ? '加载中' : '无数据'}})</text>
|
||
</view>
|
||
|
||
<!-- 一级分类 -->
|
||
<view v-if="currentLevel === 1">
|
||
<view
|
||
v-for="(item, index) in categories"
|
||
:key="index"
|
||
class="dropdown-item"
|
||
@click="selectLevel1(item)"
|
||
>
|
||
<text>{{ item.name }}</text>
|
||
<text v-if="item.children && item.children.length" class="arrow-right">›</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 二级分类 -->
|
||
<view v-else-if="currentLevel === 2">
|
||
<view class="dropdown-header" @click="backToLevel1">
|
||
<text class="back-icon">‹</text>
|
||
<text>返回</text>
|
||
</view>
|
||
<view
|
||
v-for="(item, index) in currentParent.children"
|
||
:key="index"
|
||
class="dropdown-item"
|
||
@click="selectLevel2(item)"
|
||
>
|
||
<text>{{ item.name }}</text>
|
||
<text v-if="item.children && item.children.length" class="arrow-right">›</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 三级分类 -->
|
||
<view v-else-if="currentLevel === 3">
|
||
<view class="dropdown-header" @click="backToLevel2">
|
||
<text class="back-icon">‹</text>
|
||
<text>返回</text>
|
||
</view>
|
||
<view
|
||
v-for="(item, index) in currentParent.children"
|
||
:key="index"
|
||
class="dropdown-item"
|
||
@click="selectCategory(item)"
|
||
>
|
||
<text>{{ item.name }}</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getCategory } from '@/service/categoryService.js';
|
||
|
||
export default {
|
||
name: 'CategoryDropdown',
|
||
props: {
|
||
placeholder: {
|
||
type: String,
|
||
default: '请选择分类'
|
||
},
|
||
value: {
|
||
type: [Object, String, Number],
|
||
default: null
|
||
},
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
externalCategories: {
|
||
type: Array,
|
||
default: () => []
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
categories: [],
|
||
selectedCategory: null,
|
||
showDropdown: false,
|
||
loading: false,
|
||
currentLevel: 1,
|
||
currentParent: null,
|
||
level1Selection: null,
|
||
level2Selection: null
|
||
};
|
||
},
|
||
computed: {
|
||
displayText() {
|
||
if (this.selectedCategory) {
|
||
let text = this.selectedCategory.name;
|
||
// 如果有上级分类,添加路径
|
||
if (this.level2Selection) {
|
||
text = `${this.level2Selection.name} > ${text}`;
|
||
}
|
||
if (this.level1Selection) {
|
||
text = `${this.level1Selection.name} > ${text}`;
|
||
}
|
||
return text;
|
||
}
|
||
return this.placeholder;
|
||
}
|
||
},
|
||
watch: {
|
||
// 监听外部传入的分类数据变化
|
||
externalCategories: {
|
||
handler(newVal) {
|
||
if (newVal && newVal.length > 0) {
|
||
console.log('从外部接收到分类数据:', newVal);
|
||
this.categories = newVal;
|
||
console.log('设置分类数据完成,当前categories:', this.categories);
|
||
}
|
||
},
|
||
immediate: true,
|
||
deep: true
|
||
}
|
||
},
|
||
created() {
|
||
// 如果没有外部传入的分类数据,才自动获取
|
||
if (!this.externalCategories || this.externalCategories.length === 0) {
|
||
this.fetchCategories();
|
||
}
|
||
|
||
// 如果有初始值,设置选中项
|
||
if (this.value) {
|
||
this.selectedCategory = this.value;
|
||
}
|
||
},
|
||
methods: {
|
||
async fetchCategories(cookiesParam) {
|
||
this.loading = true;
|
||
try {
|
||
// 如果没有传入cookies,从本地存储获取
|
||
const cookies = cookiesParam || uni.getStorageSync('cookies');
|
||
console.log('正在获取分类数据,cookies:', cookies);
|
||
|
||
const result = await getCategory(cookies);
|
||
console.log('获取到的分类数据:', result);
|
||
|
||
if (result) {
|
||
// 处理不同的数据格式情况
|
||
if (result.successResponse && Array.isArray(result.successResponse)) {
|
||
this.categories = result.successResponse;
|
||
console.log('成功解析分类数据,数量:', this.categories);
|
||
} else if (Array.isArray(result)) {
|
||
this.categories = result;
|
||
console.log('成功解析分类数据(数组格式),数量:', this.categories.length);
|
||
} else {
|
||
console.error('分类数据格式不正确:', result);
|
||
uni.showToast({
|
||
title: '分类数据格式不正确',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
}
|
||
} else {
|
||
console.error('未获取到分类数据');
|
||
uni.showToast({
|
||
title: '获取分类列表失败',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('获取分类失败:', error);
|
||
uni.showToast({
|
||
title: '获取分类列表失败',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
toggleDropdown() {
|
||
if (this.loading || this.disabled) return;
|
||
|
||
// 添加调试信息
|
||
console.log('toggleDropdown被触发,当前categories:', this.categories);
|
||
console.log('当前categories长度:', this.categories.length);
|
||
|
||
if (!this.showDropdown) {
|
||
// 重置当前级别为1
|
||
this.currentLevel = 1;
|
||
this.currentParent = null;
|
||
}
|
||
this.showDropdown = !this.showDropdown;
|
||
},
|
||
selectLevel1(category) {
|
||
if (category.children && category.children.length) {
|
||
this.level1Selection = category;
|
||
this.currentParent = category;
|
||
this.currentLevel = 2;
|
||
} else {
|
||
this.selectCategory(category);
|
||
}
|
||
},
|
||
selectLevel2(category) {
|
||
if (category.children && category.children.length) {
|
||
this.level2Selection = category;
|
||
this.currentParent = category;
|
||
this.currentLevel = 3;
|
||
} else {
|
||
this.selectCategory(category);
|
||
}
|
||
},
|
||
selectCategory(category) {
|
||
this.selectedCategory = category;
|
||
this.showDropdown = false;
|
||
// 向父组件发送选中的值
|
||
this.$emit('input', category);
|
||
this.$emit('change', category);
|
||
},
|
||
backToLevel1() {
|
||
this.currentLevel = 1;
|
||
this.currentParent = null;
|
||
this.level2Selection = null;
|
||
},
|
||
backToLevel2() {
|
||
this.currentLevel = 2;
|
||
this.currentParent = this.level1Selection;
|
||
},
|
||
// 关闭下拉框,用于点击外部区域时调用
|
||
closeDropdown() {
|
||
this.showDropdown = false;
|
||
},
|
||
// 添加一个手动刷新分类数据的方法
|
||
refreshCategories(cookies) {
|
||
console.log('手动刷新分类数据,cookies:', cookies);
|
||
this.fetchCategories(cookies);
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.category-dropdown {
|
||
position: relative;
|
||
width: 100%;
|
||
z-index: 10;
|
||
}
|
||
|
||
.select-box {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 80rpx;
|
||
padding: 0 20rpx;
|
||
background-color: #ffffff;
|
||
border: 1px solid #e5e5e5;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.selected-text {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
max-width: 90%;
|
||
}
|
||
|
||
.arrow {
|
||
width: 0;
|
||
height: 0;
|
||
border-left: 12rpx solid transparent;
|
||
border-right: 12rpx solid transparent;
|
||
border-top: 12rpx solid #666;
|
||
transition: transform 0.3s;
|
||
}
|
||
|
||
.arrow-up {
|
||
transform: rotate(180deg);
|
||
}
|
||
|
||
.dropdown-list {
|
||
position: absolute;
|
||
top: 90rpx;
|
||
left: 0;
|
||
width: 100%;
|
||
background-color: #fff;
|
||
border: 1px solid #e5e5e5;
|
||
border-radius: 8rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||
z-index: 11;
|
||
}
|
||
|
||
.dropdown-item {
|
||
padding: 20rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.dropdown-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.dropdown-item:active {
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.arrow-right {
|
||
font-size: 36rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.dropdown-header {
|
||
padding: 20rpx;
|
||
font-size: 28rpx;
|
||
color: #007AFF;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: #f8f8f8;
|
||
}
|
||
|
||
.back-icon {
|
||
font-size: 36rpx;
|
||
margin-right: 10rpx;
|
||
}
|
||
</style> |