changes
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
class="field has-addons"
|
class="field has-addons"
|
||||||
:id="$id()"
|
: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
|
<div
|
||||||
:class="['dropdown', pos, { 'is-active': focused }]"
|
:class="['dropdown', pos, { 'is-active': focused }]"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
@blur="lostFocus"
|
@blur="lostFocus"
|
||||||
@keyup.enter="pressEnter"
|
@keyup.enter="pressEnter"
|
||||||
@keyup.esc="lostFocus"
|
@keyup.esc="lostFocus"
|
||||||
@keyup="beginSearch"
|
@input="(e) => debouncedGetSuggestions(e.target.value)"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
/>
|
/>
|
||||||
@@ -47,20 +47,18 @@
|
|||||||
class="dropdown-menu"
|
class="dropdown-menu"
|
||||||
style="min-width: 100%"
|
style="min-width: 100%"
|
||||||
role="menu"
|
role="menu"
|
||||||
@click="doClick()"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="dropdown-content px-3"
|
class="dropdown-content"
|
||||||
style="min-width: 100%"
|
style="min-width: 100%"
|
||||||
>
|
>
|
||||||
<ScrollBox
|
<ScrollBox
|
||||||
v-if="data.length > 0"
|
v-if="suggestions.length > 0"
|
||||||
v-bind="{
|
v-bind="{
|
||||||
data: data,
|
data: suggestions,
|
||||||
name: field,
|
name: field,
|
||||||
fontsize: 14,
|
fontsize: 14,
|
||||||
maxheight: '200px',
|
maxHeight: '200px',
|
||||||
notick: true,
|
|
||||||
}"
|
}"
|
||||||
@selected="choose"
|
@selected="choose"
|
||||||
/>
|
/>
|
||||||
@@ -68,12 +66,7 @@
|
|||||||
v-else
|
v-else
|
||||||
class="has-text-grey px-2 py-1"
|
class="has-text-grey px-2 py-1"
|
||||||
>
|
>
|
||||||
<Icon
|
Không có giá trị thoả mãn
|
||||||
v-if="isLoading"
|
|
||||||
name="svg-spinners:90-ring"
|
|
||||||
:size="22"
|
|
||||||
/>
|
|
||||||
<span v-else>Không có giá trị thoả mãn</span>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -116,12 +109,12 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="control"
|
|
||||||
v-if="addon"
|
v-if="addon"
|
||||||
|
class="control"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="button is-success is-light"
|
class="button is-success is-light"
|
||||||
@click="addNew()"
|
@click="addNew"
|
||||||
style="height: 100%"
|
style="height: 100%"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
@@ -144,6 +137,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import ScrollBox from "@/components/datatable/ScrollBox.vue";
|
||||||
|
import { debounce } from "es-toolkit";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
api: String,
|
api: String,
|
||||||
field: String,
|
field: String,
|
||||||
@@ -160,20 +156,17 @@ const props = defineProps({
|
|||||||
placeholder: String,
|
placeholder: String,
|
||||||
searchfield: Array,
|
searchfield: Array,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { $copy, $dialog, $empty, $find, $findapi, $findIndex, $getdata, $id, $nonAccent, $store } = useNuxtApp();
|
const { $copy, $dialog, $empty, $find, $findapi, $findIndex, $getdata, $id, $nonAccent, $store } = useNuxtApp();
|
||||||
const emit = defineEmits(["option", "modalevent"]);
|
const emit = defineEmits(["option", "modalevent"]);
|
||||||
const search = ref();
|
const suggestions = ref([]);
|
||||||
const data = ref([]);
|
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const timer = ref();
|
|
||||||
const value = ref();
|
const value = ref();
|
||||||
const selected = ref();
|
const selected = ref();
|
||||||
const showmodal = ref();
|
const showmodal = ref();
|
||||||
const params = ref(props.api && $findapi(props.api).params);
|
const params = ref(props.api && $findapi(props.api).params);
|
||||||
const orgdata = ref();
|
const orgdata = ref();
|
||||||
const focused = ref(false);
|
const focused = ref(false);
|
||||||
const count1 = ref(0);
|
|
||||||
const count2 = ref(0);
|
|
||||||
const pos = ref();
|
const pos = ref();
|
||||||
|
|
||||||
getPos();
|
getPos();
|
||||||
@@ -183,12 +176,12 @@ if (props.vdata) {
|
|||||||
orgdata.value.forEach((v) => (v.search = $nonAccent(v[props.field])));
|
orgdata.value.forEach((v) => (v.search = $nonAccent(v[props.field])));
|
||||||
}
|
}
|
||||||
if (props.first) {
|
if (props.first) {
|
||||||
data.value = orgdata.value ? $copy(orgdata.value) : await getData();
|
suggestions.value = orgdata.value ? $copy(orgdata.value) : await getData();
|
||||||
if (props.optionid) {
|
if (props.optionid) {
|
||||||
let f = {};
|
let f = {};
|
||||||
f[props.field] = props.optionid;
|
f[props.field] = props.optionid;
|
||||||
if (props.optionid > 0) f = { id: 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) {
|
if (selected.value && props.vdata) {
|
||||||
value.value = selected.value[props.field];
|
value.value = selected.value[props.field];
|
||||||
}
|
}
|
||||||
@@ -201,7 +194,7 @@ if (selected.value) doSelect(selected.value);
|
|||||||
watch(
|
watch(
|
||||||
() => props.optionid,
|
() => 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);
|
if (selected.value) doSelect(selected.value);
|
||||||
else value.value = undefined;
|
else value.value = undefined;
|
||||||
},
|
},
|
||||||
@@ -210,7 +203,7 @@ watch(
|
|||||||
watch(
|
watch(
|
||||||
() => props.filter,
|
() => props.filter,
|
||||||
async () => {
|
async () => {
|
||||||
data.value = await getData();
|
suggestions.value = await getData();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -220,10 +213,10 @@ watch(
|
|||||||
if (newVal) {
|
if (newVal) {
|
||||||
orgdata.value = $copy(props.vdata);
|
orgdata.value = $copy(props.vdata);
|
||||||
orgdata.value.forEach((v) => (v.search = $nonAccent(v[props.field])));
|
orgdata.value.forEach((v) => (v.search = $nonAccent(v[props.field])));
|
||||||
data.value = $copy(orgdata.value);
|
suggestions.value = $copy(orgdata.value);
|
||||||
selected.value = undefined;
|
selected.value = undefined;
|
||||||
value.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);
|
if (selected.value) doSelect(selected.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -231,30 +224,22 @@ watch(
|
|||||||
|
|
||||||
function choose(v) {
|
function choose(v) {
|
||||||
focused.value = false;
|
focused.value = false;
|
||||||
count1.value = 0;
|
|
||||||
count2.value = 0;
|
|
||||||
doSelect(v);
|
doSelect(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFocus() {
|
function setFocus() {
|
||||||
focused.value = true;
|
focused.value = true;
|
||||||
count1.value = 0;
|
|
||||||
count2.value = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function lostFocus() {
|
function lostFocus() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (focused.value && count1.value === 0) focused.value = false;
|
if (focused.value) focused.value = false;
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pressEnter() {
|
function pressEnter() {
|
||||||
if (data.value.length === 0) return;
|
if (suggestions.value.length === 0) return;
|
||||||
choose(data.value[0]);
|
choose(suggestions.value[0]);
|
||||||
}
|
|
||||||
|
|
||||||
function doClick() {
|
|
||||||
count1.value += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function doSelect(option) {
|
function doSelect(option) {
|
||||||
@@ -272,45 +257,46 @@ function clearValue() {
|
|||||||
emit("modalevent", { name: "option", data: null });
|
emit("modalevent", { name: "option", data: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(value, (newVal) => {
|
||||||
|
if (newVal === "") clearValue();
|
||||||
|
});
|
||||||
|
|
||||||
function findObject(val) {
|
function findObject(val) {
|
||||||
const rows = $copy(orgdata.value);
|
const rows = $copy(orgdata.value);
|
||||||
if ($empty(val)) data.value = rows;
|
if ($empty(val)) suggestions.value = rows;
|
||||||
else {
|
else {
|
||||||
const text = $nonAccent(val);
|
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() {
|
async function getData() {
|
||||||
isLoading.value = false;
|
isLoading.value = true;
|
||||||
params.value.filter = props.filter;
|
params.value.filter = props.filter;
|
||||||
const data = await $getdata(props.api, undefined, params.value);
|
const data = await $getdata(props.api, undefined, params.value);
|
||||||
isLoading.value = true;
|
isLoading.value = false;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getApi(val) {
|
async function getSuggestions(val) {
|
||||||
if (props.vdata) return findObject(val);
|
if (props.vdata) return findObject(val);
|
||||||
const text = val ? val.toLowerCase() : "";
|
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
|
// Sử dụng searchfield nếu có, nếu không thì dùng column
|
||||||
const fieldsToSearch = props.searchfield || props.column;
|
const fieldsToSearch = props.searchfield || props.column;
|
||||||
|
|
||||||
fieldsToSearch.map((v) => {
|
fieldsToSearch.forEach((v) => {
|
||||||
f[`${v}__icontains`] = text;
|
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);
|
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 debouncedGetSuggestions = debounce(getSuggestions, 200);
|
||||||
const val = e.target.value;
|
|
||||||
search.value = val;
|
|
||||||
clearTimeout(timer.value);
|
|
||||||
timer.value = setTimeout(() => getApi(val), 150);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addNew() {
|
function addNew() {
|
||||||
showmodal.value = props.addon;
|
showmodal.value = props.addon;
|
||||||
@@ -325,14 +311,14 @@ function dataevent(v) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tìm và cập nhật trong danh sách
|
// 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) {
|
if (idx < 0) {
|
||||||
// Nếu chưa có trong danh sách, thêm vào đầu
|
// 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);
|
console.log("Added new item to data:", v);
|
||||||
} else {
|
} else {
|
||||||
// Nếu đã có, cập nhật
|
// Nếu đã có, cập nhật
|
||||||
data.value[idx] = v;
|
suggestions.value[idx] = v;
|
||||||
console.log("Updated existing item in data:", v);
|
console.log("Updated existing item in data:", v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +344,6 @@ function dataevent(v) {
|
|||||||
|
|
||||||
// Đóng modal
|
// Đóng modal
|
||||||
showmodal.value = undefined;
|
showmodal.value = undefined;
|
||||||
console.log("SearchBox data after update:", data.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewInfo() {
|
function viewInfo() {
|
||||||
@@ -391,9 +376,14 @@ function getPos() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.control.is-loading::after {
|
||||||
|
inset-inline-end: 2.75em;
|
||||||
|
}
|
||||||
|
|
||||||
.field:not(:last-child) {
|
.field:not(:last-child) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.is-success {
|
.button.is-success {
|
||||||
&.is-light {
|
&.is-light {
|
||||||
--bulma-button-background-l: 89%;
|
--bulma-button-background-l: 89%;
|
||||||
|
|||||||
@@ -406,7 +406,7 @@
|
|||||||
v-bind="{
|
v-bind="{
|
||||||
data: props.filterData,
|
data: props.filterData,
|
||||||
name: props.field.name,
|
name: props.field.name,
|
||||||
maxheight: '380px',
|
maxHeight: '380px',
|
||||||
perpage: 20,
|
perpage: 20,
|
||||||
}"
|
}"
|
||||||
@selected="doSelect"
|
@selected="doSelect"
|
||||||
|
|||||||
@@ -349,9 +349,6 @@ const getStyle = function (field, record) {
|
|||||||
return val;
|
return val;
|
||||||
};
|
};
|
||||||
const getSettingStyle = function (name, field) {
|
const getSettingStyle = function (name, field) {
|
||||||
if (name === "table") {
|
|
||||||
console.log("tablesetting", tablesetting);
|
|
||||||
}
|
|
||||||
if (!tablesetting || tablesetting.length === 0) {
|
if (!tablesetting || tablesetting.length === 0) {
|
||||||
// manual temp fix
|
// manual temp fix
|
||||||
tablesetting = [
|
tablesetting = [
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :style="`max-height: ${maxheight}; overflow-y: auto;`">
|
<div
|
||||||
|
:style="{
|
||||||
|
maxHeight,
|
||||||
|
overflowY: 'auto',
|
||||||
|
}"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(v, i) in rows"
|
v-for="(v, i) in rows"
|
||||||
:key="i"
|
:key="i"
|
||||||
class="field is-grouped my-0"
|
class=""
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="button is-white fs-14 font-normal w-full is-justify-content-start py-1.5"
|
class="button is-white rounded-none fs-14 font-normal w-full is-justify-content-space-between"
|
||||||
type="button"
|
type="button"
|
||||||
@click="doClick(v, i)"
|
@click="doClick(v, i)"
|
||||||
>
|
>
|
||||||
<span>{{ $stripHtml(v[name] || v.fullname || v.code || "n/a", 75) }}</span>
|
<span>{{ $stripHtml(v[name] || v.fullname || v.code || "n/a", 75) }}</span>
|
||||||
<span
|
<span
|
||||||
v-if="checked[i] && notick !== true"
|
v-if="checked[i] && notick !== true"
|
||||||
class="icon has-text-primary"
|
class="icon right-3"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name="material-symbols:check-rounded"
|
name="material-symbols:check-rounded"
|
||||||
:size="17"
|
:size="17"
|
||||||
|
class="has-text-primary"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -82,85 +88,98 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: ["data", "name", "maxheight", "perpage", "sort", "selects", "keyval", "show", "notick"],
|
data: Array,
|
||||||
data() {
|
name: String,
|
||||||
return {
|
maxHeight: String,
|
||||||
currentPage: 1,
|
perpage: Number,
|
||||||
total: this.data.length,
|
sort: String,
|
||||||
rows: this.data.slice(0, this.perpage),
|
selects: String,
|
||||||
selected: [],
|
keyval: String,
|
||||||
checked: {},
|
show: Object,
|
||||||
time: undefined,
|
notick: Boolean,
|
||||||
array: [],
|
});
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.getdata();
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
data: function (newVal) {
|
|
||||||
this.getdata();
|
|
||||||
},
|
|
||||||
selects: function (newVal) {
|
|
||||||
this.getSelect();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getdata() {
|
|
||||||
this.currentPage = 1;
|
|
||||||
this.array = this.$copy(this.data);
|
|
||||||
if (this.sort) {
|
|
||||||
const f = {};
|
|
||||||
if (this.show?.time) {
|
|
||||||
f.create_time = "desc";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.sort.startsWith("-")) {
|
const emit = defineEmits(["selected"]);
|
||||||
f[this.sort.slice(1)] = "desc";
|
const { $copy, $multiSort, $remove } = useNuxtApp();
|
||||||
} else {
|
const currentPage = ref(1);
|
||||||
f[this.sort] = "asc";
|
const total = ref(props.data.length);
|
||||||
}
|
const rows = ref(props.data.slice(0, props.perpage));
|
||||||
|
const selected = ref([]);
|
||||||
|
const checked = ref({});
|
||||||
|
const array = ref([]);
|
||||||
|
|
||||||
this.$multiSort(this.array, f);
|
getdata();
|
||||||
}
|
|
||||||
this.rows = this.array.slice(0, this.perpage);
|
watch(
|
||||||
this.getSelect();
|
() => props.data,
|
||||||
},
|
() => getdata(),
|
||||||
getSelect() {
|
{ deep: true },
|
||||||
if (!this.selects) return;
|
);
|
||||||
this.selected = [];
|
|
||||||
this.checked = {};
|
watch(
|
||||||
this.selects.map((v) => {
|
() => props.selects,
|
||||||
let idx = this.rows.findIndex((x) => x[this.keyval ? this.keyval : this.name] === v);
|
(newVal) => {
|
||||||
if (idx >= 0) {
|
console.log("props.selects changed", newVal);
|
||||||
this.selected.push(this.rows[idx]);
|
getSelect();
|
||||||
this.checked[idx] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
doClick(v, i, type) {
|
|
||||||
this.checked[i] = this.checked[i] ? false : true;
|
|
||||||
this.checked = this.$copy(this.checked);
|
|
||||||
let idx = this.selected.findIndex((x) => x.id === v.id);
|
|
||||||
idx >= 0 ? this.$remove(this.selected) : this.selected.push(v);
|
|
||||||
this.$emit("selected", v, type);
|
|
||||||
},
|
|
||||||
handleScroll(e) {
|
|
||||||
const bottom = e.target.scrollHeight - e.target.scrollTop - 5 < e.target.clientHeight;
|
|
||||||
if (bottom) {
|
|
||||||
if (this.total ? this.total > this.rows.length : true) {
|
|
||||||
this.currentPage += 1;
|
|
||||||
let arr = this.array.filter(
|
|
||||||
(ele, index) => index >= (this.currentPage - 1) * this.perpage && index < this.currentPage * this.perpage,
|
|
||||||
);
|
|
||||||
this.rows = this.rows.concat(arr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
function getdata() {
|
||||||
|
currentPage.value = 1;
|
||||||
|
array.value = $copy(props.data);
|
||||||
|
if (props.sort) {
|
||||||
|
const f = {};
|
||||||
|
if (props.show?.time) {
|
||||||
|
f.create_time = "desc";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.sort.startsWith("-")) {
|
||||||
|
f[props.sort.slice(1)] = "desc";
|
||||||
|
} else {
|
||||||
|
f[props.sort] = "asc";
|
||||||
|
}
|
||||||
|
|
||||||
|
$multiSort(array.value, f);
|
||||||
|
}
|
||||||
|
rows.value = array.value.slice(0, props.perpage);
|
||||||
|
getSelect();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelect() {
|
||||||
|
if (!props.selects) return;
|
||||||
|
selected.value = [];
|
||||||
|
checked.value = {};
|
||||||
|
props.selects.map((v) => {
|
||||||
|
const idx = rows.value.findIndex((x) => x[props.keyval ? props.keyval : props.name] === v);
|
||||||
|
if (idx >= 0) {
|
||||||
|
selected.value.push(rows.value[idx]);
|
||||||
|
checked.value[idx] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doClick(v, i, type) {
|
||||||
|
checked.value = { [i]: true };
|
||||||
|
const idx = selected.value.findIndex((x) => x.id === v.id);
|
||||||
|
idx >= 0 ? $remove(selected.value) : selected.value.push(v);
|
||||||
|
emit("selected", v, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScroll(e) {
|
||||||
|
const bottom = e.target.scrollHeight - e.target.scrollTop - 5 < e.target.clientHeight;
|
||||||
|
if (bottom) {
|
||||||
|
if (total.value ? total.value > rows.value.length : true) {
|
||||||
|
currentPage.value += 1;
|
||||||
|
let arr = array.value.filter(
|
||||||
|
(_, index) => index >= (currentPage.value - 1) * props.perpage && index < currentPage.value * props.perpage,
|
||||||
|
);
|
||||||
|
rows.value = rows.value.concat(arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.button.is-ghost {
|
.button.is-ghost {
|
||||||
|
|||||||
@@ -73,9 +73,23 @@ export default defineNuxtPlugin(() => {
|
|||||||
return val === undefined || val === null || val === "";
|
return val === undefined || val === null || val === "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toRawDeep = function (observed) {
|
||||||
|
const val = toRaw(observed);
|
||||||
|
|
||||||
|
if (val instanceof Date) return val;
|
||||||
|
if (Array.isArray(val)) return val.map(toRawDeep);
|
||||||
|
if (val === null) return null;
|
||||||
|
|
||||||
|
if (typeof val === "object") {
|
||||||
|
const entries = Object.entries(val).map(([key, val]) => [key, toRawDeep(val)]);
|
||||||
|
return Object.fromEntries(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
const copy = function (val) {
|
const copy = function (val) {
|
||||||
if (empty(val)) return val;
|
return structuredClone(toRawDeep(val));
|
||||||
return JSON.parse(JSON.stringify(val));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const clone = function (obj) {
|
const clone = function (obj) {
|
||||||
|
|||||||
@@ -573,6 +573,10 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|||||||
params: {
|
params: {
|
||||||
values:
|
values:
|
||||||
"id,code,name,manufacturer,manufacturer__name,os,os__name,battery,battery__code,screen,cpu,cpu__name,gpu,gpu__name,camera_system,camera_system__code,sim,sim__code,network_technology,network_technology__name,charging_technology,charging_technology__code,external_storage,external_storage__max_capacity,ip_rating,ip_rating__code,design,create_time,update_time",
|
"id,code,name,manufacturer,manufacturer__name,os,os__name,battery,battery__code,screen,cpu,cpu__name,gpu,gpu__name,camera_system,camera_system__code,sim,sim__code,network_technology,network_technology__name,charging_technology,charging_technology__code,external_storage,external_storage__max_capacity,ip_rating,ip_rating__code,design,create_time,update_time",
|
||||||
|
distinct_values: {
|
||||||
|
label: { type: "Concat", field: ["name", "os__name", "manufacturer__name"] },
|
||||||
|
},
|
||||||
|
summary: "annotate",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
4
build.sh
4
build.sh
@@ -6,5 +6,5 @@ python3 envprod.py
|
|||||||
PROJECT="erp"
|
PROJECT="erp"
|
||||||
IMAGE="web"
|
IMAGE="web"
|
||||||
|
|
||||||
docker build -t docker.bigdatatech.vn/$PROJECT/$IMAGE:latest .
|
docker build -t docker.bigdatatech.vn/erp/web:latest .
|
||||||
docker push docker.bigdatatech.vn/$PROJECT/$IMAGE:latest
|
docker push docker.bigdatatech.vn/erp/web:latest
|
||||||
|
|||||||
Reference in New Issue
Block a user