feat: 新增Collpase组件

This commit is contained in:
xuqingkai 2023-08-03 23:21:11 +08:00
parent e40352ff2f
commit 02d34fe081
7 changed files with 369 additions and 59 deletions

View File

@ -510,6 +510,16 @@
},
"navigationBarTitleText": "Form 表单组合"
}
},
{
"path": "pages/collapse/Index",
"name": "collapse",
"style": {
"mp-alipay": {
"allowsBounceVertical": "NO"
},
"navigationBarTitleText": "Collapse 折叠面板"
}
}
],
// "tabBar": {

View File

@ -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>

View File

@ -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>

View File

@ -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
// (nullundefinedundefinedvoid (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';

View File

@ -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(

View File

@ -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
}

View File

@ -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+高质量组件,覆盖大量移动端场景",