automated_uniapp/uni_modules/cool-ui/components/cl-select-popup/cl-select-popup.vue
2025-01-09 16:16:11 +08:00

299 lines
5.6 KiB
Vue

<template>
<view class="cl-select-popup" @tap="open" v-if="showPicker">
<slot :label="text" :value="checked">
<cl-select-inner
:height="height"
:placeholder="placeholder"
:disabled="disabled"
:border="border"
:round="round"
:backgroundColor="backgroundColor"
:borderRadius="borderRadius"
:arrowIcon="arrowIcon"
:padding="padding"
:text="text"
/>
</slot>
</view>
<!-- 弹出框 -->
<cl-popup
:ref="setRefs('popup')"
:padding="0"
:title="title"
direction="bottom"
border-radius="24rpx 24rpx 0 0"
show-close-btn
@opened="onOpened"
@close="onClose"
@closed="onClosed"
>
<view class="cl-select-popup__wrap">
<scroll-view
class="cl-select-popup__container"
:ref="setRefs('scroller')"
scroll-y
:scroll-into-view="scroller.view"
:style="{
height: parseRpx(scrollerHeight),
maxHeight: parseRpx(scrollerHeight || scrollerMaxHeight),
}"
>
<slot name="list">
<view class="cl-select-popup__list">
<view
class="cl-select-popup__item"
v-for="(item, index) in options"
:key="index"
:class="{
'is-active': isActive(item.value),
}"
:id="`item-${item.value}`"
@tap="check(item.value)"
>
<slot
name="item"
:item="item"
:active="isActive(item.value)"
:selection="selection"
>
{{ item.label }}
<text class="is-check cl-icon-check"></text>
</slot>
</view>
</view>
<!-- 空态 -->
<view class="cl-select-popup__empty" v-show="options.length == 0">
<cl-empty :fixed="false" />
</view>
</slot>
</scroll-view>
<view class="cl-select-popup__footer" v-if="showFooter">
<slot name="confirm">
<cl-button
round
fill
type="primary"
size="large"
:disabled="required ? selection.length == 0 : false"
@tap="confirm"
>
确定
</cl-button>
</slot>
</view>
</view>
</cl-popup>
</template>
<script lang="ts">
import { defineComponent, ref, type PropType, watch, computed, nextTick, reactive } from "vue";
import { isArray, isEmpty, last } from "lodash-es";
import { useRefs } from "/@/cool";
import { parseRpx } from "/@/cool/utils";
import { Props } from "../cl-select-inner/config";
interface Item {
label: string;
value: any;
[key: string]: any;
}
export default defineComponent({
name: "cl-select-popup",
props: {
...Props,
// 绑定值
modelValue: [String, Number, Array],
// 标题
title: String,
// 滚动高度
scrollerHeight: [String, Number],
// 最大滚动高度
scrollerMaxHeight: {
type: [String, Number],
default: 600,
},
// 选项列表
options: {
type: Array as PropType<Item[]>,
default: () => [],
},
// 是否多选
multiple: Boolean,
// 是否显示选择器
showPicker: {
type: Boolean,
default: true,
},
// 是否显示底部
showFooter: {
type: Boolean,
default: true,
},
// 是否必填
required: Boolean,
},
emits: ["update:modelValue", "change", "confirm", "opened", "close", "closed"],
setup(props, { emit }) {
const { refs, setRefs } = useRefs();
// 已选
const checked = ref<any[]>([]);
// 选中项
const selection = ref<any[]>([]);
// 显示的文本内容
const text = computed(() => {
return checked.value
.map((e) => props.options.find((a) => a.value == e)?.label)
.join("、");
});
// 滚动条
const scroller = reactive({
stop: null as (() => void) | null,
view: "",
clear() {
scroller.stop?.();
scroller.view = "";
},
watch() {
scroller.stop = watch(
() => [checked.value, props.options],
() => {
if (!isEmpty(checked.value) && !isEmpty(props.options)) {
nextTick(() => {
scroller.view = `item-${last(checked.value)}`;
scroller.stop?.();
});
}
},
{
immediate: true,
deep: true,
},
);
},
});
// 打开
function open() {
// 打开弹出
refs.popup?.open();
// 监听是否滚动
scroller.watch();
// 设置选中值
selection.value = [...checked.value];
}
// 打开完成事件
function onOpened() {
emit("opened");
}
// 关闭
function close() {
refs.popup?.close();
}
// 关闭事件
function onClose() {
scroller.clear();
emit("close");
}
// 关闭完成事件
function onClosed() {
emit("closed");
}
// 选中
function check(value: any) {
const i = selection.value.indexOf(value);
if (props.multiple) {
if (i >= 0) {
selection.value.splice(i, 1);
} else {
selection.value.push(value);
}
} else {
if (i >= 0) {
selection.value = [];
} else {
selection.value = [value];
}
}
}
// 确认
function confirm() {
const v = props.multiple ? selection.value : selection.value[0];
emit("update:modelValue", v);
emit("change", v);
emit("confirm", v);
close();
}
// 是否选中
function isActive(value: any) {
return selection.value.includes(value);
}
// 监听值
watch(
() => props.modelValue,
(value) => {
if (isArray(value)) {
checked.value = [...value] || [];
} else {
if (value === undefined) {
checked.value = [];
} else {
checked.value = [value];
}
}
selection.value = checked.value;
},
{
deep: true,
immediate: true,
},
);
return {
refs,
setRefs,
checked,
selection,
text,
scroller,
open,
onOpened,
close,
onClose,
onClosed,
confirm,
check,
isActive,
parseRpx,
};
},
});
</script>