123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- <template>
- <view class="wk-dropdown">
- <view class="wk-dropdown-menu">
- <template v-for="(menu, index) in menuList">
- <view
- :key="index"
- :class="{ active: activeMenuIndex === index }"
- :style="{
- color: activeMenuIndex === index ? activeColor : inactiveColor
- }"
- class="wk-dropdown-menu_item"
- @tap="handleMenuClick(index)">
- <text :class="menu.leftIcon" class="wk wk-dropdown-menu_item__left-icon" />
- <text class="wk-dropdown-menu_item__text">
- {{ menu.name }}
- </text>
- <text v-if="!menu.leftIcon" class="wk wk-arrow-right wk-dropdown-menu_item__icon" />
- </view>
- </template>
- </view>
-
- <view
- :style="{
- height: contentStyle.height + 'px',
- zIndex: contentStyle.zIndex
- }"
- class="wk-dropdown-content">
- <view
- :style="{
- transform: activeMenuIndex !== -1 ? 'translateY(0)' : 'translateY(-100%)',
- maxHeight: contentHeight * 0.75 + 'px'
- }"
- class="wk-dropdown-content__pop" @click.stop>
- <slot />
- </view>
- <view
- :style="{
- height: maskStyle.height,
- top: maskStyle.top,
- opacity: maskStyle.opacity,
- zIndex: maskStyle.zIndex
- }"
- class="wk-dropdown-content__mask"
- @tap.stop.prevent="handleMenuClick(-1)" />
- </view>
- </view>
- </template>
- <script>
- /**
- * WkDropdown 下拉菜单(配合 WkDropdownItem 使用)
- * @author yxk
- * @property {String} activeColor 标题和选项卡选中的颜色(默认#3C80F7)
- * @property {String} inactiveColor 标题和选项卡未选中的颜色(默认#666666)
- * @property {Boolean} autoClose DropdownItem为非自定义内容时选择选项后是否自动关闭(默认 false 不自动关闭)
- * @property {Function} beforeOpen 拦截下拉菜单打开 参数: index next
- * @event {Function} open 下拉菜单被打开时触发 参数: index
- * @event {Function} change 切换下拉菜单时触发 参数: index
- * @event {Function} close 下拉菜单全部被关闭时触发
- *
- * @tips: 关闭下拉选项需手动调用 close 方法
- *
- * @example
- * <wk-dropdown>
- * <wk-dropdown-item name="排序" :options="[]"></wk-dropdown-item>
- * <wk-dropdown-item name="自定义内容">
- * your slot content
- * </wk-dropdown-item>
- * </wk-dropdown>
- */
- export default {
- name: 'WkDropdown',
- provide() {
- return {
- dropdown: this
- }
- },
- props: {
- activeColor: {
- type: String,
- default: '#3C80F7'
- },
- inactiveColor: {
- type: String,
- default: '#666666'
- },
- beforeOpen: {
- type: Function,
- default: null
- },
- autoClose: {
- type: Boolean,
- default: false
- }
- },
- data() {
- return {
- activeMenuIndex: -1,
- menuList: [], // 菜单项
- contentStyle: {
- height: 0,
- zIndex: -1
- },
- contentHeight: 0,
- maskStyle: {
- height: 0,
- top: 0,
- opacity: 0,
- zIndex: 1
- },
-
- children: []
- }
- },
- created() {
- this.children = []
- },
- mounted() {
- this.$nextTick(() => {
- this.getContentHeight()
- })
- // console.log(this.children)
- // this.initTab()
- },
- methods: {
- /**
- * 点击menu
- * @param {Number} index
- */
- handleMenuClick(index) {
- if (this.beforeOpen) {
- const that = this
- this.beforeOpen(index, () => {
- that.open(index)
- })
- } else {
- this.open(index)
- }
- },
-
- getParentNode(name = undefined) {
- let parent = this.$parent;
- // 通过while历遍,这里主要是为了H5需要多层解析的问题
- while (parent) {
- // 父组件
- if (parent.$options && parent.$options.name !== name) {
- // 如果组件的name不相等,继续上一级寻找
- parent = parent.$parent;
- } else {
- return parent;
- }
- }
- return false;
- },
-
- addChild(node) {
- // console.log('add node: ', this)
- // const findRes = this.menuList.find(o => o.name === node.name)
- // if (findRes) {
- // findRes.instance = node
- // }
- },
-
- /**
- * 展开选项
- * @param {Number} openIndex
- */
- open(openIndex) {
- let showFlag = false
- const oldIndex = this.activeMenuIndex
- if (openIndex === this.activeMenuIndex) {
- this.activeMenuIndex = -1
- } else {
- this.activeMenuIndex = openIndex
- this.emitChangeEvt(openIndex)
- }
-
- this.$nextTick(() => {
- const children = this.getChildren()
- // this.children = children
-
- children.forEach((node, index) => {
- if (index === this.activeMenuIndex) {
- console.log('open node', node)
- node.opened = true
- showFlag = true
- } else if (index === oldIndex && this.activeMenuIndex === -1) {
- // 如果原来没有激活的索引则去执行关闭动画
- this.close(index)
- } else {
- // 直接关闭原来被激活的索引不执行动画
- node.opened = false
- }
- })
-
- if (showFlag) {
- this.$set(this.contentStyle, 'zIndex', 10)
- this.$set(this.maskStyle, 'opacity', 1)
- this.emitOpenEvt(this.activeMenuIndex)
- }
- })
- },
-
- getChildren() {
- let children = null
- // #ifdef MP-WEIXIN
- children = this.$children
- // #endif
- // #ifndef MP-WEIXIN
- children = this.$slots.default || []
- children = children.filter(o => Boolean(o.tag)).map(o => {
- return o.componentInstance
- })
- // #endif
- return children
- },
-
- /**
- * 执行关闭动画
- * @param {Number} index
- */
- close(index = -1) {
- this.$set(this.maskStyle, 'opacity', 0)
- let vm = null
- const children = this.getChildren()
- if (index !== -1) {
- vm = children[index]
- } else {
- vm = children.find(v => v.opened)
- this.activeMenuIndex = -1
- }
- if (!vm) {
- this.$set(this.contentStyle, 'zIndex', -1)
- return
- }
- // 动画执行完成后关闭延时关闭
- setTimeout(() => {
- vm.opend = false
- this.emitCloseEvt()
- this.$set(this.contentStyle, 'zIndex', -1)
- }, 300)
- },
- /**
- * 开启动画执行完毕后触发open事件
- * @param {Object} index
- */
- emitOpenEvt(index) {
- setTimeout(() => {
- this.$emit('open', index)
- }, 300)
- },
- /**
- * 切换下拉tab时触发
- * @param {Object} index
- */
- emitChangeEvt(index) {
- this.$emit('change', index)
- },
- /**
- * 关闭动画执行完成后触发close事件
- */
- emitCloseEvt() {
- this.$emit('close')
- },
-
- /**
- * 获取content内容高度
- */
- getContentHeight() {
- const that = this
- const windowHeight = uni.getSystemInfoSync().windowHeight
- uni.createSelectorQuery()
- .in(this)
- .select('.wk-dropdown')
- .boundingClientRect(res => {
- // console.log('getContentHeight: ', res)
- if (!res) return
- const height = windowHeight - res.bottom
- that.$set(that.contentStyle, 'height', height)
- that.maskStyle = {
- height: height + 'px',
- top: res.bottom + 'px',
- opacity: 0,
- zIndex: 1
- }
- this.contentHeight = height
- })
- .exec()
- }
- }
- }
- </script>
- <style scoped lang="scss">
- .wk-dropdown {
- position: relative;
- width: 100%;
- height: 80rpx;
- padding: 0 34rpx;
- @include center;
-
- .wk-dropdown-menu {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- &_item {
- flex: 1;
- font-size: 26rpx;
- @include center;
-
- &__text {
- line-height: 1;
- margin: 0 10rpx;
- }
-
- &__left-icon {
- font-size: 26rpx;
- align-content: center;
- }
-
- &__icon {
- transform: rotate(90deg);
- font-size: 24rpx;
- align-content: center;
- will-change: transform;
- transition: transform ease .3s;
- }
- }
-
- .wk-dropdown-menu_item.active {
- .wk-dropdown-menu_item__icon {
- transform: rotate(-90deg);
- }
- }
- }
-
- .wk-dropdown-content {
- position: absolute;
- top: 80rpx;
- left: 0;
- z-index: 11;
- width: 750rpx;
- font-size: 28rpx;
- overflow: hidden;
- &__pop {
- position: absolute;
- z-index: 100;
- width: 100%;
- background-color: white;
- border-top: 1rpx solid $border-color;
- will-change: transform;
- transition: transform ease 0.3s;
- transform: translateY(-100%);
- overflow: hidden;
- display: flex;
- flex-direction: column;
- }
- &__mask {
- position: fixed;
- bottom: 0;
- left: 0;
- width: 100%;
- background-color: rgba(0, 0, 0, .4);
- opacity: 0;
- will-change: opacity;
- transition: opacity ease 0.3s;
- }
- }
- }
- </style>
|