361 lines
10 KiB
Vue
361 lines
10 KiB
Vue
<template>
|
|
<div>
|
|
<v-autocomplete
|
|
v-bind:value="value"
|
|
v-on:input="selectionMade($event)"
|
|
:readonly="readonly"
|
|
:disabled="disabled"
|
|
return-object
|
|
:items="searchResults"
|
|
:label="label"
|
|
item-text="name"
|
|
item-value="id"
|
|
item-disabled="!active"
|
|
:error-messages="errors"
|
|
:loading="fetching"
|
|
:placeholder="$ay.t('Search')"
|
|
:search-input.sync="searchEntry"
|
|
:filter="customFilter"
|
|
hide-no-data
|
|
:clearable="!readonly"
|
|
:no-filter="isTagFilter"
|
|
:append-icon="errorIcon"
|
|
@click:append="handleErrorClick"
|
|
@mousedown="dropdown"
|
|
>
|
|
<template v-slot:prepend-item v-if="hasError()">
|
|
<div class="pl-2">
|
|
<span class="error--text"> {{ errors[0] }}</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-slot:prepend>
|
|
<v-icon @click="handleEditClick">{{ editIcon() }}</v-icon>
|
|
</template>
|
|
</v-autocomplete>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/* eslint-disable */
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//NOTE: have to import lodash directly here as no combination was working with the window.$gz._
|
|
//it would not recognize window in the function call cache-items
|
|
import _ from "../libs/lodash.min.js";
|
|
|
|
export default {
|
|
created() {
|
|
this.fetchValueIfNotPresent();
|
|
},
|
|
data() {
|
|
return {
|
|
searchResults: [],
|
|
errors: [],
|
|
searchEntry: null,
|
|
lastSelection: null,
|
|
fetching: false,
|
|
isTagFilter: false,
|
|
errorIcon: null,
|
|
initialized: false
|
|
};
|
|
},
|
|
props: {
|
|
value: {
|
|
type: Number,
|
|
default: null
|
|
},
|
|
readonly: { type: Boolean, default: false },
|
|
disabled: { type: Boolean, default: false },
|
|
ayaType: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
includeInactive: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
showEditIcon: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
label: { type: String, default: "" }
|
|
},
|
|
watch: {
|
|
value(val) {
|
|
this.fetchValueIfNotPresent();
|
|
},
|
|
searchEntry(val, oldVal) {
|
|
let vm = this;
|
|
//clear any local errors
|
|
vm.clearErrors();
|
|
//if the search entry is in the results list then it's a drop down selection not a typed search so bail
|
|
for (let i = 0; i < vm.searchResults.length; i++) {
|
|
if (vm.searchResults[i].name == val) {
|
|
return;
|
|
}
|
|
}
|
|
if (!val || vm.fetching || !vm.initialized) {
|
|
if (!vm.initialized) {
|
|
vm.$nextTick(() => {
|
|
vm.initialized = true;
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.doSearch(val);
|
|
},
|
|
errors(val) {
|
|
if (this.hasError()) {
|
|
this.errorIcon = "fa-question-circle";
|
|
} else {
|
|
this.errorIcon = null;
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
hasError: function() {
|
|
return this.errors.length > 0;
|
|
},
|
|
clearErrors: function() {
|
|
this.errors = [];
|
|
},
|
|
handleErrorClick: function() {
|
|
//open help nav for picklist
|
|
window.$gz.eventBus.$emit("menu-click", {
|
|
key: "app:help",
|
|
data: "ay-start-form-select-list"
|
|
});
|
|
},
|
|
editIcon: function() {
|
|
if (!this.showEditIcon) {
|
|
return null;
|
|
}
|
|
if (this.value != 0) {
|
|
return "fa-edit";
|
|
}
|
|
return "fa-plus";
|
|
},
|
|
handleEditClick: function() {
|
|
let idToOpen = 0;
|
|
if (this.lastSelection != null && this.lastSelection.id) {
|
|
idToOpen = this.lastSelection.id;
|
|
}
|
|
window.$gz.eventBus.$emit("openobject", {
|
|
type: this.ayaType,
|
|
id: idToOpen
|
|
});
|
|
},
|
|
selectionMade(e) {
|
|
this.clearErrors();
|
|
if (e == undefined) {
|
|
//this will happen when clear clicked
|
|
return;
|
|
}
|
|
if (e.id != null) {
|
|
//this is required for the control to update and parent form to detect it
|
|
this.$emit("input", e.id);
|
|
}
|
|
this.lastSelection = e;
|
|
},
|
|
fetchValueIfNotPresent() {
|
|
//is there a value that might require fetching?
|
|
|
|
let vm = this;
|
|
let val = vm.value;
|
|
if (val == null) {
|
|
return;
|
|
}
|
|
//check if it's in the list of items we have here
|
|
for (let i = 0; i < vm.searchResults.length; i++) {
|
|
if (vm.searchResults[i].id == val) {
|
|
return;
|
|
}
|
|
}
|
|
//is it the no selection item?
|
|
if (val == 0) {
|
|
window.$gz.form.addNoSelectionItem(vm.searchResults);
|
|
} else {
|
|
//Not here, better get it
|
|
let urlParams = "?ayaType=" + vm.ayaType + "&preId=" + vm.value;
|
|
vm.getList(urlParams);
|
|
}
|
|
},
|
|
replaceLastSelection() {
|
|
let vm = this;
|
|
//check if searchResults has last selection, if not then add it back in again
|
|
if (vm.lastSelection == null) {
|
|
//it might be initializing
|
|
for (let i = 0; i < vm.searchResults.length; i++) {
|
|
if (vm.searchResults[i].id == vm.value) {
|
|
vm.lastSelection = vm.searchResults[i];
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.searchResults.length; i++) {
|
|
if (vm.searchResults[i].id == vm.lastSelection.id) {
|
|
return; //already there
|
|
}
|
|
}
|
|
//Not there so insert it
|
|
vm.searchResults.push(vm.lastSelection);
|
|
},
|
|
dropdown(e) {
|
|
let vm = this;
|
|
//check if we have only the initial loaded item and no selection item
|
|
if (vm.searchResults.length < 3) {
|
|
//get the default list
|
|
vm.getList();
|
|
}
|
|
},
|
|
customFilter(item, queryText, itemText) {
|
|
//NOTE: I wanted this to work with tags but all it does is highlight all of each row if tag query is present
|
|
//I guess because it later on attempts to do the highlighting and can't find all the entered query
|
|
//it's not clean so I'm just going to make it only highlight if it's a non tag query for now
|
|
//and do no filtering (highlighting) at all if it's a tag query
|
|
if (queryText.includes(" ") || queryText.startsWith("..")) {
|
|
this.isTagFilter = true;
|
|
return false;
|
|
}
|
|
if (this.$store.state.globalSettings.searchCaseSensitiveOnly == true) {
|
|
return item.name.indexOf(queryText) > -1;
|
|
} else {
|
|
//need to do a case insensitive filter and hopefully it mirrors postgres at the backend
|
|
return item.name.toLowerCase().indexOf(queryText.toLowerCase()) > -1;
|
|
}
|
|
|
|
//split out just the text search part
|
|
// let searchText = queryText;
|
|
// if (queryText.includes(" ")) {
|
|
// //get the non tag part of query if possible
|
|
// //ignore bad condition of too many terms
|
|
// let searchTerms = queryText.split(" ");
|
|
// if (searchTerms[0].includes("..")) {
|
|
// searchText = searchTerms[1];
|
|
// } else {
|
|
// searchText = searchTerms[0];
|
|
// }
|
|
// }
|
|
},
|
|
getList: function(urlParams) {
|
|
let vm = this;
|
|
if (vm.fetching) {
|
|
return;
|
|
}
|
|
vm.fetching = true;
|
|
//default params for when called on init
|
|
if (!urlParams) {
|
|
urlParams = "?ayaType=" + vm.ayaType;
|
|
if (vm.includeInactive) {
|
|
urlParams += "&inactive=true";
|
|
}
|
|
}
|
|
window.$gz.api
|
|
.get("pick-list/List" + urlParams)
|
|
.then(res => {
|
|
vm.fetching = false;
|
|
if (res.error) {
|
|
throw res.error;
|
|
}
|
|
vm.searchResults = res.data;
|
|
window.$gz.form.addNoSelectionItem(vm.searchResults);
|
|
vm.replaceLastSelection();
|
|
})
|
|
.catch(err => {
|
|
window.$gz.errorHandler.handleFormError(err);
|
|
vm.fetching = false;
|
|
});
|
|
},
|
|
doSearch: _.debounce(function(searchFor) {
|
|
//NOTE debounce with a watcher is a bit different, currently it has to be done exactly this way, nothing else will work properly
|
|
//https://vuejs.org/v2/guide/migration.html#debounce-Param-Attribute-for-v-model-removed
|
|
//-----------------
|
|
|
|
let vm = this;
|
|
let isATwoTermQuery = false;
|
|
let queryTerms = [];
|
|
//NOTE: empty query is valid; it means get the top 100 ordered by template order
|
|
let emptyQuery = false;
|
|
if (searchFor == null || searchFor == "") {
|
|
emptyQuery = true;
|
|
} else {
|
|
//Pre-process the query to validate and send conditionally
|
|
|
|
//get the discrete search terms and verify there are max two
|
|
|
|
if (searchFor.includes(" ")) {
|
|
queryTerms = searchFor.split(" ");
|
|
if (queryTerms.length > 2) {
|
|
vm.errors.push(vm.$ay.t("ErrorPickListQueryInvalid"));
|
|
return;
|
|
}
|
|
isATwoTermQuery = true;
|
|
} else {
|
|
//one term only so push it into array
|
|
queryTerms.push(searchFor);
|
|
//Marker term, will be weeded back out later
|
|
queryTerms.push("[?]");
|
|
}
|
|
|
|
//Now vet the terms
|
|
//Is user in mid entry of a second tag (space only?)
|
|
//will appear as an empty string post split
|
|
if (queryTerms[1] == "") {
|
|
//mid entry of a second term, just return
|
|
return;
|
|
}
|
|
|
|
//Is user in mid entry of tag query, just bounce back
|
|
if (
|
|
queryTerms[0] == "." ||
|
|
queryTerms[0] == ".." ||
|
|
queryTerms[1] == "." ||
|
|
queryTerms[1] == ".."
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (isATwoTermQuery) {
|
|
//check that both terms aren't tags
|
|
if (
|
|
window.$gz._.startsWith(queryTerms[0], "..") &&
|
|
window.$gz._.startsWith(queryTerms[1], "..")
|
|
) {
|
|
vm.errors.push(vm.$ay.t("ErrorPickListQueryInvalid"));
|
|
return;
|
|
}
|
|
|
|
//check that both aren't non-tags
|
|
if (
|
|
!window.$gz._.startsWith(queryTerms[0], "..") &&
|
|
!window.$gz._.startsWith(queryTerms[1], "..")
|
|
) {
|
|
vm.errors.push(vm.$ay.t("ErrorPickListQueryInvalid"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//build url
|
|
let urlParams = "?ayaType=" + vm.ayaType;
|
|
if (!emptyQuery) {
|
|
let query = queryTerms[0];
|
|
if (queryTerms[1] != "[?]") {
|
|
query += " " + queryTerms[1];
|
|
}
|
|
urlParams += "&query=" + query;
|
|
}
|
|
|
|
if (vm.includeInactive) {
|
|
urlParams += "&inactive=true";
|
|
}
|
|
this.getList(urlParams);
|
|
//------------
|
|
}, 300) //did some checking, 200-300ms seems to be the most common debounce time for ajax search queries
|
|
}
|
|
};
|
|
</script>
|