使用uniapp开发小程序的时候,使用了uview的ui组件,但是里面没有下拉选择组件,只有Picker 选择器,但是我们想要使用下拉选择的组件,所以需要自定义个一个下拉选择的自定义组件,我就只能自己动手创建这个自定义组件了,支持:多选,单选,下拉框在框内还是框外
实现的效果:
自定义组件源代码:Select.vue
-
- <view class="uni-select-dc" :style="{ 'z-index': zindex }">
- <view class="uni-select-dc-select" :class="{ active: active }" @click.stop="handleSelect">
-
- <view class="uni-disabled" v-if="disabled">view>
-
- <view class="close-icon close-postion" v-if="realValue.length && !active && !disabled && showClearIcon">
- <text @click.stop="handleRemove(null)">text>
- view>
-
- <view class="uni-select-multiple" v-show="realValue.length">
- <view class="uni-select-multiple-item" v-if="multiple" v-for="(item, index) in changevalue" :key="index">
- {{ item.text }}
- <view class="close-icon" v-if="showValueClear">
- <text @click.stop="handleRemove(index)">
- text>
- view>
- view>
-
- <view v-else class="single-text">
- {{ changevalue.length ? changevalue[0].text : "" }}
- view>
- view>
-
- <view v-if="realValue.length == 0 && showplaceholder">{{
- placeholder
- }}view>
-
- <view :class="{ disabled: disabled, 'uni-select-dc-icon': !downInner, 'uni-select-dc-inner': downInner }">
- <text>text>
- view>
- view>
-
- <scroll-view class="uni-select-dc-options" :scroll-y="true" v-show="active">
- <template>
- <view class="uni-select-dc-item" :class="{ active: realValue.includes((item as any)[svalue]) }"
- v-for="(item, index) in options" :key="index" @click.stop="handleChange(index, item)">
- {{ (item as any)[slabel] }}
- view>
- template>
- scroll-view>
- view>
- template>
-
- import { onMounted, reactive, ref } from "vue";
-
- const props = defineProps({
- // 是否显示全部清空按钮
- showClearIcon: {
- type: Boolean,
- default: false,
- },
- // 是否多选
- multiple: {
- type: Boolean,
- default: false,
- },
- // 下拉箭头是否在框内
- downInner: {
- type: Boolean,
- default: true,
- },
- // 是否显示单个删除
- showValueClear: {
- type: Boolean,
- default: true,
- },
- zindex: {
- type: Number,
- default: 999,
- },
- // 禁用选择
- disabled: {
- type: Boolean,
- default: false,
- },
- options: {
- type: Array,
- default() {
- return [];
- },
- },
- value: {
- type: Array,
- default() {
- return [];
- },
- },
- placeholder: {
- type: String,
- default: "请选择",
- },
- showplaceholder: {
- type: Boolean,
- default: true,
- },
- // 默认取text
- slabel: {
- type: String,
- default: "text",
- },
- // 默认取value
- svalue: {
- type: String,
- default: "value",
- },
- });
- const emit = defineEmits(["change"]);
- const active = ref
(false); // 组件是否激活, - let changevalue = reactive
>([]); - let realValue = reactive
>([]); - onMounted(() => {
- init();
- });
-
- // 初始化函数
- const init = () => {
- if (props.value.length > 0) {
- props.options.forEach((item) => {
- props.value.forEach((i) => {
- if ((item as any)[props.svalue] === i) {
- changevalue.push(item);
- }
- })
- })
- realValue = props.value;
- console.log("props---", changevalue);
-
- } else {
- changevalue = [];
- realValue = [];
- }
- };
- // 点击展示选项
- const handleSelect = () => {
- if (props.disabled) return;
- active.value = !active.value;
- };
- // 移除数据
- const handleRemove = (index: any) => {
- if (index === null) {
- realValue = [];
- changevalue = [];
- } else {
- realValue.splice(index, 1);
- changevalue.splice(index, 1);
- }
- emit("change", changevalue, realValue);
- };
- // 点击组件某一项
- const handleChange = (index, item) => {
- console.log("选中了某一项", index, item);
- // 如果是单选框,选中一项后直接关闭
- if (!props.multiple) {
- console.log("关闭下拉框");
- changevalue.length = 0
- realValue.length = 0
- changevalue.push(item);
- realValue.push(item[props.svalue])
- active.value = !active.value;
- } else {
- // 多选操作
- const arrIndex = realValue.indexOf(item[props.svalue]);
- if (arrIndex > -1) {
- // 如果该选项已经选中,当点击后就不选中
- changevalue.splice(arrIndex, 1);
- realValue.splice(arrIndex, 1);
- } else {
- // 否则选中该选项
- changevalue.push(item);
- realValue.push(item[props.svalue]);
- }
- }
- // 触发回调函数
- emit("change", changevalue, realValue);
- };
-
- .uni-select-dc {
- position: relative;
- z-index: 999;
-
- .uni-select-mask {
- width: 100%;
- height: 100%;
- }
-
- /* 删除按钮样式*/
- .close-icon {
- height: 100%;
- width: 20px;
- display: flex;
- align-items: center;
- justify-content: center;
- // z-index: 3;
- cursor: pointer;
-
- text {
- position: relative;
- background: #c0c4cc;
- width: 13px;
- height: 13px;
- border-radius: 50%;
- border: 1px solid #bbb;
-
- &::before,
- &::after {
- content: "";
- position: absolute;
- left: 20%;
- top: 50%;
- height: 1px;
- width: 60%;
- transform: rotate(45deg);
- background-color: #909399;
- }
-
- &::after {
- transform: rotate(-45deg);
- }
- }
- }
-
- //所有情空的定位
- .close-postion {
- position: absolute;
- right: 35px;
- top: 0;
- height: 100%;
- width: 15px;
- }
-
- /* 多选盒子 */
- .uni-select-multiple {
- display: flex;
- flex-wrap: nowrap;
- overflow: scroll;
-
- .single-text {
- color: #333;
- }
-
- .uni-select-multiple-item {
- background: #f4f4f5;
- margin-right: 5px;
- padding: 2px 4px;
- border-radius: 4px;
- color: #909399;
- display: flex;
- flex-shrink: 0;
- }
- }
-
- // select部分
- .uni-select-dc-select {
- user-select: none;
- position: relative;
- z-index: 3;
- height: 30px;
- padding: 0 30px 0 10px;
- box-sizing: border-box;
- border-radius: 4px;
- border: 1px solid rgb(229, 229, 229);
- display: flex;
- align-items: center;
- font-size: 12px;
- color: #999;
- min-width: 210px;
-
- .uni-disabled {
- position: absolute;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 19;
- cursor: no-drop;
- background: rgba(255, 255, 255, 0.5);
- }
-
- .uni-select-dc-input {
- font-size: 14px;
- color: #999;
- display: block;
- width: 96%;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- line-height: 30px;
- box-sizing: border-box;
-
- &.active {
- color: #333;
- }
- }
-
- .uni-select-dc-icon {
- cursor: pointer;
- position: absolute;
- right: 0;
- top: 0;
- height: 100%;
- width: 30px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-left: 1px solid rgb(229, 229, 229);
-
- text {
- display: block;
- width: 0;
- height: 0;
- border-width: 12rpx 12rpx 0;
- border-style: solid;
- border-color: #bbb transparent transparent;
- transition: 0.3s;
- }
-
- &.disabled {
- cursor: no-drop;
-
- text {
- width: 20rpx;
- height: 20rpx;
- border: 2px solid #ff0000;
- border-radius: 50%;
- transition: 0.3s;
- position: relative;
- z-index: 999;
-
- &::after {
- content: "";
- position: absolute;
- top: 50%;
- left: 0;
- width: 100%;
- height: 2px;
- margin-top: -1px;
- background-color: #ff0000;
- transform: rotate(45deg);
- }
- }
- }
- }
-
- .uni-select-dc-inner {
- cursor: pointer;
- position: absolute;
- right: 0;
- top: 0;
- height: 100%;
- width: 30px;
- display: flex;
- align-items: center;
- justify-content: center;
-
- text {
- display: block;
- width: 10px;
- height: 10px;
- position: absolute;
- right: 10px;
- top: 6px;
- border: 1px solid #bbb;
- transform: rotate(-45deg);
- border-color: transparent transparent#bbb #bbb;
- transition: 0.3s;
- }
-
- &.disabled {
- cursor: no-drop;
-
- text {
- width: 20rpx;
- height: 20rpx;
- border: 2px solid #ff0000;
- border-radius: 50%;
- transition: 0.3s;
- position: relative;
- z-index: 999;
-
- &::after {
- content: "";
- position: absolute;
- top: 50%;
- left: 0;
- width: 100%;
- height: 2px;
- margin-top: -1px;
- background-color: #ff0000;
- transform: rotate(45deg);
- }
- }
- }
- }
-
- // 激活之后,图标旋转180度
- &.active .uni-select-dc-icon {
- text {
- transform: rotate(180deg);
- }
- }
-
- &.active .uni-select-dc-inner {
- text {
- position: absolute;
- right: 10px;
- top: 12px;
- transform: rotate(-225deg);
- }
- }
- }
-
- // options部分
- .uni-select-dc-options {
- user-select: none;
- position: absolute;
- top: calc(100% + 5px);
- left: 0;
- width: 100%;
- height: 400rpx;
- border-radius: 4px;
- border: 1px solid rgb(229, 229, 229);
- background: #fff;
- padding: 5px 0;
- box-sizing: border-box;
- z-index: 9;
-
- .uni-select-dc-item {
- padding: 0 10px;
- box-sizing: border-box;
- cursor: pointer;
- line-height: 2.5;
- transition: 0.3s;
- font-size: 14px;
-
- &.active {
- color: #409eff;
-
- background-color: #f5f7fa &hover {
- color: #409eff;
- background-color: #f5f7fa;
- }
- }
-
- &:hover {
- background-color: #f5f5f5;
- }
- }
- }
- }
父组件调用:
- <view class="arena-main">
- 自定义下拉选择组件:
- <Select :value="monIndex" downInner :options="options" @change="changeValue">
- Select>
- view>
-
- <script setup lang="ts">
- import Select from "@/components/select/index.vue"
- import { reactive } from "vue";
-
- let monIndex = reactive([1]);
- const changeValue = (item: any, value: any) => {
- console.log("父组件接收到选中的值", item, value);
- monIndex = value;
- };
-
- const options = [
- { value: 0, text: "测试1" },
- { value: 1, text: "测试2" },
- { value: 2, text: "测试3" },
- { value: 3, text: "测试4" },
- { value: 4, text: "测试5" },
- ];
-
-
- script>
-
- <style lang="scss" scoped>
- .arena-main {
- padding: 10px;
- }
- style>
使用参数说明:
- monIndex:选中的值列表
-
- options:可选项列表
-
- multiple:是否为多选
-
- downInner:下拉箭头是否在选择框内
-
- showValueClear:是否显示单个删除
-
- disabled:是否禁用
-
- placeholder:占位符
-
- slabel:label标签展示某项
-
- svalue:value选中的值