remove device shadow
This commit is contained in:
@ -1,56 +1,271 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import {
|
||||
nextTick,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
ref,
|
||||
toRefs,
|
||||
watch,
|
||||
watchEffect,
|
||||
} from "vue";
|
||||
import InfiniteLoading from "v3-infinite-loading";
|
||||
import "v3-infinite-loading/lib/style.css"; //required if you're not going to override default slots
|
||||
import "v3-infinite-loading/lib/style.css";
|
||||
import { debounce } from "lodash-es"; //required if you're not going to override default slots
|
||||
import { ElInput, ElPopover, ElScrollbar } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
},
|
||||
modelValue: {},
|
||||
remoteMethod: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
},
|
||||
prefixIcon: { type: String },
|
||||
query: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
prop: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请选择",
|
||||
},
|
||||
defaultLabel: {
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
const placeholder = ref();
|
||||
|
||||
const loadMore = () => {
|
||||
console.log("load more");
|
||||
const { modelValue } = toRefs(props);
|
||||
const loadKey = ref(0);
|
||||
const emit = defineEmits(["update:modelValue", "change", "confirm"]);
|
||||
|
||||
const showPopOver = ref(false);
|
||||
|
||||
const inputRefWhenShowPop = ref();
|
||||
const placeholderWhenShowPop = ref("");
|
||||
const placeholderWhenNotShowPop = ref("");
|
||||
const optionLabelWhenShowPop = ref("");
|
||||
const optionLabelWhenNotShowPop = ref("");
|
||||
const echoLabel = ref("");
|
||||
const options = ref([]);
|
||||
const page = ref(0);
|
||||
|
||||
const initOptions = (keyword) => {
|
||||
props
|
||||
.remoteMethod({
|
||||
[props.query.page]: page.value,
|
||||
[props.query.size]: 10,
|
||||
[props.query.searchKey]: keyword,
|
||||
})
|
||||
.then((rows) => {
|
||||
options.value = rows;
|
||||
});
|
||||
};
|
||||
|
||||
const loadMore = async ($state) => {
|
||||
page.value++;
|
||||
props
|
||||
.remoteMethod({
|
||||
[props.query.page]: page.value,
|
||||
[props.query.size]: 10,
|
||||
[props.query.searchKey]: showPopOver.value
|
||||
? optionLabelWhenShowPop.value ?? null
|
||||
: null,
|
||||
})
|
||||
.then((rows) => {
|
||||
options.value.push(...rows);
|
||||
if (rows.length < 10) {
|
||||
$state.complete();
|
||||
} else {
|
||||
$state.loaded();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
$state.error();
|
||||
});
|
||||
};
|
||||
|
||||
const handleInputClick = () => {
|
||||
showPopOver.value = true;
|
||||
placeholderWhenShowPop.value = optionLabelWhenNotShowPop.value;
|
||||
nextTick(() => {
|
||||
if (inputRefWhenShowPop.value) {
|
||||
inputRefWhenShowPop.value.focus();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除选项
|
||||
*/
|
||||
const handleClearSeletion = () => {
|
||||
emit("update:modelValue", null);
|
||||
};
|
||||
/**
|
||||
* 点击选项
|
||||
* @param option
|
||||
*/
|
||||
const selectOption = (option) => {
|
||||
emit("update:modelValue", option[props.prop.value]);
|
||||
emit("confirm", option);
|
||||
/*重新加载选项*/
|
||||
if (optionLabelWhenShowPop.value) {
|
||||
page.value = 0;
|
||||
options.value = [];
|
||||
loadKey.value++;
|
||||
}
|
||||
|
||||
showPopOver.value = false;
|
||||
|
||||
echoLabel.value = option[props.prop.label];
|
||||
optionLabelWhenShowPop.value = "";
|
||||
};
|
||||
|
||||
/**
|
||||
* 输入时,搜索选项
|
||||
* @type {DebouncedFuncLeading<(function(*): void)|*> | DebouncedFunc<(function(*): void)|*>}
|
||||
*/
|
||||
const handleInputChange = debounce((value) => {
|
||||
page.value = 0;
|
||||
options.value = [];
|
||||
loadKey.value++;
|
||||
}, 500);
|
||||
|
||||
watchEffect(() => {
|
||||
if (showPopOver.value) return;
|
||||
if (modelValue.value) {
|
||||
optionLabelWhenNotShowPop.value =
|
||||
options.value.find((el) => el[props.prop.value] === modelValue.value)?.[
|
||||
props.prop.label
|
||||
] ??
|
||||
echoLabel.value ??
|
||||
props.defaultLabel ??
|
||||
"...";
|
||||
} else {
|
||||
optionLabelWhenNotShowPop.value = "";
|
||||
placeholderWhenNotShowPop.value = props.placeholder;
|
||||
}
|
||||
});
|
||||
|
||||
watch(modelValue, (value) => {
|
||||
emit("change", value);
|
||||
});
|
||||
|
||||
/**
|
||||
* 点击空白关闭弹出选项列表
|
||||
* @param event
|
||||
*/
|
||||
const handleBodyClick = (event) => {
|
||||
if (showPopOver.value) {
|
||||
if (optionLabelWhenShowPop.value) {
|
||||
page.value = 0;
|
||||
options.value = [];
|
||||
loadKey.value++;
|
||||
}
|
||||
showPopOver.value = false;
|
||||
optionLabelWhenShowPop.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
page.value++;
|
||||
initOptions();
|
||||
document.body.addEventListener("click", handleBodyClick);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.body.removeEventListener("click", handleBodyClick);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-popover
|
||||
:popper-style="{
|
||||
padding: 0,
|
||||
}"
|
||||
:width="320"
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
>
|
||||
<template #reference>
|
||||
<div style="width: 320px">
|
||||
<el-input :placeholder="placeholder" />
|
||||
</div>
|
||||
</template>
|
||||
<el-scrollbar class="options-wrap" height="300">
|
||||
<ul class="options">
|
||||
<li v-for="i in 32" :key="i" class="option-item">option</li>
|
||||
</ul>
|
||||
<infinite-loading @infinite="loadMore"> </infinite-loading>
|
||||
</el-scrollbar>
|
||||
</el-popover>
|
||||
</div>
|
||||
<el-popover
|
||||
:popper-style="{
|
||||
padding: 0,
|
||||
}"
|
||||
:visible="showPopOver"
|
||||
:width="width ?? 240"
|
||||
placement="bottom"
|
||||
>
|
||||
<template #reference>
|
||||
<div :style="`width: ${width ?? 240}px`">
|
||||
<!--选项显示时-->
|
||||
<el-input
|
||||
v-if="showPopOver"
|
||||
ref="inputRefWhenShowPop"
|
||||
v-model="optionLabelWhenShowPop"
|
||||
:placeholder="placeholderWhenShowPop"
|
||||
:prefix-icon="prefixIcon"
|
||||
:size="size ?? 'default'"
|
||||
class="select-inner"
|
||||
suffix-icon="ArrowUp"
|
||||
@input="handleInputChange"
|
||||
@click.stop
|
||||
>
|
||||
<template #prefix>
|
||||
<slot name="prefix" />
|
||||
</template>
|
||||
</el-input>
|
||||
<!--选项隐藏时-->
|
||||
<el-input
|
||||
v-else
|
||||
v-model="optionLabelWhenNotShowPop"
|
||||
:placeholder="placeholderWhenNotShowPop"
|
||||
:prefix-icon="prefixIcon"
|
||||
:size="size ?? 'default'"
|
||||
class="select-inner"
|
||||
clearable
|
||||
suffix-icon="ArrowDown"
|
||||
@clear="handleClearSeletion"
|
||||
@click.stop="handleInputClick"
|
||||
>
|
||||
<!-- @blur="handleInputBlur"-->
|
||||
<template #prefix>
|
||||
<slot name="prefix" />
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
<el-scrollbar class="options-wrap" height="260" @click.stop>
|
||||
<ul class="options">
|
||||
<li
|
||||
v-for="option in options"
|
||||
:key="option[prop.value]"
|
||||
:class="`option-item ${
|
||||
option[prop.value] === modelValue ? 'selected' : null
|
||||
}`"
|
||||
@click.stop="selectOption(option)"
|
||||
>
|
||||
{{ option[prop.label] }}
|
||||
</li>
|
||||
</ul>
|
||||
<infinite-loading :identifier="loadKey" @infinite="loadMore">
|
||||
<template #spinner>
|
||||
<div style="text-align: center; font-size: 12px">......</div>
|
||||
</template>
|
||||
<template #complete>
|
||||
<div
|
||||
style="display: flex; justify-content: center; align-items: center"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
</template>
|
||||
</infinite-loading>
|
||||
</el-scrollbar>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.options-wrap {
|
||||
//height: 300px;
|
||||
margin: 6px 0;
|
||||
|
||||
.options {
|
||||
list-style: none;
|
||||
@ -58,13 +273,17 @@ const loadMore = () => {
|
||||
padding: 0;
|
||||
|
||||
.option-item {
|
||||
color: #409eff;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32px 0 20px;
|
||||
font-weight: 700;
|
||||
|
||||
&.selected {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
@ -72,4 +291,8 @@ const loadMore = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-inner {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user