| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- <template>
- <view class=" h-full">
- <view :id="`dropdown-trigger-${config.prop}`" class="w-full flex items-center justify-center gap-10 h-full relative"
- @click="handleClick">
- <view class="relative">
- <view class="text-xs text-center ellipsis-1" :class="[show ? 'text-primary' : 'text-fore-title ']">
- {{ config.label }}
- </view>
- <view class="absolute -top-4 -right-4 w-6 h-6 bg-red-500 rounded-full" v-if="hasValue"></view>
- </view>
- <view :class="['transition-transform duration-300 ease-out', { 'rotate-180': show }]">
- <uv-icon name="arrow-down" :color="show ? 'primary' : 'info'" size="14" />
- </view>
- </view>
- <view v-show="isOpen" class="fixed left-0 right-0 z-[9999]" :style="containerStyle">
- <view class="fixed z-0 overflow-hidden" :id="`dropdown-content-${config.prop}`" :style="maskStyle"
- @click="handleMaskClick">
- </view>
- <view class="relative z-1 overflow-hidden ">
- <view class="left-0 right-0 z-1 w-full bg-white box-border transition-transform duration-300 ease-out"
- :style="contentStyle" @click="">
- <scroll-view class="relative max-h-[300px]" scroll-y>
- <uv-checkbox-group v-model="checkboxValue" placement="column" iconPlacement="right" borderBottom>
- <uv-checkbox :customStyle="{ marginBottom: '0', paddingBottom: '0', height: '38px', padding: '0 10px' }"
- v-for="(item, index) in config.options" :key="index" :label="item.label"
- :name="item.value"></uv-checkbox>
- </uv-checkbox-group>
- </scroll-view>
- <view class="flex items-center justify-between gap-24 p-[12px]">
- <view class="flex-1">
- <uv-button type="primary" plain shape="circle" @click="handleReset">重置</uv-button>
- </view>
- <view class="flex-1">
- <uv-button type="primary" shape="circle" @click="handleSubmit">
- <text>确定</text>
- <text v-if="checkboxValue.length > 0">({{ checkboxValue.length }})</text>
- </uv-button>
- </view>
- </view>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script lang="ts" setup>
- import { type Dropdown } from '@/types';
- import { DROPDOWN_SYMBOL } from './ie-dropdown-hooks';
- import { getCurrentInstance } from 'vue';
- const instance = getCurrentInstance();
- const props = defineProps<{
- config: Dropdown.DropdownItem;
- index: number;
- // 是否使用绝对定位
- absolute?: boolean;
- }>();
- const dropdown = inject(DROPDOWN_SYMBOL);
- const triggerSelector = `#dropdown-trigger-${props.config.prop}`;
- const isOpen = ref(false);
- const show = ref(false);
- const isMeasuringHeight = ref(false);
- const top = ref(0);
- const hasValue = computed(() => {
- return dropdown?.form.value[props.config.prop] && dropdown?.form.value[props.config.prop].length > 0;
- });
- const checkboxValue = ref([]);
- const containerStyle = computed(() => {
- return {
- top: `${top.value + 1}px`,
- }
- });
- const maskStyle = computed(() => {
- return {
- top: `${top.value + 1}px`,
- left: 0,
- right: 0,
- bottom: 0,
- backgroundColor: 'rgba(0, 0, 0, 0.3)',
- opacity: show.value ? 1 : 0,
- transition: 'opacity 0.3s ease-out',
- }
- });
- const contentStyle = computed(() => {
- return {
- transform: `translateY(${show.value ? '0' : '-100%'})`,
- }
- });
- watch(() => dropdown?.openIndex.value, (newVal) => {
- if (newVal === props.index) {
- open();
- } else {
- if (isOpen.value) {
- close();
- }
- }
- });
- const handleReset = () => {
- checkboxValue.value = [];
- }
- const handleSubmit = () => {
- dropdown?.submit(props.index, checkboxValue.value);
- dropdown?.close();
- }
- const open = async () => {
- checkboxValue.value = dropdown?.form.value[props.config.prop] || [];
- isOpen.value = true;
- const triggerRect = await getRect(triggerSelector);
- if (props.absolute) {
- top.value = triggerRect.height;
- } else {
- top.value = triggerRect.top + triggerRect.height;
- }
- setTimeout(() => {
- nextTick(() => {
- show.value = true;
- });
- }, 20);
- }
- const close = () => {
- show.value = false;
- setTimeout(() => {
- isOpen.value = false;
- }, 300);
- }
- const getRect = (selector: string) => {
- return new Promise((resolve: (rect: { top: number, height: number }) => void) => {
- const query = uni.createSelectorQuery().in(instance?.proxy);
- query.select(selector).boundingClientRect(function (rect) {
- resolve(rect as { top: number, height: number });
- }).exec();
- });
- }
- const measureHeight = (selector: string) => {
- return new Promise((resolve: (height: number) => void) => {
- isMeasuringHeight.value = true;
- setTimeout(() => {
- nextTick(() => {
- getRect(selector).then((res: any) => {
- isMeasuringHeight.value = false;
- setTimeout(() => {
- nextTick(() => {
- resolve(res?.height ?? 0);
- });
- }, 50);
- });
- });
- }, 50);
- });
- }
- const handleMaskClick = () => {
- if (isOpen.value) {
- dropdown?.close();
- }
- }
- const handleClick = () => {
- if (isOpen.value) {
- dropdown?.close();
- } else {
- dropdown?.open(props.index);
- }
- }
- </script>
- <style lang="scss" scoped></style>
|