471 行
13 KiB
Plaintext
471 行
13 KiB
Plaintext
<template>
|
||
<scroll-view class="page">
|
||
<view class="section">
|
||
<text class="section-title">手风琴模式</text>
|
||
<uni-collapse :accordion="true">
|
||
<uni-collapse-item title="面板 A(默认展开)" :open="data.enableOpen" @change="onPanelAChange">
|
||
<view class="content-box" @click="onPanelAClick">
|
||
<text class="content-text">手风琴模式下,同时只会保持一个面板展开。点击当前标题会自动收起其他面板。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
<uni-collapse-item title="面板 B" @change="onPanelBChange">
|
||
<view id="panel-b" class="content-box">
|
||
<text class="content-text">当前项展开时,其他已展开的项会自动收起。这里用于验证切换动画和事件回调。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
<uni-collapse-item title="禁用项" :disabled="true">
|
||
<view class="content-box">
|
||
<text class="content-text">禁用状态下不会响应点击,也不会触发展开或收起。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
</uni-collapse>
|
||
</view>
|
||
|
||
<view class="section">
|
||
<text class="section-title">普通模式-可同时展开的分组折叠列表</text>
|
||
<uni-collapse :accordion="false">
|
||
<uni-collapse-item v-for="(group, index) in categoryGroups" :key="group.title" :title="group.title" @change="onCategoryChange(group.title, $event as boolean)">
|
||
<view class="category-list">
|
||
<view v-for="(item, itemIndex) in group.items" :key="item.title" class="category-list-item"
|
||
:class="{'category-list-item--last': itemIndex == group.items.length - 1}">
|
||
<text class="category-list-text">{{ item.title }}</text>
|
||
<view class="category-list-arrow"></view>
|
||
</view>
|
||
</view>
|
||
</uni-collapse-item>
|
||
</uni-collapse>
|
||
</view>
|
||
|
||
<view class="section">
|
||
<text class="section-title">externalClass 示例</text>
|
||
<uni-collapse :accordion="false">
|
||
<uni-collapse-item title="自定义标题文字" title-class="demo-title-emphasis">
|
||
<view class="content-box">
|
||
<text class="content-text">通过 title-class 可以覆盖 title 属性对应 text 组件的样式。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
<uni-collapse-item title="自定义箭头样式" arrow-class="demo-arrow-large">
|
||
<view class="content-box">
|
||
<text class="content-text">通过 arrow-class 可以覆盖默认箭头 view 的尺寸、边框粗细和颜色。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
<uni-collapse-item title="同时自定义标题和箭头" title-class="demo-title-accent" arrow-class="demo-arrow-accent">
|
||
<view class="content-box">
|
||
<text class="content-text">title-class 和 arrow-class 可以组合使用。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
</uni-collapse>
|
||
</view>
|
||
|
||
<view class="section">
|
||
<text class="section-title">具名插槽标题</text>
|
||
<uni-collapse :accordion="false">
|
||
<uni-collapse-item>
|
||
<template #title="{ open, disabled }">
|
||
<view class="slot-title-wrap">
|
||
<view class="slot-title-icon" :class="{'slot-title-icon--open': open, 'slot-title-icon--disabled': disabled}"></view>
|
||
<text class="slot-title-text" :class="{'slot-title-text--open': open, 'slot-title-text--disabled': disabled}">消息中心</text>
|
||
<text class="slot-title-badge" :class="{'slot-title-badge--open': open}">{{ open ? 'OPEN' : 'CLOSE' }}</text>
|
||
</view>
|
||
</template>
|
||
<view class="content-box">
|
||
<text class="content-text">通过 #title 可以自定义标题区域结构,例如在文字前增加图标、状态徽标和辅助说明。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
<uni-collapse-item>
|
||
<template #title="{ open }">
|
||
<view class="slot-title-wrap">
|
||
<view class="slot-title-icon slot-title-icon--secondary" :class="{'slot-title-icon--open': open}"></view>
|
||
<text class="slot-title-text" :class="{'slot-title-text--open': open}">设置中心</text>
|
||
<text class="slot-title-note">{{ open ? '当前已展开' : '点击展开配置项' }}</text>
|
||
</view>
|
||
</template>
|
||
<view class="content-box">
|
||
<text class="content-text">具名插槽会透出 open 和 disabled 状态,方便你在标题区域里联动样式。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
</uni-collapse>
|
||
</view>
|
||
|
||
<view class="section section-dark">
|
||
<text class="section-title section-title-dark">暗黑模式 externalClass 示例</text>
|
||
<view class="dark-tip">
|
||
<text class="dark-tip-text">这个示例通过给 uni-collapse-item 传入 class 和 externalClass,分别定制内容区和标题区样式。</text>
|
||
</view>
|
||
<uni-collapse :accordion="false">
|
||
<uni-collapse-item class="dark-collapse-item" title="暗色标题 A" :open="true" title-wrap-class="dark-title-wrap" title-class="dark-title-text" title-open-class="dark-title-text-open" content-wrap-class="dark-content-wrap" arrow-class="dark-arrow" arrow-open-class="dark-arrow-open">
|
||
<view class="content-box dark-content-box">
|
||
<text class="content-text dark-content-text">组件根节点通过 class 统一接入暗色卡片样式,标题背景、默认文字、展开态文字和箭头颜色通过 externalClass 传入。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
<uni-collapse-item class="dark-collapse-item dark-collapse-item-strong" title="暗色标题 B" title-wrap-class="dark-title-wrap-strong" title-class="dark-title-text-strong" title-open-class="dark-title-text-open" content-wrap-class="dark-content-wrap" arrow-class="dark-arrow-strong" arrow-open-class="dark-arrow-open">
|
||
<view class="content-box dark-content-box">
|
||
<text class="content-text dark-content-text">你也可以继续组合不同的根容器 class、标题字重、箭头粗细和激活色样式。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
<uni-collapse-item class="dark-collapse-item dark-collapse-item-muted" title="暗色禁用项" :disabled="true" title-wrap-class="dark-title-wrap-muted" title-class="dark-title-text" title-disabled-class="dark-title-text-disabled" content-wrap-class="dark-content-wrap-muted" arrow-class="dark-arrow" arrow-disabled-class="dark-arrow-disabled">
|
||
<view class="content-box dark-content-box dark-content-box-muted">
|
||
<text class="content-text dark-content-text">禁用态默认通过透明度表达状态,外部依然可以继续指定禁用态文字和箭头颜色。</text>
|
||
</view>
|
||
</uni-collapse-item>
|
||
</uni-collapse>
|
||
</view>
|
||
|
||
<view class="log-section">
|
||
<text class="log-title">最近事件</text>
|
||
<text class="log-text">{{ data.lastEvent }}</text>
|
||
</view>
|
||
</scroll-view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
type CategoryItem = {
|
||
title : string
|
||
}
|
||
|
||
type CategoryGroup = {
|
||
title : string,
|
||
items : CategoryItem[]
|
||
}
|
||
|
||
const categoryGroups : CategoryGroup[] = [
|
||
{
|
||
title: '分类1',
|
||
items: [
|
||
{ title: '分类1-item1' },
|
||
{ title: '分类1-item2' },
|
||
{ title: '分类1-item3' }
|
||
]
|
||
},
|
||
{
|
||
title: '分类2',
|
||
items: [
|
||
{ title: '分类2-item1' },
|
||
{ title: '分类2-item2' },
|
||
{ title: '分类2-item3' }
|
||
]
|
||
},
|
||
{
|
||
title: '分类3',
|
||
items: [
|
||
{ title: '分类3-item1' },
|
||
{ title: '分类3-item2' },
|
||
{ title: '分类3-item3' }
|
||
]
|
||
}
|
||
]
|
||
|
||
type Data = {
|
||
lastEvent: string,
|
||
enableOpen: boolean
|
||
}
|
||
|
||
const data = reactive<Data>({
|
||
lastEvent: '暂无交互',
|
||
enableOpen: true
|
||
})
|
||
|
||
function updateEvent(panel : string, open : boolean) {
|
||
const stateText = open ? '展开' : '收起'
|
||
const message = `${panel}${stateText}`
|
||
data.lastEvent = message
|
||
console.log(`[collapse] ${message}`)
|
||
}
|
||
|
||
function onPanelAChange(open : boolean) {
|
||
updateEvent('面板 A ', open)
|
||
}
|
||
|
||
function onPanelBChange(open : boolean) {
|
||
updateEvent('面板 B ', open)
|
||
}
|
||
|
||
function onCategoryChange(title : string, open : boolean) {
|
||
updateEvent(`${title} `, open)
|
||
}
|
||
|
||
const onPanelAClick = () => {
|
||
console.log('onPanelAClick')
|
||
}
|
||
|
||
defineExpose({
|
||
data,
|
||
onPanelAClick
|
||
})
|
||
</script>
|
||
|
||
<style>
|
||
.page {
|
||
flex: 1;
|
||
padding: 12px;
|
||
}
|
||
|
||
.section {
|
||
margin-bottom: 12px;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
background-color: var(--list-background-color, #ffffff);
|
||
}
|
||
|
||
.section-title {
|
||
padding: 12px;
|
||
font-size: 14px;
|
||
color: var(--text-color, #333333);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.content-box {
|
||
padding: 12px;
|
||
background-color: var(--active-background-color, #f8fafc);
|
||
}
|
||
|
||
.content-text {
|
||
font-size: 13px;
|
||
color: var(--text-color, #475569);
|
||
opacity: 0.78;
|
||
line-height: 20px;
|
||
}
|
||
|
||
.category-list {
|
||
background-color: var(--active-background-color, #f8fafc);
|
||
}
|
||
|
||
.category-list-item {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 18px;
|
||
border-bottom-width: 1px;
|
||
border-bottom-style: solid;
|
||
border-bottom-color: var(--border-color, #e2e8f0);
|
||
}
|
||
|
||
.category-list-item--last {
|
||
border-bottom-width: 0px;
|
||
}
|
||
|
||
.category-list-text {
|
||
flex: 1;
|
||
font-size: 13px;
|
||
color: var(--text-color, #334155);
|
||
line-height: 20px;
|
||
}
|
||
|
||
.category-list-arrow {
|
||
width: 8px;
|
||
height: 8px;
|
||
margin-left: 12px;
|
||
transform: rotate(-45deg);
|
||
border-right-width: 1px;
|
||
border-right-style: solid;
|
||
border-right-color: var(--arrow-color, #94a3b8);
|
||
border-bottom-width: 1px;
|
||
border-bottom-style: solid;
|
||
border-bottom-color: var(--arrow-color, #94a3b8);
|
||
}
|
||
|
||
.demo-title-emphasis {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.demo-title-accent {
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
color: #0f766e;
|
||
}
|
||
|
||
.demo-arrow-large {
|
||
width: 12px;
|
||
height: 12px;
|
||
border-right-width: 2px;
|
||
border-bottom-width: 2px;
|
||
}
|
||
|
||
.demo-arrow-accent {
|
||
border-right-color: #0f766e;
|
||
border-bottom-color: #0f766e;
|
||
border-right-width: 2px;
|
||
border-bottom-width: 2px;
|
||
}
|
||
|
||
.slot-title-wrap {
|
||
flex: 1;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.slot-title-icon {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 5px;
|
||
margin-right: 10px;
|
||
background-color: #94a3b8;
|
||
}
|
||
|
||
.slot-title-icon--secondary {
|
||
background-color: #c084fc;
|
||
}
|
||
|
||
.slot-title-icon--open {
|
||
background-color: #2563eb;
|
||
}
|
||
|
||
.slot-title-icon--disabled {
|
||
background-color: #cbd5e1;
|
||
}
|
||
|
||
.slot-title-text {
|
||
flex: 1;
|
||
font-size: 14px;
|
||
color: var(--text-color, #1f2937);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.slot-title-text--open {
|
||
color: #2563eb;
|
||
}
|
||
|
||
.slot-title-text--disabled {
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.slot-title-badge {
|
||
padding: 4px 8px;
|
||
border-radius: 10px;
|
||
background-color: #e2e8f0;
|
||
font-size: 11px;
|
||
color: #475569;
|
||
}
|
||
|
||
.slot-title-badge--open {
|
||
background-color: #dbeafe;
|
||
color: #1d4ed8;
|
||
}
|
||
|
||
.slot-title-note {
|
||
font-size: 12px;
|
||
color: #64748b;
|
||
}
|
||
|
||
.section-dark {
|
||
background-color: #111827;
|
||
}
|
||
|
||
.section-title-dark {
|
||
color: #e5e7eb;
|
||
}
|
||
|
||
.dark-collapse-item {
|
||
background-color: #111827;
|
||
}
|
||
|
||
.dark-collapse-item-strong {
|
||
background-color: #0f172a;
|
||
}
|
||
|
||
.dark-collapse-item-muted {
|
||
background-color: #101826;
|
||
}
|
||
|
||
.dark-tip {
|
||
padding: 0px 12px 12px 12px;
|
||
}
|
||
|
||
.dark-tip-text {
|
||
font-size: 12px;
|
||
color: #9ca3af;
|
||
line-height: 18px;
|
||
}
|
||
|
||
.dark-content-box {
|
||
background-color: #1f2937;
|
||
}
|
||
|
||
.dark-content-wrap {
|
||
background-color: #1f2937;
|
||
}
|
||
|
||
.dark-content-text {
|
||
color: #cbd5e1;
|
||
}
|
||
|
||
.dark-content-box-muted {
|
||
background-color: #182333;
|
||
}
|
||
|
||
.dark-content-wrap-muted {
|
||
background-color: #182333;
|
||
}
|
||
|
||
.dark-title-wrap {
|
||
background-color: #111827;
|
||
}
|
||
|
||
.dark-title-wrap-strong {
|
||
background-color: #0f172a;
|
||
}
|
||
|
||
.dark-title-wrap-muted {
|
||
background-color: #101826;
|
||
}
|
||
|
||
.dark-title-text {
|
||
color: #f8fafc;
|
||
}
|
||
|
||
.dark-title-text-strong {
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
color: #e2e8f0;
|
||
}
|
||
|
||
.dark-title-text-open {
|
||
color: #60a5fa;
|
||
}
|
||
|
||
.dark-title-text-disabled {
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.dark-arrow {
|
||
border-right-color: #cbd5e1;
|
||
border-bottom-color: #cbd5e1;
|
||
}
|
||
|
||
.dark-arrow-strong {
|
||
border-right-color: #cbd5e1;
|
||
border-bottom-color: #cbd5e1;
|
||
border-right-width: 2px;
|
||
border-bottom-width: 2px;
|
||
}
|
||
|
||
.dark-arrow-open {
|
||
border-right-color: #60a5fa;
|
||
border-bottom-color: #60a5fa;
|
||
}
|
||
|
||
.dark-arrow-disabled {
|
||
border-right-color: #94a3b8;
|
||
border-bottom-color: #94a3b8;
|
||
}
|
||
|
||
.log-section {
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
background-color: var(--list-background-color, #ffffff);
|
||
}
|
||
|
||
.log-title {
|
||
font-size: 13px;
|
||
color: var(--text-color, #333333);
|
||
font-weight: 500;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.log-text {
|
||
font-size: 13px;
|
||
color: #2563eb;
|
||
}
|
||
</style>
|