page select 9/10

This commit is contained in:
ailanyin
2023-06-01 17:33:23 +08:00
parent 719669c666
commit 274ce44dfe
12 changed files with 1330 additions and 747 deletions

View File

@ -0,0 +1,306 @@
<script setup>
import { onMounted, ref, toRefs, watch, watchEffect } from "vue";
import InfiniteLoading from "v3-infinite-loading";
import "v3-infinite-loading/lib/style.css";
import { debounce } from "lodash-es"; //required if you're not going to override default slots
const props = defineProps({
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 { modelValue } = toRefs(props);
const loadKey = ref(0);
const emit = defineEmits(["update:modelValue", "change"]);
const showPopOver = ref(false);
const placeholderWhenShowPop = ref("");
const placeholderWhenNotShowPop = ref("");
const optionLabelWhenShowPop = ref("");
const optionLabelWhenNotShowPop = ref("");
const echoLabel = ref("");
const label = ref("");
const inputRef = 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) => {
console.log("loadmore");
page.value++;
props
.remoteMethod({
[props.query.page]: page.value,
[props.query.size]: 10,
[props.query.searchKey]: showPopOver.value
? optionLabelWhenShowPop.value
: null,
})
.then((rows) => {
options.value.push(...rows);
if (rows.length < 10) {
$state.complete();
} else {
$state.loaded();
}
})
.catch(() => {
$state.error();
});
};
const handleShow = () => {};
const handleHide = () => {
console.log("hide");
};
const handleInputFocus = () => {
console.log("focus");
};
const handleInputClick = () => {
showPopOver.value = true;
// if (label.value) {
// page.value = 0;
// options.value = []
// loadKey.value++;
// }
placeholderWhenShowPop.value = optionLabelWhenNotShowPop.value;
};
const handleInputBlur = () => {
showPopOver.value = false;
if (label.value) {
setTimeout(() => {
page.value = 1;
initOptions();
loadKey.value++;
}, 1000);
}
// label.value = placeholder.value;
// placeholder.value = "";
};
/**
* 点击选项
* @param option
*/
const selectOption = (option) => {
emit("update:modelValue", option[props.prop.value]);
/*重新加载选项*/
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 = "";
// optionLabelWhenNotShowPop.value = props.placeholder;
}
};
onMounted(() => {
page.value++;
initOptions();
document.body.addEventListener("click", handleBodyClick);
});
</script>
<template>
<el-popover
:popper-style="{
padding: 0,
}"
:visible="showPopOver"
:width="width ?? 240"
placement="bottom"
@hide="handleHide"
@show="handleShow"
>
<template #reference>
<div :style="`width: ${width ?? 240}px`">
<!--选项显示时-->
<el-input
v-if="showPopOver"
ref="inputRef"
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
ref="inputRef"
v-model="optionLabelWhenNotShowPop"
:placeholder="placeholderWhenNotShowPop"
:prefix-icon="prefixIcon"
:size="size ?? 'default'"
class="select-inner"
suffix-icon="ArrowDown"
@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 {
margin: 6px 0;
.options {
list-style: none;
margin: 0;
padding: 0;
.option-item {
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;
}
}
}
}
.select-inner {
cursor: pointer;
}
</style>