Files
YGChatCS/src/pages-quick/components/Tabs/index.vue
2026-03-20 00:25:12 +08:00

134 lines
3.5 KiB
Vue
Raw 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="tab-container relative">
<view class="tab-wrapper flex flex-items-center">
<view v-for="(item, index) in tabList" :key="item.id" :class="[
'tab-item flex flex-items-center flex-justify-center relative',
activeIndex === index && 'tab-item-active',
]" @click="handleTabClick(index)">
<view class="tab-item-inner flex flex-items-center">
<uni-icons :class="['icon mr-4', activeIndex === index && 'icon-active']" fontFamily="znicons" size="20" color="opacity">
{{ zniconsMap[item.iconCode] }}
</uni-icons>
<text :class="[
'font-size-16 font-500 color-525866 ',
activeIndex === index && 'tab-text-active',
]">
{{ item.iconTitle }}
</text>
</view>
<!-- 每项内的下划线指示器通过类控制显示/隐藏 -->
<view class="tab-item-indicator" :class="{ visible: activeIndex === index }"></view>
</view>
</view>
</view>
</template>
<script setup>
import { onMounted, ref, watch } from "vue";
import { zniconsMap } from "@/static/fonts/znicons";
import { commodityTypePageList } from "@/request/api/GoodsApi";
// Props
const props = defineProps({
tabs: {
type: Array,
default: () => [
{ iconTitle: "客房", typeCode: "0", iconCode: "zn-nav-room" },
{ iconTitle: "门票", typeCode: "1", iconCode: "zn-nav-ticket" },
{ iconTitle: "餐食", typeCode: "2", iconCode: "zn-nav-meal" },
{ iconTitle: "套餐", typeCode: "3", iconCode: "zn-package" },
{ iconTitle: "文创", typeCode: "4", iconCode: "zn-package" },
],
},
defaultActive: {
type: Number,
default: 0,
},
indicatorColor: {
type: String,
default: "#1890ff"
},
});
// Emits
const emit = defineEmits(["change", "update:modelValue"]);
// 响应式数据
const activeIndex = ref(props.defaultActive);
const tabList = ref([]);
// 处理Tab点击
const handleTabClick = (index) => {
changeTabItem(index);
};
// 支持 force 参数,强制触发(即使 index 与当前相同)
const changeTabItem = (index, force = false) => {
if (!force && activeIndex.value === index) return;
activeIndex.value = index;
emit("change", {
index,
item: tabList.value[index],
});
emit("update:modelValue", index);
}
// 监听tabs变化
watch(
() => props.tabs,
(newTabs) => {
tabList.value = newTabs;
},
{ deep: true }
);
// 监听defaultActive变化
watch(
() => props.defaultActive,
(newActive) => {
if (newActive !== activeIndex.value) {
activeIndex.value = newActive;
}
}
);
// 暴露方法
defineExpose({
setActiveIndex: (index) => {
if (index >= 0 && index < tabList.value.length) {
handleTabClick(index);
}
},
getActiveIndex: () => activeIndex.value,
getActiveItem: () => tabList.value[activeIndex.value],
});
onMounted(() => {
getCommodityTypePageList();
});
// 获取商品类型列表(示例方法,实际使用时根据需要调用)
const getCommodityTypePageList = async () => {
const res = await commodityTypePageList({size: 20, current: 1});
if (res && res.data && res.data.records) {
tabList.value = res.data.records;
} else {
tabList.value = props.tabs;
}
// 加载完成后强制选中第一个项并通知外部
if (tabList.value && tabList.value.length > 0) {
changeTabItem(0, true);
} else {
changeTabItem(props.defaultActive, true);
}
};
</script>
<style lang="scss" scoped>
@import "./styles/index.scss";
</style>