286 lines
7.1 KiB
Vue
286 lines
7.1 KiB
Vue
<template>
|
|
<div class="ModelLink form-control">
|
|
<VueSelect
|
|
:placeholder="intPlaceholder"
|
|
:loading="loading"
|
|
v-model="intValue"
|
|
:clearable="true"
|
|
label="label"
|
|
:options="data"
|
|
@search="searchAjax"
|
|
:disabled="intReadonly || intDisabled"
|
|
:filterable="true"
|
|
:searchable="true"
|
|
>
|
|
<template #option="{ label }: { label: string }">
|
|
{{
|
|
label.length >= intMaxLength ? label.substring(0, intMaxLength) + ".." : label
|
|
}}
|
|
</template>
|
|
<template
|
|
#no-options="{ search, searching }: { search: string, searching: boolean }"
|
|
>
|
|
<template v-if="searching">
|
|
{{ __("Aucun résultat pour") }}
|
|
<em>{{ search }}</em> .
|
|
</template>
|
|
<em
|
|
v-else
|
|
style="opacity: 0.5"
|
|
>
|
|
{{ __("Chercher un lien du site") }}
|
|
</em>
|
|
</template>
|
|
<template #list-footer>
|
|
<li
|
|
class="pagination"
|
|
v-if="data.length"
|
|
>
|
|
<button
|
|
@click.prevent="page -= 1"
|
|
:disabled="!hasPrevPage"
|
|
class="btn btn-sm btn-primary mt-2 mb-2 ms-2 me-2"
|
|
>
|
|
{{ __("précédent") }}
|
|
</button>
|
|
<button
|
|
@click.prevent="page += 1"
|
|
:disabled="!hasNextPage"
|
|
class="btn btn-sm btn-primary mt-2 mb-2"
|
|
>
|
|
{{ __("suivant") }}
|
|
</button>
|
|
</li>
|
|
</template>
|
|
</VueSelect>
|
|
<input
|
|
v-if="isJson"
|
|
type="hidden"
|
|
:name="String(name)"
|
|
:value="intValue?.value"
|
|
>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent } from "vue";
|
|
import VueSelect from "vue-select";
|
|
import trans from "../../modules/trans";
|
|
import route from "../../modules/route";
|
|
import axios from "axios";
|
|
|
|
type SelectModel = {
|
|
class: string;
|
|
label: string;
|
|
value: string;
|
|
};
|
|
|
|
export default defineComponent({
|
|
mixins: [trans, route],
|
|
components: {
|
|
VueSelect,
|
|
},
|
|
emits: ["input"],
|
|
beforeMount() {
|
|
if (!this.$attrs.json) {
|
|
throw new Error("this component requires json");
|
|
}
|
|
},
|
|
mounted() {
|
|
const json = String(this.$attrs.json ?? "{}");
|
|
const data = JSON.parse(json);
|
|
if (Object.keys(data).length) {
|
|
this.isJson = true;
|
|
this.name = data.name;
|
|
}
|
|
this.intDisabled = this.disabled ? this.disabled : data.disabled ? true : false;
|
|
this.intReadonly = this.readonly ? this.readonly : data.readonly ? true : false;
|
|
this.intPlaceholder = this.placeholder ? this.placeholder : data.placeholder;
|
|
this.intMaxLength = this.maxLength ? this.maxLength : data.maxLength;
|
|
this.parseValue(this.value ? this.value : data.value);
|
|
this.searchAjax();
|
|
},
|
|
props: {
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
readonly: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
placeholder: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
value: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
maxLength: {
|
|
type: Number,
|
|
default: 35,
|
|
},
|
|
},
|
|
data(): {
|
|
isJson: boolean;
|
|
name: string | null;
|
|
intDisabled: boolean;
|
|
intReadonly: boolean;
|
|
intPlaceholder: string;
|
|
intMaxLength: number;
|
|
intValue: SelectModel | null;
|
|
loading: boolean;
|
|
searchText: string;
|
|
data: SelectModel[];
|
|
page: number;
|
|
perPage: number;
|
|
hasNextPage: boolean;
|
|
hasPrevPage: boolean;
|
|
} {
|
|
return {
|
|
isJson: false,
|
|
name: null,
|
|
intDisabled: false,
|
|
intReadonly: false,
|
|
intPlaceholder: "",
|
|
intValue: null,
|
|
loading: false,
|
|
searchText: "",
|
|
data: [],
|
|
page: 1,
|
|
perPage: 15,
|
|
hasNextPage: true,
|
|
hasPrevPage: false,
|
|
intMaxLength: 65,
|
|
};
|
|
},
|
|
watch: {
|
|
page() {
|
|
if (this.page < 1) {
|
|
this.page = 1;
|
|
}
|
|
if (this.page > 1) {
|
|
this.hasPrevPage = true;
|
|
} else {
|
|
this.hasPrevPage = false;
|
|
}
|
|
this.searchAjax();
|
|
},
|
|
data() {
|
|
if (this.data.length == this.perPage) {
|
|
this.hasNextPage = true;
|
|
} else {
|
|
this.hasNextPage = false;
|
|
}
|
|
},
|
|
value() {
|
|
this.parseValue(this.value);
|
|
},
|
|
intValue() {
|
|
this.$emit("input", this.intValue ? this.intValue.value : "");
|
|
},
|
|
},
|
|
methods: {
|
|
searchAjax(search = null) {
|
|
if (search !== false && search !== null) {
|
|
this.searchText = search;
|
|
}
|
|
if (!this.searchText) {
|
|
return;
|
|
}
|
|
let routeUrl = route.methods.route("bo.ranges.search-uri");
|
|
if (!routeUrl) {
|
|
throw new Error("undefined route bo.ranges.search-uri");
|
|
}
|
|
const url = new URL(routeUrl);
|
|
routeUrl = `${url.origin}${url.pathname}?`;
|
|
routeUrl += "page=" + encodeURIComponent(this.page);
|
|
routeUrl += "&search=" + encodeURIComponent(this.searchText);
|
|
this.loading = true;
|
|
axios
|
|
.get(routeUrl)
|
|
.then((response) => {
|
|
this.parseResponse(response.data.data);
|
|
})
|
|
.catch((error) => {
|
|
console.error(error);
|
|
})
|
|
.then(() => {
|
|
this.loading = false;
|
|
});
|
|
},
|
|
parseValue(value: string | null) {
|
|
if (!value) {
|
|
this.intValue = null;
|
|
return;
|
|
}
|
|
let routeUrl = route.methods.route("bo.ranges.model-title");
|
|
if (!routeUrl) {
|
|
throw new Error("undefined route bo.ranges.model-title");
|
|
}
|
|
const url = new URL(routeUrl),
|
|
model = value.replace(/^#/, "").replace(/#$/, "").split("#");
|
|
|
|
routeUrl = `${url.origin}${url.pathname}?`;
|
|
|
|
routeUrl += "model=" + encodeURIComponent(model[0]);
|
|
routeUrl += "&id=" + encodeURIComponent(model[1]);
|
|
axios.get(routeUrl).then((response) => {
|
|
let label = String(response.data as string);
|
|
label =
|
|
label.length >= this.intMaxLength
|
|
? label.substring(0, this.intMaxLength) + ".."
|
|
: label;
|
|
if (label) {
|
|
this.intValue = this.parseResource({
|
|
class: model[0],
|
|
label: label,
|
|
value: model[1],
|
|
});
|
|
} else {
|
|
console.error("empty data response");
|
|
}
|
|
});
|
|
},
|
|
parseResource(model: SelectModel): SelectModel {
|
|
let label = model.label;
|
|
switch (model.class) {
|
|
case "App\\Models\\Recipe":
|
|
label = `${this.__("models.classes.recipes")} - ${model.label}`;
|
|
break;
|
|
case "App\\Models\\Breeder":
|
|
label = `${this.__("models.classes.breeders")} - ${model.label}`;
|
|
break;
|
|
}
|
|
return {
|
|
label: label,
|
|
class: model.class,
|
|
value: `#${model.class}#${model.value}#`,
|
|
};
|
|
},
|
|
parseResponse(data: SelectModel[]) {
|
|
const out: SelectModel[] = [];
|
|
for (const model of data) {
|
|
out.push(this.parseResource(model));
|
|
}
|
|
// pass searchText if is valid url and nothing else is found
|
|
if (out.length) {
|
|
this.data = out;
|
|
}
|
|
},
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "../../../../node_modules/vue-select/dist/vue-select.css";
|
|
|
|
.ModelLink {
|
|
padding: 0 !important;
|
|
|
|
.vs__dropdown-toggle {
|
|
border: 0 !important;
|
|
}
|
|
}
|
|
</style>
|