CNCEC_APP/uni_modules/uview-pro/components/u-collapse/u-collapse.vue

191 lines
5.5 KiB
Vue
Raw Normal View History

2026-03-25 14:54:15 +08:00
<template>
<view class="u-collapse" :style="$u.toStyle(customStyle)" :class="customClass">
<slot />
</view>
</template>
<script lang="ts">
export default {
name: 'u-collapse',
options: {
addGlobalClass: true,
// #ifndef MP-TOUTIAO
virtualHost: true,
// #endif
styleIsolation: 'shared'
}
};
</script>
<script setup lang="ts">
import { nextTick, onMounted, ref } from 'vue';
import { CollapseProps } from './types';
import { $u, useParent } from '../../';
/**
* collapse 手风琴
* @description 通过折叠面板收纳内容区域
* @tutorial https://uviewpro.cn/zh/components/collapse.html
* @property {Boolean} accordion 是否手风琴模式默认true
* @property {Boolean} arrow 是否显示标题右侧的箭头默认true
* @property {String} arrow-color 标题右侧箭头的颜色默认var(--u-tips-color)
* @property {Object} head-style 标题自定义样式对象形式
* @property {Object} body-style 主体自定义样式对象形式
* @property {String} hover-class 样式类名按下时有效默认u-hover-class
* @event {Function} change 当前激活面板展开时触发(如果是手风琴模式参数activeNames类型为String否则为Array)
* @example <u-collapse></u-collapse>
*/
const props = defineProps(CollapseProps);
const emit = defineEmits(['change']);
// 使用通信库的父组件Hook
const { children, broadcast, getChildrenExposed } = useParent('u-collapse');
// 当前激活的面板 - 只在手风琴模式下使用
const activeName = ref<string | number>('');
onMounted(() => {
nextTick(() => {
// 初始化收集所有open为true的项
setTimeout(() => {
initializeActiveName();
}, 100);
});
});
/**
* 初始化activeName - 找到第一个open为true的项
*/
function initializeActiveName() {
if (props.accordion) {
// 手风琴模式下取第一个open为true的项作为初始激活项
const childrenExposed = getChildrenExposed();
const openChild = childrenExposed.find(child => child.exposed.isShow === true);
if (openChild) {
activeName.value = openChild.exposed.itemName || '';
}
}
}
/**
* collapse item被点击由collapse item调用父组件方法
*/
function onChange(name: string | number) {
if (props.accordion) {
// 手风琴模式
const childrenExposed = getChildrenExposed();
const targetChild = childrenExposed.find(child => child.exposed.itemName === name);
if (targetChild?.exposed.isShow.value === true) {
// 目标项当前是展开状态,点击后要关闭它
activeName.value = '';
broadcast('closeAll', {});
} else {
// 目标项当前是关闭状态,点击后要展开它并关闭其他
activeName.value = name;
broadcast('openSingle', { targetName: name });
}
} else {
// 非手风琴模式 - 只通知目标项切换状态
broadcast('toggleSingle', { targetName: name });
}
// 收集当前所有展开的项
let currentActiveNames: (string | number)[] = [];
if (props.accordion) {
currentActiveNames = activeName.value === 0 || activeName.value ? [activeName.value] : [];
} else {
// 对于非手风琴模式,我们不知道所有项的状态
currentActiveNames = [];
children.forEach(child => {
if (child.getExposed().isShow.value) {
currentActiveNames.push(child.getExposed().itemName);
}
});
}
currentActiveNames.length > 0 && emit('change', props.accordion ? activeName.value || '' : currentActiveNames);
}
/**
* 设置激活的面板
*/
function setActiveNames(names: string | number | (string | number)[]) {
if (props.accordion) {
// 手风琴模式
const name = Array.isArray(names) ? names[0] : names;
activeName.value = name || '';
if (name) {
broadcast('openSingle', { targetName: name });
} else {
broadcast('closeAll', {});
}
} else {
// 非手风琴模式
const namesArray = Array.isArray(names) ? names : [names];
broadcast('setMultiple', { targetNames: namesArray });
}
}
/**
* 打开所有面板
*/
function openAll() {
if (props.accordion) {
console.warn('手风琴模式下不能打开所有面板');
return;
}
const childrenExposed = getChildrenExposed();
const allNames = childrenExposed.map(child => child.exposed.itemName).filter(Boolean);
broadcast('setMultiple', { targetNames: allNames });
}
/**
* 关闭所有面板
*/
function closeAll() {
broadcast('closeAll', {});
if (props.accordion) {
activeName.value = '';
}
}
/**
* 重新初始化用于动态内容变化
*/
function init() {
const childrenExposed = getChildrenExposed();
childrenExposed.forEach(child => {
if (child.exposed.init) {
child.exposed.init();
}
});
// 重新初始化activeName
setTimeout(() => {
initializeActiveName();
}, 150);
}
// 使用defineExpose暴露给外部
defineExpose({
// props
props,
// 状态
activeName,
// 方法
onChange,
setActiveNames,
openAll,
closeAll,
init,
// 计算属性
childrenCount: () => children.length
});
</script>
<style lang="scss" scoped>
@import '../../libs/css/style.components.scss';
</style>