Initial commit
This commit is contained in:
200
app/components/viewer/Search.vue
Normal file
200
app/components/viewer/Search.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<script setup>
|
||||
import SvgIcon from '@/components/SvgIcon.vue';
|
||||
import { findLandByTradeCode, findTemInside, objIsTem } from '@/components/viewer/utils/aps-viewer';
|
||||
import { getBounds } from '@/components/viewer/utils/geometry';
|
||||
|
||||
const props = defineProps({
|
||||
products: Array,
|
||||
})
|
||||
|
||||
const productInput = ref('');
|
||||
const isSubmitting = ref(false);
|
||||
|
||||
const productMatches = computed(() => {
|
||||
const val = productInput.value.trim().toUpperCase();
|
||||
if (!val) return;
|
||||
return props.products.filter(p => p.trade_code.includes(val));
|
||||
});
|
||||
|
||||
function panToLand(trade_code) {
|
||||
const { viewer } = window;
|
||||
|
||||
viewer.getObjectTree(async (instanceTree) => {
|
||||
const dbIdMap = instanceTree.nodeAccess.dbIdToIndex;
|
||||
const allDbIds = Object.keys(dbIdMap).map((id) => parseInt(id));
|
||||
|
||||
viewer.model.getBulkProperties(allDbIds, {}, (results) => {
|
||||
const frags = viewer.model.getFragmentList();
|
||||
const tems = results.filter(objIsTem);
|
||||
const lands = results.filter(land => {
|
||||
const layerProp = land.properties.find(prop => prop.displayName === 'Layer');
|
||||
if (!layerProp) return false;
|
||||
const globalWidthProp = land.properties.find(prop => prop.displayName === 'Global width');
|
||||
return layerProp.displayValue === '1-bodim' && globalWidthProp.displayValue === 0;
|
||||
});
|
||||
|
||||
const temsWithBounds = tems.map(tem => ({ ...tem, bounds: getBounds(tem.dbId, frags) }));
|
||||
const landsWithBounds = lands.map(land => ({ ...land, bounds: getBounds(land.dbId, frags) }));
|
||||
const temsInside = landsWithBounds.map(landWithBounds => findTemInside(landWithBounds, temsWithBounds));
|
||||
|
||||
const [foundLand] = findLandByTradeCode(trade_code, landsWithBounds, temsInside);
|
||||
if (!foundLand) return;
|
||||
|
||||
viewer.fitToView([foundLand.dbId]);
|
||||
isSubmitting.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function clickMatchBtn(product) {
|
||||
isSubmitting.value = true;
|
||||
panToLand(product.trade_code);
|
||||
}
|
||||
|
||||
function submit() {
|
||||
isSubmitting.value = true;
|
||||
const trade_code = productInput.value.toUpperCase();
|
||||
|
||||
const foundProduct = props.products.find(p => {
|
||||
return p.trade_code === trade_code;
|
||||
});
|
||||
|
||||
if (!foundProduct) {
|
||||
isSubmitting.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
panToLand(trade_code);
|
||||
}
|
||||
|
||||
const showMatches = ref(true);
|
||||
const input = useTemplateRef('input');
|
||||
const matches = useTemplateRef('matches');
|
||||
|
||||
function clickAwayListener(e) {
|
||||
if (input.value?.contains(e.target)
|
||||
|| matches.value?.contains(e.target)
|
||||
) {
|
||||
showMatches.value = true;
|
||||
} else {
|
||||
showMatches.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', clickAwayListener)
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', clickAwayListener)
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="has-background-white" style="position: sticky; top: 0">
|
||||
<form class="is-flex is-gap-1">
|
||||
<div class="field has-addons is-flex-grow-1">
|
||||
<p ref="input" class="control mb-0 is-flex-grow-1">
|
||||
<input
|
||||
v-model="productInput"
|
||||
class="input is-radiusless has-icons-right fs-13"
|
||||
type="text"
|
||||
placeholder="Tìm sản phẩm"
|
||||
style="
|
||||
box-sizing: border-box;
|
||||
border-inline-width: 0;
|
||||
box-shadow: none;
|
||||
"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="clearInputBtn"
|
||||
v-if="productInput"
|
||||
@click="productInput = ''"
|
||||
tabindex="-1"
|
||||
>
|
||||
<SvgIcon v-bind="{ name: 'close.svg', type: 'primary', size: 14 }" />
|
||||
</button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button
|
||||
:class="[
|
||||
'button is-small is-primary is-radiusless',
|
||||
isSubmitting && 'is-loading'
|
||||
]"
|
||||
:disabled="!productInput"
|
||||
tabindex="-1"
|
||||
style="box-sizing: border-box; width: 38px; height: 100%"
|
||||
@click.prevent="submit"
|
||||
type="submit"
|
||||
>
|
||||
<span v-if="!isSubmitting" class="icon">
|
||||
<SvgIcon v-bind="{ name: 'search.svg', type: 'white', size: 18 }" />
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
v-if="showMatches"
|
||||
ref="matches"
|
||||
tabindex="-1"
|
||||
style="
|
||||
max-height: 100px;
|
||||
overflow-y: scroll;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
box-shadow: 0 4px 4px 0 hsla(0, 0%, 0%, 0.1);
|
||||
"
|
||||
>
|
||||
<button
|
||||
v-for="product in productMatches"
|
||||
@click="clickMatchBtn(product)"
|
||||
class="button is-small is-fullwidth is-radiusless is-justify-content-start"
|
||||
style="
|
||||
box-sizing: border-box;
|
||||
border-inline-width: 0;
|
||||
border-bottom-width: 0;
|
||||
"
|
||||
>
|
||||
<p>{{ product.trade_code }}</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.control.is-loading::after, .select.is-loading::after, .button.is-loading::after {
|
||||
/* overrides spinner color */
|
||||
border: 3px solid white;
|
||||
border-right-color: transparent;
|
||||
border-top-color: transparent;
|
||||
}
|
||||
|
||||
.button:focus-visible, .button.is-focused {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
/* copy styles from is-hovered */
|
||||
--bulma-button-background-l-delta: var(--bulma-button-hover-background-l-delta);
|
||||
--bulma-button-border-l-delta: var(--bulma-button-hover-border-l-delta);
|
||||
}
|
||||
|
||||
.clearInputBtn {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 5;
|
||||
border-radius: 9999px;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
background-color: hsla(0, 0%, 0%, 0.05);
|
||||
|
||||
&:hover {
|
||||
background-color: hsla(0, 0%, 0%, 0.1)
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user