<template>
|
<view class="nut-menu-item" v-show="state.showWrapper" :style="style">
|
<view
|
v-show="state.showPopup"
|
@click="handleClickOutside"
|
class="nut-menu-item-placeholder-element"
|
:style="placeholderElementStyle"
|
:catch-move="parent.props.lockScroll"
|
>
|
</view>
|
<nut-popup
|
:style="{ position: 'absolute' }"
|
:overlayStyle="{ position: 'absolute' }"
|
v-bind="$attrs"
|
v-model:visible="state.showPopup"
|
:position="parent.props.direction === 'down' ? 'top' : 'bottom'"
|
:duration="parent.props.duration"
|
:destroy-on-close="false"
|
:overlay="parent.props.overlay"
|
:lockScroll="parent.props.lockScroll"
|
@closed="handleClose"
|
:close-on-click-overlay="parent.props.closeOnClickOverlay"
|
>
|
<scroll-view :scroll-y="true">
|
<view class="nut-menu-item__content" :class="{ noPadding: !options?.length }">
|
<view
|
v-for="(option, index) in options"
|
:key="index"
|
class="nut-menu-item__option"
|
:class="[{ active: optionIsActive(option) }]"
|
:style="{ 'flex-basis': 100 / cols + '%' }"
|
@click="onClick(option)"
|
>
|
<span
|
class="nut-menu-item__span"
|
v-if="optionIsActive(option)"
|
:class="[optionIsActive(option) ? activeTitleClass : inactiveTitleClass]"
|
>
|
<slot name="icon">
|
<Check v-bind="$attrs" :color="parent.props.activeColor"></Check>
|
</slot>
|
</span>
|
<view
|
:class="[optionIsActive(option) ? activeTitleClass : inactiveTitleClass]"
|
:style="{ color: optionIsActive(option) ? parent.props.activeColor : '' }"
|
>{{ option.text }}</view
|
>
|
</view>
|
</view>
|
<slot></slot>
|
</scroll-view>
|
</nut-popup>
|
</view>
|
</template>
|
<script lang="ts">
|
import { MenuItemOption } from '@nutui/nutui-taro/dist/types/__VUE/menuitem/type';
|
import { Check } from '@nutui/icons-vue-taro';
|
import { isArray } from 'lodash';
|
|
export default defineComponent({
|
name: 'bl-menu-item',
|
props: {
|
title: String,
|
options: {
|
type: Array as PropType<MenuItemOption[]>,
|
default: () => [] as MenuItemOption[],
|
},
|
disabled: {
|
type: Boolean,
|
default: false,
|
},
|
modelValue: {
|
type: [Array, String, Number],
|
},
|
cols: {
|
type: Number,
|
default: 1,
|
},
|
activeTitleClass: String,
|
inactiveTitleClass: String,
|
emptyValue: {
|
type: [String, Number],
|
default: null,
|
},
|
emptyTitle: {
|
type: String,
|
default: '',
|
},
|
enableCancelSelect: {
|
type: Boolean,
|
default: true,
|
},
|
multiple: {
|
type: Boolean,
|
default: false,
|
},
|
},
|
components: {
|
Check,
|
},
|
emits: ['update:modelValue', 'change', 'closePopup'],
|
setup(props, { emit }) {
|
const state = reactive({
|
showPopup: false,
|
showWrapper: false,
|
});
|
|
const useParent: any = () => {
|
const parent = inject('menuParent', null);
|
if (parent) {
|
// 获取子组件自己的实例
|
const instance = getCurrentInstance()!;
|
const { link, removeLink } = parent;
|
// @ts-ignore
|
link(instance);
|
onUnmounted(() => {
|
// @ts-ignore
|
removeLink(instance);
|
});
|
return { parent };
|
}
|
};
|
|
const { parent } = useParent();
|
|
const style = computed(() => {
|
return parent.props.direction === 'down'
|
? { top: parent.offset.value + 'px' }
|
: { bottom: parent.offset.value + 'px' };
|
});
|
|
const placeholderElementStyle = computed(() => {
|
const heightStyle = { height: parent.offset.value + 'px' };
|
if (parent.props.direction === 'down') {
|
return { ...heightStyle, top: '0px' };
|
} else {
|
return { ...heightStyle, bottom: '0px' };
|
}
|
});
|
|
const toggle = (show = !state.showPopup) => {
|
if (show === state.showPopup) {
|
return;
|
}
|
state.showPopup = show;
|
if (show) {
|
state.showWrapper = true;
|
}
|
};
|
|
const renderTitle = () => {
|
if (props.title) {
|
return props.title;
|
}
|
if (isArray(props.modelValue)) {
|
const selectedOptions = (props.options ?? [])
|
.filter((option) => (props.modelValue as any).includes(option.value))
|
.map((x) => x.text);
|
return selectedOptions.length > 0 ? selectedOptions.join(',') : props.emptyTitle;
|
}
|
const match: any = props.options?.find((option: any) => option.value === props.modelValue);
|
return match ? match.text : props.emptyTitle;
|
};
|
|
const onClick = (option: MenuItemOption) => {
|
console.log(1);
|
|
state.showPopup = false;
|
if (props.multiple) {
|
const modelValue = props.modelValue as Array<any>;
|
const val = modelValue.includes(option.value)
|
? modelValue.filter((x) => x !== option.value)
|
: [...modelValue, option.text];
|
console.log('val: ', val);
|
|
emit('update:modelValue', val);
|
emit('change', val);
|
} else {
|
if (option.value !== props.modelValue) {
|
emit('update:modelValue', option.value);
|
emit('change', option.value);
|
} else {
|
if (props.enableCancelSelect) {
|
emit('update:modelValue', props.emptyValue);
|
emit('change', props.emptyValue);
|
}
|
}
|
}
|
};
|
|
const handleClose = () => {
|
state.showWrapper = false;
|
};
|
|
const handleClickOutside = () => {
|
state.showPopup = false;
|
emit('closePopup');
|
};
|
|
function optionIsActive(option: MenuItemOption) {
|
return isArray(props.modelValue)
|
? props.modelValue.includes(option.value)
|
: option.value === props.modelValue;
|
}
|
|
return {
|
style,
|
placeholderElementStyle,
|
renderTitle,
|
state,
|
parent,
|
toggle,
|
onClick,
|
handleClose,
|
handleClickOutside,
|
optionIsActive,
|
};
|
},
|
});
|
</script>
|