158 lines
4.7 KiB
Vue
158 lines
4.7 KiB
Vue
|
<template>
|
||
|
<div class="input-group">
|
||
|
<span v-if="label" class="input-group-text">{{label}}</span>
|
||
|
<select v-if="selectable_dgnm" class="form-select" style="flex: 0.15 1 auto;" v-model="x_dgnm">
|
||
|
<option v-for="item in ORDERABLE_DGNM" :value="item.dgnm">{{item.name}}</option>
|
||
|
</select>
|
||
|
<input class="form-control" placeholder="Filter..." v-model="x_query" />
|
||
|
</div>
|
||
|
<div class="scroller" ref="scroller">
|
||
|
<table class="table table-striped">
|
||
|
<tbody>
|
||
|
<tr v-for="item in resultset" :class="{ 'table-active': item.IEN == x_modelValue }" @click="x_modelValue = item.IEN">
|
||
|
<td>{{item.IEN}}</td>
|
||
|
<td>{{item.description}}</td>
|
||
|
</tr>
|
||
|
<tr ref="bottom" />
|
||
|
</tbody>
|
||
|
</table>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<style scoped>
|
||
|
div.scroller {
|
||
|
max-height: 25vh;
|
||
|
overflow-y: auto;
|
||
|
}
|
||
|
td {
|
||
|
cursor: default;
|
||
|
}
|
||
|
.table-active, .table-active:nth-of-type(odd) > * {
|
||
|
color: #fff;
|
||
|
background-color: #0d6efd;
|
||
|
}
|
||
|
</style>
|
||
|
|
||
|
<script>
|
||
|
import { debounce } from './util.mjs';
|
||
|
|
||
|
import { ORDERABLE_DGNM } from './constants.mjs';
|
||
|
|
||
|
const SZ_WINDOW = 100;
|
||
|
|
||
|
export default {
|
||
|
props: {
|
||
|
client: Object,
|
||
|
dgnm: String,
|
||
|
label: String,
|
||
|
query: String,
|
||
|
modelValue: String
|
||
|
},
|
||
|
emits: {
|
||
|
'update:dgnm': String,
|
||
|
'update:query': String,
|
||
|
'update:modelValue': String
|
||
|
},
|
||
|
data() {
|
||
|
return {
|
||
|
ORDERABLE_DGNM,
|
||
|
selectable_dgnm: !this.dgnm,
|
||
|
x_dgnm: this.dgnm || 'O RX',
|
||
|
IEN: null,
|
||
|
count: null,
|
||
|
x_query: this.query,
|
||
|
resultset: [],
|
||
|
end: null,
|
||
|
has_more: false,
|
||
|
is_loading: true,
|
||
|
observer_bottom: null,
|
||
|
x_modelValue: this.modelValue
|
||
|
};
|
||
|
},
|
||
|
computed: {
|
||
|
query_view() {
|
||
|
return this.x_query ? this.x_query.replace(/^\s+$/g, '').replace(/\s+/g, ' ').toUpperCase() : ''; // allow trailing space
|
||
|
}
|
||
|
},
|
||
|
watch: {
|
||
|
dgnm(value) { this.x_dgnm = value; },
|
||
|
x_dgnm(value) {
|
||
|
this.$emit('update:dgnm', value);
|
||
|
this.$emit('update:query', this.x_query = '');
|
||
|
},
|
||
|
modelValue(value) { this.x_modelValue = value; },
|
||
|
x_modelValue(value) { this.$emit('update:modelValue', value); },
|
||
|
query(value) { this.x_query = value; },
|
||
|
x_query(value) {
|
||
|
this.$emit('update:query', value);
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
async handle_bottom([entry]) {
|
||
|
if((entry.isIntersecting) && (this.has_more) && (this.end < this.count) && (!this.is_loading)) {
|
||
|
this.is_loading = true;
|
||
|
this.has_more = false;
|
||
|
try {
|
||
|
var batch = await this.client.ORWUL_FVSUB(this.IEN, this.end + 1, this.end = Math.min(this.end + SZ_WINDOW, this.count));
|
||
|
if(this.query_view.length >= 3) batch = batch.filter(x => x.description.startsWith(this.query_view));
|
||
|
if(batch.length > 0) {
|
||
|
Array.prototype.push.apply(this.resultset, batch);
|
||
|
this.has_more = true;
|
||
|
} else this.has_more = false;
|
||
|
} catch(ex) {
|
||
|
this.has_more = false;
|
||
|
} finally {
|
||
|
this.is_loading = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
created() {
|
||
|
this.$watch(
|
||
|
() => (this.client, this.dgnm, {}),
|
||
|
async () => {
|
||
|
if((this.client) && (this.dgnm)) {
|
||
|
var res = await this.client.ORWUL_FV4DG(this.dgnm);
|
||
|
this.IEN = res.IEN;
|
||
|
this.count = res.count;
|
||
|
} else this.IEN = this.count = null;
|
||
|
},
|
||
|
{ immediate: true }
|
||
|
);
|
||
|
this.$watch(
|
||
|
() => (this.client, this.IEN, this.query_view, {}),
|
||
|
debounce(async function() {
|
||
|
if((this.client) && (this.IEN)) {
|
||
|
this.is_loading = true;
|
||
|
this.has_more = false;
|
||
|
try {
|
||
|
var query = this.query_view;
|
||
|
if(query.length >= 3) {
|
||
|
var row = await this.client.ORWUL_FVIDX(this.IEN, query.slice(0, -1) + String.fromCharCode(query.charCodeAt(query.length - 1) - 1) + '~');
|
||
|
var pos = Math.floor((row.index - 1)/SZ_WINDOW)*SZ_WINDOW;
|
||
|
var batch = await this.client.ORWUL_FVSUB(this.IEN, pos + 1, this.end = Math.min(pos + SZ_WINDOW, this.count));
|
||
|
this.resultset = batch.filter(x => x.description.startsWith(query));
|
||
|
} else this.resultset = await this.client.ORWUL_FVSUB(this.IEN, 1, this.end = Math.min(SZ_WINDOW, this.count));
|
||
|
this.has_more = this.resultset.length > 0;
|
||
|
} catch(ex) {
|
||
|
this.resultset = [];
|
||
|
this.has_more = false;
|
||
|
} finally {
|
||
|
this.is_loading = false;
|
||
|
this.$refs.scroller.scrollTo(0, 0);
|
||
|
}
|
||
|
}
|
||
|
}, 500),
|
||
|
{ immediate: true }
|
||
|
);
|
||
|
},
|
||
|
mounted() {
|
||
|
this.observer_bottom = new IntersectionObserver(this.handle_bottom, { root: this.$refs.scroller, rootMargin: '25%' });
|
||
|
this.observer_bottom.observe(this.$refs.bottom);
|
||
|
},
|
||
|
destroyed() {
|
||
|
if(this.observer_bottom) this.observer_bottom.disconnect();
|
||
|
}
|
||
|
};
|
||
|
</script>
|