changes
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
class="field has-addons"
|
||||
:id="$id()"
|
||||
>
|
||||
<div class="control has-icons-left has-icons-right is-expanded">
|
||||
<div :class="['control has-icons-left has-icons-right is-expanded', isLoading && 'is-loading']">
|
||||
<div
|
||||
:class="['dropdown', pos, { 'is-active': focused }]"
|
||||
style="width: 100%"
|
||||
@@ -26,7 +26,7 @@
|
||||
@blur="lostFocus"
|
||||
@keyup.enter="pressEnter"
|
||||
@keyup.esc="lostFocus"
|
||||
@keyup="beginSearch"
|
||||
@input="(e) => debouncedGetSuggestions(e.target.value)"
|
||||
v-model="value"
|
||||
:placeholder="placeholder"
|
||||
/>
|
||||
@@ -47,20 +47,18 @@
|
||||
class="dropdown-menu"
|
||||
style="min-width: 100%"
|
||||
role="menu"
|
||||
@click="doClick()"
|
||||
>
|
||||
<div
|
||||
class="dropdown-content px-3"
|
||||
class="dropdown-content"
|
||||
style="min-width: 100%"
|
||||
>
|
||||
<ScrollBox
|
||||
v-if="data.length > 0"
|
||||
v-if="suggestions.length > 0"
|
||||
v-bind="{
|
||||
data: data,
|
||||
data: suggestions,
|
||||
name: field,
|
||||
fontsize: 14,
|
||||
maxheight: '200px',
|
||||
notick: true,
|
||||
maxHeight: '200px',
|
||||
}"
|
||||
@selected="choose"
|
||||
/>
|
||||
@@ -68,12 +66,7 @@
|
||||
v-else
|
||||
class="has-text-grey px-2 py-1"
|
||||
>
|
||||
<Icon
|
||||
v-if="isLoading"
|
||||
name="svg-spinners:90-ring"
|
||||
:size="22"
|
||||
/>
|
||||
<span v-else>Không có giá trị thoả mãn</span>
|
||||
Không có giá trị thoả mãn
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,12 +109,12 @@
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="control"
|
||||
v-if="addon"
|
||||
class="control"
|
||||
>
|
||||
<button
|
||||
class="button is-success is-light"
|
||||
@click="addNew()"
|
||||
@click="addNew"
|
||||
style="height: 100%"
|
||||
type="button"
|
||||
>
|
||||
@@ -144,6 +137,9 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ScrollBox from "@/components/datatable/ScrollBox.vue";
|
||||
import { debounce } from "es-toolkit";
|
||||
|
||||
const props = defineProps({
|
||||
api: String,
|
||||
field: String,
|
||||
@@ -160,20 +156,17 @@ const props = defineProps({
|
||||
placeholder: String,
|
||||
searchfield: Array,
|
||||
});
|
||||
|
||||
const { $copy, $dialog, $empty, $find, $findapi, $findIndex, $getdata, $id, $nonAccent, $store } = useNuxtApp();
|
||||
const emit = defineEmits(["option", "modalevent"]);
|
||||
const search = ref();
|
||||
const data = ref([]);
|
||||
const suggestions = ref([]);
|
||||
const isLoading = ref(false);
|
||||
const timer = ref();
|
||||
const value = ref();
|
||||
const selected = ref();
|
||||
const showmodal = ref();
|
||||
const params = ref(props.api && $findapi(props.api).params);
|
||||
const orgdata = ref();
|
||||
const focused = ref(false);
|
||||
const count1 = ref(0);
|
||||
const count2 = ref(0);
|
||||
const pos = ref();
|
||||
|
||||
getPos();
|
||||
@@ -183,12 +176,12 @@ if (props.vdata) {
|
||||
orgdata.value.forEach((v) => (v.search = $nonAccent(v[props.field])));
|
||||
}
|
||||
if (props.first) {
|
||||
data.value = orgdata.value ? $copy(orgdata.value) : await getData();
|
||||
suggestions.value = orgdata.value ? $copy(orgdata.value) : await getData();
|
||||
if (props.optionid) {
|
||||
let f = {};
|
||||
f[props.field] = props.optionid;
|
||||
if (props.optionid > 0) f = { id: props.optionid };
|
||||
selected.value = $find(data.value, f);
|
||||
selected.value = $find(suggestions.value, f);
|
||||
if (selected.value && props.vdata) {
|
||||
value.value = selected.value[props.field];
|
||||
}
|
||||
@@ -201,7 +194,7 @@ if (selected.value) doSelect(selected.value);
|
||||
watch(
|
||||
() => props.optionid,
|
||||
() => {
|
||||
if (props.optionid) selected.value = $find(data.value, { id: props.optionid });
|
||||
if (props.optionid) selected.value = $find(suggestions.value, { id: props.optionid });
|
||||
if (selected.value) doSelect(selected.value);
|
||||
else value.value = undefined;
|
||||
},
|
||||
@@ -210,7 +203,7 @@ watch(
|
||||
watch(
|
||||
() => props.filter,
|
||||
async () => {
|
||||
data.value = await getData();
|
||||
suggestions.value = await getData();
|
||||
},
|
||||
);
|
||||
|
||||
@@ -220,10 +213,10 @@ watch(
|
||||
if (newVal) {
|
||||
orgdata.value = $copy(props.vdata);
|
||||
orgdata.value.forEach((v) => (v.search = $nonAccent(v[props.field])));
|
||||
data.value = $copy(orgdata.value);
|
||||
suggestions.value = $copy(orgdata.value);
|
||||
selected.value = undefined;
|
||||
value.value = undefined;
|
||||
if (props.optionid) selected.value = $find(data.value, { id: props.optionid });
|
||||
if (props.optionid) selected.value = $find(suggestions.value, { id: props.optionid });
|
||||
if (selected.value) doSelect(selected.value);
|
||||
}
|
||||
},
|
||||
@@ -231,30 +224,22 @@ watch(
|
||||
|
||||
function choose(v) {
|
||||
focused.value = false;
|
||||
count1.value = 0;
|
||||
count2.value = 0;
|
||||
doSelect(v);
|
||||
}
|
||||
|
||||
function setFocus() {
|
||||
focused.value = true;
|
||||
count1.value = 0;
|
||||
count2.value = 0;
|
||||
}
|
||||
|
||||
function lostFocus() {
|
||||
setTimeout(() => {
|
||||
if (focused.value && count1.value === 0) focused.value = false;
|
||||
if (focused.value) focused.value = false;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function pressEnter() {
|
||||
if (data.value.length === 0) return;
|
||||
choose(data.value[0]);
|
||||
}
|
||||
|
||||
function doClick() {
|
||||
count1.value += 1;
|
||||
if (suggestions.value.length === 0) return;
|
||||
choose(suggestions.value[0]);
|
||||
}
|
||||
|
||||
function doSelect(option) {
|
||||
@@ -272,45 +257,46 @@ function clearValue() {
|
||||
emit("modalevent", { name: "option", data: null });
|
||||
}
|
||||
|
||||
watch(value, (newVal) => {
|
||||
if (newVal === "") clearValue();
|
||||
});
|
||||
|
||||
function findObject(val) {
|
||||
const rows = $copy(orgdata.value);
|
||||
if ($empty(val)) data.value = rows;
|
||||
if ($empty(val)) suggestions.value = rows;
|
||||
else {
|
||||
const text = $nonAccent(val);
|
||||
data.value = rows.filter((v) => v.search.toLowerCase().indexOf(text.toLowerCase()) >= 0);
|
||||
suggestions.value = rows.filter((v) => v.search.toLowerCase().indexOf(text.toLowerCase()) >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
async function getData() {
|
||||
isLoading.value = false;
|
||||
isLoading.value = true;
|
||||
params.value.filter = props.filter;
|
||||
const data = await $getdata(props.api, undefined, params.value);
|
||||
isLoading.value = true;
|
||||
isLoading.value = false;
|
||||
return data;
|
||||
}
|
||||
|
||||
async function getApi(val) {
|
||||
async function getSuggestions(val) {
|
||||
if (props.vdata) return findObject(val);
|
||||
const text = val ? val.toLowerCase() : "";
|
||||
const f = {};
|
||||
const filter_or = {};
|
||||
|
||||
// Sử dụng searchfield nếu có, nếu không thì dùng column
|
||||
const fieldsToSearch = props.searchfield || props.column;
|
||||
|
||||
fieldsToSearch.map((v) => {
|
||||
f[`${v}__icontains`] = text;
|
||||
fieldsToSearch.forEach((v) => {
|
||||
filter_or[`${v}__icontains`] = text;
|
||||
});
|
||||
params.value.filter_or = f;
|
||||
params.value.filter_or = filter_or;
|
||||
if (props.filter) params.value.filter = $copy(props.filter);
|
||||
data.value = await $getdata(props.api, undefined, params.value);
|
||||
isLoading.value = true;
|
||||
suggestions.value = await $getdata(props.api, undefined, params.value);
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
function beginSearch(e) {
|
||||
const val = e.target.value;
|
||||
search.value = val;
|
||||
clearTimeout(timer.value);
|
||||
timer.value = setTimeout(() => getApi(val), 150);
|
||||
}
|
||||
const debouncedGetSuggestions = debounce(getSuggestions, 200);
|
||||
|
||||
function addNew() {
|
||||
showmodal.value = props.addon;
|
||||
@@ -325,14 +311,14 @@ function dataevent(v) {
|
||||
}
|
||||
|
||||
// Tìm và cập nhật trong danh sách
|
||||
const idx = $findIndex(data.value, { id: v.id });
|
||||
const idx = $findIndex(suggestions.value, { id: v.id });
|
||||
if (idx < 0) {
|
||||
// Nếu chưa có trong danh sách, thêm vào đầu
|
||||
data.value.unshift(v);
|
||||
suggestions.value.unshift(v);
|
||||
console.log("Added new item to data:", v);
|
||||
} else {
|
||||
// Nếu đã có, cập nhật
|
||||
data.value[idx] = v;
|
||||
suggestions.value[idx] = v;
|
||||
console.log("Updated existing item in data:", v);
|
||||
}
|
||||
|
||||
@@ -358,7 +344,6 @@ function dataevent(v) {
|
||||
|
||||
// Đóng modal
|
||||
showmodal.value = undefined;
|
||||
console.log("SearchBox data after update:", data.value);
|
||||
}
|
||||
|
||||
function viewInfo() {
|
||||
@@ -391,9 +376,14 @@ function getPos() {
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.control.is-loading::after {
|
||||
inset-inline-end: 2.75em;
|
||||
}
|
||||
|
||||
.field:not(:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.button.is-success {
|
||||
&.is-light {
|
||||
--bulma-button-background-l: 89%;
|
||||
|
||||
Reference in New Issue
Block a user