看板
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import type { ExtractPropTypes, PropType } from 'vue';
|
||||
import { baseProps } from '../common/props';
|
||||
|
||||
/**
|
||||
* u-collapse 手风琴 Props
|
||||
* @description 通过折叠面板收纳内容区域
|
||||
* @property {Boolean} accordion 是否手风琴模式(默认true)
|
||||
* @property {Object} headStyle 标题自定义样式,对象形式
|
||||
* @property {Object} bodyStyle 主体自定义样式,对象形式
|
||||
* @property {Object} itemStyle 每一个item的样式,对象形式
|
||||
* @property {Boolean} arrow 是否显示标题右侧的箭头(默认true)
|
||||
* @property {String} arrowColor 标题右侧箭头的颜色(默认var(--u-tips-color))
|
||||
* @property {String} hoverClass 样式类名,按下时有效(默认u-hover-class)
|
||||
*/
|
||||
export const CollapseProps = {
|
||||
...baseProps,
|
||||
/** 是否手风琴模式 */
|
||||
accordion: { type: Boolean, default: true },
|
||||
/** 头部的样式 */
|
||||
headStyle: { type: Object as PropType<Record<string, any>>, default: () => ({}) },
|
||||
/** 主体的样式 */
|
||||
bodyStyle: { type: Object as PropType<Record<string, any>>, default: () => ({}) },
|
||||
/** 每一个item的样式 */
|
||||
itemStyle: { type: Object as PropType<Record<string, any>>, default: () => ({}) },
|
||||
/** 是否显示右侧的箭头 */
|
||||
arrow: { type: Boolean, default: true },
|
||||
/** 箭头的颜色 */
|
||||
arrowColor: { type: String, default: 'var(--u-tips-color)' },
|
||||
/** 标题部分按压时的样式类,"none"为无效果 */
|
||||
hoverClass: { type: String, default: 'u-hover-class' }
|
||||
};
|
||||
|
||||
export type CollapseProps = ExtractPropTypes<typeof CollapseProps>;
|
||||
@@ -0,0 +1,190 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user