mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-06 17:18:40 +08:00
feat: ✨ 新增Collpase组件
This commit is contained in:
parent
e40352ff2f
commit
02d34fe081
@ -510,6 +510,16 @@
|
||||
},
|
||||
"navigationBarTitleText": "Form 表单组合"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/collapse/Index",
|
||||
"name": "collapse",
|
||||
"style": {
|
||||
"mp-alipay": {
|
||||
"allowsBounceVertical": "NO"
|
||||
},
|
||||
"navigationBarTitleText": "Collapse 折叠面板"
|
||||
}
|
||||
}
|
||||
],
|
||||
// "tabBar": {
|
||||
|
||||
@ -1,83 +1,80 @@
|
||||
<!--
|
||||
* @Author: weisheng
|
||||
* @Date: 2023-07-20 00:34:54
|
||||
* @LastEditTime: 2023-07-21 17:56:33
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: \wot-design-uni\src\pages\collapse\Index.vue
|
||||
* 记得注释
|
||||
-->
|
||||
<template>
|
||||
<view>
|
||||
<!-- <demo-block title="基础用法" transparent>
|
||||
<wd-collapse value="{{ value1 }}" bind:change="handleChange1">
|
||||
<wd-collapse-item title="标签1" name="{{ name }}">这是一条简单的示例文字。</wd-collapse-item>
|
||||
<demo-block title="基础用法" transparent>
|
||||
<wd-collapse v-model="value1" @change="handleChange1">
|
||||
<wd-collapse-item title="标签1" name="name">这是一条简单的示例文字。</wd-collapse-item>
|
||||
<wd-collapse-item title="标签2" name="item2">这是一条简单的示例文字。</wd-collapse-item>
|
||||
<wd-collapse-item title="标签3" name="item3">这是一条简单的示例文字。</wd-collapse-item>
|
||||
</wd-collapse>
|
||||
</demo-block>
|
||||
<demo-block title="手风琴" transparent>
|
||||
<wd-collapse value="{{ value2 }}" accordion="{{ accordion }}" bindchange="handleChange2">
|
||||
<wd-collapse v-model="value2" :accordion="accordion" @change="handleChange2">
|
||||
<wd-collapse-item title="标签1" name="item1">这是一条简单的示例文字。</wd-collapse-item>
|
||||
<wd-collapse-item title="标签2" name="item2">这是一条简单的示例文字。</wd-collapse-item>
|
||||
<wd-collapse-item title="标签3" name="item3">这是一条简单的示例文字。</wd-collapse-item>
|
||||
</wd-collapse>
|
||||
</demo-block>
|
||||
<demo-block title="禁用" transparent>
|
||||
<wd-collapse value="{{ value3 }}" bind:change="handleChange3">
|
||||
<wd-collapse v-model="value3" @change="handleChange3">
|
||||
<wd-collapse-item title="标签1" name="item1">这是一条简单的示例文字。</wd-collapse-item>
|
||||
<wd-collapse-item title="标签2" name="item2" disabled>这是一条简单的示例文字。</wd-collapse-item>
|
||||
<wd-collapse-item title="标签3" name="item3">这是一条简单的示例文字。</wd-collapse-item>
|
||||
</wd-collapse>
|
||||
</demo-block>
|
||||
<demo-block title="查看更多" transparent>
|
||||
<wd-collapse viewmore value="{{ value4 }}" bind:change="handleChange4">
|
||||
<wd-collapse viewmore v-model="value4" @change="handleChange4">
|
||||
这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。
|
||||
</wd-collapse>
|
||||
</demo-block>
|
||||
<demo-block title="查看更多-行数显示设置" transparent>
|
||||
<wd-collapse viewmore value="{{ value5 }}" bind:change="handleChange5" line-num="3">
|
||||
<wd-collapse viewmore v-model="value5" @change="handleChange5" :line-num="3">
|
||||
行数显示设置:这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。
|
||||
</wd-collapse>
|
||||
</demo-block>
|
||||
<demo-block title="查看更多-具名插槽" transparent>
|
||||
<wd-collapse viewmore value="{{ value6 }}" bind:change="handleChange6" use-more-slot custom-more-slot-class="more-slot">
|
||||
<wd-collapse viewmore v-model="value6" @change="handleChange6" use-more-slot custom-more-slot-class="more-slot">
|
||||
具名插槽:这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。这是一条简单的示例文字。
|
||||
<view slot="more">显示全部</view>
|
||||
<template #more>
|
||||
<view>显示全部</view>
|
||||
</template>
|
||||
</wd-collapse>
|
||||
</demo-block> -->
|
||||
</demo-block>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
// const value1 = ref<string[]>(['item1'])
|
||||
// const value2 = ref<string>('item1')
|
||||
// const value3 = ref<string[]>(['item1'])
|
||||
// const value4 = ref<boolean>(false)
|
||||
// const value5 = ref<boolean>(false)
|
||||
// const value6 = ref<boolean>(false)
|
||||
// const accordion = ref<boolean>(true)
|
||||
// const name = ref<string>('item1')
|
||||
const value1 = ref<string[]>(['item1'])
|
||||
const value2 = ref<string>('item1')
|
||||
const value3 = ref<string[]>(['item1'])
|
||||
const value4 = ref<boolean>(false)
|
||||
const value5 = ref<boolean>(false)
|
||||
const value6 = ref<boolean>(false)
|
||||
const accordion = ref<boolean>(true)
|
||||
const name = ref<string>('item1')
|
||||
|
||||
// function handleChange1({ detail }) {
|
||||
// this.setData({ value1: detail.value })
|
||||
// }
|
||||
// function handleChange2({ detail }) {
|
||||
// this.setData({ value2: detail.value })
|
||||
// }
|
||||
// function handleChange3({ detail }) {
|
||||
// this.setData({ value3: detail.value })
|
||||
// }
|
||||
// function handleChange4({ detail }) {
|
||||
// this.setData({ value4: detail.value })
|
||||
// }
|
||||
// function handleChange5({ detail }) {
|
||||
// this.setData({ value5: detail.value })
|
||||
// }
|
||||
function handleChange1({ value }) {
|
||||
console.log(value)
|
||||
}
|
||||
function handleChange2({ value }) {
|
||||
console.log(value)
|
||||
}
|
||||
function handleChange3({ value }) {
|
||||
console.log(value)
|
||||
}
|
||||
function handleChange4({ value }) {
|
||||
console.log(value)
|
||||
}
|
||||
function handleChange5({ value }) {
|
||||
console.log(value)
|
||||
}
|
||||
|
||||
function handleChange6({ value }) {
|
||||
console.log(value)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.more-slot {
|
||||
:deep(.more-slot) {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,16 +1,162 @@
|
||||
<!--
|
||||
* @Author: weisheng
|
||||
* @Date: 2023-07-20 00:34:54
|
||||
* @LastEditTime: 2023-07-21 17:53:34
|
||||
* @Date: 2023-08-01 11:12:05
|
||||
* @LastEditTime: 2023-08-03 14:33:08
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-collapse-item\wd-collapse-item.vue
|
||||
* 记得注释
|
||||
-->
|
||||
<template>
|
||||
<view></view>
|
||||
<view :class="`wd-collapse-item ${disabled ? 'is-disabled' : ''} ${customClass}`">
|
||||
<view :class="`wd-collapse-item__header ${firstItem ? 'wd-collapse-item__header-first' : ''}`" @click="toggle">
|
||||
<text class="wd-collapse-item__title">{{ title }}</text>
|
||||
<wd-icon name="arrow-down" :class="`wd-collapse-item__arrow ${isExpand ? 'is-retract' : ''}`" />
|
||||
</view>
|
||||
<view
|
||||
class="wd-collapse-item__wrapper"
|
||||
:style="`height: ${height}; position: ${show ? 'relative' : 'absolute'}; visibility: ${
|
||||
show ? 'show' : 'hidden'
|
||||
}; transition-duration: ${transD}`"
|
||||
@transitionend="onTransitionend"
|
||||
>
|
||||
<view class="wd-collapse-item__body">
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script></script>
|
||||
<script lang="ts" setup>
|
||||
import { computed, getCurrentInstance, inject, onBeforeMount, onMounted, ref, watch } from 'vue'
|
||||
import { getRect } from '../common/util'
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
const $body = '.wd-collapse-item__body'
|
||||
|
||||
interface Props {
|
||||
customClass?: string
|
||||
title: string
|
||||
disabled: boolean
|
||||
name: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
customClass: '',
|
||||
disabled: false
|
||||
})
|
||||
|
||||
const parent = inject<any>('wdcollapse')
|
||||
const children = inject<any[]>('collapseChildren')
|
||||
|
||||
const height = ref<string | number>('')
|
||||
const show = ref<boolean>(false)
|
||||
const firstItem = ref<boolean>(false)
|
||||
const transD = ref<string>('0.3s')
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
|
||||
const isExpand = computed(() => {
|
||||
if (!parent.modelValue) {
|
||||
console.warn('[wot-design] warning(wd-collapse-item): there is no v-model with Collapse.')
|
||||
return true
|
||||
}
|
||||
return parent.accordion ? parent.modelValue === props.name : parent.modelValue.indexOf(props.name) > -1
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.name,
|
||||
(newVal) => {
|
||||
const condition = parent && children && parent.$.exposed.checkRepeat(children, newVal, 'name')
|
||||
// 比较数组中是否存在重复数据
|
||||
if (condition > -1) {
|
||||
throw Error('Name attribute cannot be defined repeatedly')
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (parent) {
|
||||
parent.$.exposed.setChild && parent.$.exposed.setChild(proxy)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
show.value = isExpand.value
|
||||
})
|
||||
|
||||
function initState() {
|
||||
scrollHeight($body, true)
|
||||
}
|
||||
/**
|
||||
* 控制折叠面板滚动
|
||||
* @param {String} select 选择器名称
|
||||
* @param {Boolean} firstRender 是否首次渲染
|
||||
*/
|
||||
function scrollHeight(select, firstRender = false) {
|
||||
getRect(select).then((rect: any) => {
|
||||
if (!rect) return
|
||||
const { height: rectHeight } = rect
|
||||
if (isExpand.value) {
|
||||
if (rectHeight === 0) {
|
||||
height.value = 'auto'
|
||||
show.value = true
|
||||
transD.value = firstRender ? '0s' : '0.3s'
|
||||
return
|
||||
}
|
||||
|
||||
height.value = 0
|
||||
show.value = true
|
||||
transD.value = firstRender ? '0s' : '0.3s'
|
||||
setTimeout(() => {
|
||||
height.value = rectHeight + 'px'
|
||||
}, 30)
|
||||
} else {
|
||||
height.value = rectHeight + 'px'
|
||||
transD.value = firstRender ? '0s' : '0.3s'
|
||||
setTimeout(() => {
|
||||
height.value = 0
|
||||
}, 30)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 点击触发
|
||||
function toggle() {
|
||||
const { disabled, name } = props
|
||||
if (disabled) return
|
||||
// 如果是手风琴模式, 那么只展开一个,其余全部折叠
|
||||
if (parent.accordion) {
|
||||
children &&
|
||||
children.forEach((item) => {
|
||||
item.$.exposed.scrollHeight($body)
|
||||
})
|
||||
} else {
|
||||
scrollHeight($body)
|
||||
}
|
||||
// 调用父组件方法 switchValue 当前选中的是什么,判断当前是否处于选中状态
|
||||
parent.$.exposed.switchValue(name, !isExpand.value)
|
||||
}
|
||||
// 动画结束时触发
|
||||
function onTransitionend(event) {
|
||||
if (!isExpand.value) {
|
||||
show.value = false
|
||||
} else {
|
||||
height.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
function setFirstItem(first: boolean) {
|
||||
firstItem.value = first
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
scrollHeight,
|
||||
setFirstItem
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
|
||||
@ -1,8 +1,171 @@
|
||||
<!--
|
||||
* @Author: weisheng
|
||||
* @Date: 2023-08-01 11:12:05
|
||||
* @LastEditTime: 2023-08-03 23:17:24
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-collapse\wd-collapse.vue
|
||||
* 记得注释
|
||||
-->
|
||||
<template>
|
||||
<view></view>
|
||||
<view :class="`wd-collapse ${viewmore ? 'is-viewmore' : ''} ${customClass}`">
|
||||
<!-- 普通或手风琴 -->
|
||||
<block v-if="!viewmore">
|
||||
<slot></slot>
|
||||
</block>
|
||||
<!-- 查看更多模式 -->
|
||||
<view v-else>
|
||||
<view
|
||||
:class="`wd-collapse__content ${!modelValue ? 'is-retract' : ''} `"
|
||||
:style="`-webkit-line-clamp: ${contentLineNum}; -webkit-box-orient: vertical`"
|
||||
>
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="wd-collapse__more" @click="switchValue(!modelValue)">
|
||||
<!-- 自定义展开按钮 -->
|
||||
<view v-if="useMoreSlot" :class="customMoreSlotClass">
|
||||
<slot name="more"></slot>
|
||||
</view>
|
||||
<!-- 显示展开或折叠按钮 -->
|
||||
<block v-else>
|
||||
<span class="wd-collapse__more-txt">{{ !modelValue ? '展开' : '折叠' }}</span>
|
||||
<view :class="`wd-collapse__arrow ${modelValue ? 'is-retract' : ''}`">
|
||||
<wd-icon name="arrow-down"></wd-icon>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script></script>
|
||||
<script lang="ts" setup>
|
||||
import { getCurrentInstance, onBeforeMount, provide, ref, watch } from 'vue'
|
||||
|
||||
interface Props {
|
||||
customClass?: string
|
||||
customMoreSlotClass?: string
|
||||
modelValue: string | Array<string> | boolean
|
||||
accordion: boolean
|
||||
viewmore: boolean
|
||||
useMoreSlot: boolean
|
||||
lineNum: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
customClass: '',
|
||||
customMoreSlotClass: '',
|
||||
accordion: false,
|
||||
viewmore: false,
|
||||
useMoreSlot: false,
|
||||
lineNum: 2
|
||||
})
|
||||
|
||||
const contentLineNum = ref<number>(0) // 查看更多的折叠面板,收起时的显示行数
|
||||
const children: any[] = [] // 子组件
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal, oldVal) => {
|
||||
const { viewmore, accordion } = props
|
||||
// 类型校验,支持所有值(除null、undefined。undefined建议统一写成void (0)防止全局undefined被覆盖)
|
||||
if (newVal === null || newVal === undefined) {
|
||||
throw Error('value can not be null or undefined')
|
||||
}
|
||||
// 手风琴状态下 value 类型只能为 string
|
||||
if (accordion && typeof newVal !== 'string') {
|
||||
throw Error('accordion value must be string')
|
||||
} else if (!accordion && !viewmore && checkType(newVal) !== 'Array') {
|
||||
throw Error('value must be Array')
|
||||
}
|
||||
// 初始状态不执行动画
|
||||
// 外部修改 value 滚动
|
||||
if (oldVal && !viewmore && children) {
|
||||
children.forEach((item) => {
|
||||
const condition = Array.isArray(newVal) ? newVal.indexOf(item.name) > -1 : newVal === item.name
|
||||
if (item.isExpand === condition) return
|
||||
item.$.exposed.scrollHeight('.wd-collapse-item__body')
|
||||
})
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.lineNum,
|
||||
(newVal) => {
|
||||
if (newVal <= 0) {
|
||||
throw Error('lineNum must greater than 0')
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
)
|
||||
|
||||
onBeforeMount(() => {
|
||||
const { lineNum, viewmore, modelValue } = props
|
||||
contentLineNum.value = viewmore && !modelValue ? lineNum : 0
|
||||
})
|
||||
|
||||
/**
|
||||
* 设置子项
|
||||
* @param child
|
||||
*/
|
||||
function setChild(child) {
|
||||
const hasChild = children.findIndex((item) => {
|
||||
return item.$.uid === child.$.uid
|
||||
})
|
||||
if (hasChild === -1) {
|
||||
const repeat = checkRepeat(children, child.name, 'name')
|
||||
if (repeat > -1) {
|
||||
throw Error('Name attribute cannot be defined repeatedly')
|
||||
}
|
||||
children.push(child)
|
||||
children[0].$.exposed.setFirstItem(true)
|
||||
} else {
|
||||
children[hasChild] = child
|
||||
}
|
||||
}
|
||||
|
||||
function checkType(value) {
|
||||
return Object.prototype.toString.call(value).slice(8, -1)
|
||||
}
|
||||
/**
|
||||
* 检查是否存在重复属性
|
||||
* @param {Array} currentList
|
||||
* @param {String} checkValue 比较的重复值
|
||||
* @param {String} key 键名
|
||||
*/
|
||||
function checkRepeat(currentList, checkValue, key) {
|
||||
return currentList.findIndex((item) => item[key] === checkValue)
|
||||
}
|
||||
/**
|
||||
* 折叠控制
|
||||
* @param {String} name 当前选中的 item name
|
||||
* @param {Boolean} expanded 是否展开 false: 开->关(删除);true: 关->开(添加)
|
||||
*/
|
||||
function switchValue(name, expanded?) {
|
||||
const { accordion, viewmore, modelValue } = props
|
||||
if (!accordion && !viewmore && modelValue && Array.isArray(modelValue)) {
|
||||
name = expanded ? modelValue.concat(name) : modelValue.filter((item) => item !== name)
|
||||
}
|
||||
emit('update:modelValue', name)
|
||||
emit('change', {
|
||||
value: name
|
||||
})
|
||||
}
|
||||
|
||||
provide('wdcollapse', proxy)
|
||||
provide('collapseChildren', children)
|
||||
|
||||
defineExpose({
|
||||
children,
|
||||
setChild,
|
||||
checkRepeat,
|
||||
switchValue
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, getCurrentInstance, provide, ref, watch } from 'vue'
|
||||
import { getCurrentInstance, provide, ref, watch } from 'vue'
|
||||
const nextTick = () => new Promise((resolve) => setTimeout(resolve, 20))
|
||||
|
||||
interface Props {
|
||||
@ -27,7 +27,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
bgColor: '#ffffff'
|
||||
})
|
||||
|
||||
const children: any[] = []
|
||||
const children: any[] = [] // 子组件
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
|
||||
watch(
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\index.ts
|
||||
* 记得注释
|
||||
*/
|
||||
import type { App } from 'vue'
|
||||
|
||||
// Toast
|
||||
export { useToast } from './components/wd-toast'
|
||||
@ -19,9 +18,3 @@ export { dayjs } from './components/common/dayjs'
|
||||
export * as CommonUtil from './components/common/util'
|
||||
|
||||
export * as clickOut from './components/common/clickoutside'
|
||||
|
||||
const install = (Vue: App) => {}
|
||||
|
||||
export default {
|
||||
install
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"id": "wot-design-uni",
|
||||
"name": "wot-design-uni",
|
||||
"displayName": "wot-design-uni",
|
||||
"version": "0.0.0",
|
||||
"description": "一个参照Wot-design打造的uni-app组件库,基于Vue3+Typescript开发,提供50+高质量组件,覆盖大量移动端场景",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user