|
|
<template>
|
|
|
<div class="flex details-deck">
|
|
|
<va-card v-show="hasImage" id="preview" class="content-container">
|
|
|
<va-image contain :src="slotPreviewPath" :alt="`${this.itemDetails.description}`">
|
|
|
<template #loader>
|
|
|
<va-progress-circle indeterminate />
|
|
|
</template>
|
|
|
<template #error>
|
|
|
Изображение не загружено
|
|
|
</template>
|
|
|
</va-image>
|
|
|
</va-card>
|
|
|
<va-card id="description" class="content-container">
|
|
|
<va-card-title>Общее описание</va-card-title>
|
|
|
<va-card-content>
|
|
|
<p class="item-description">{{ itemDetails.description }}</p>
|
|
|
<p class="item-description">{{ itemDetails.additional_info }}</p>
|
|
|
</va-card-content>
|
|
|
</va-card>
|
|
|
<div class="flex md6 lg4">
|
|
|
<va-card id="details" class="content-container">
|
|
|
<va-card-title>Детальное описание</va-card-title>
|
|
|
|
|
|
<va-card-content>
|
|
|
<div class="va-table-responsive">
|
|
|
<table class="va-table va-table--striped detail-table">
|
|
|
<tbody>
|
|
|
<tr v-for="[key, value] of formattedDetails">
|
|
|
<td>{{ key }}</td>
|
|
|
<td>{{ value }}</td>
|
|
|
</tr>
|
|
|
</tbody>
|
|
|
</table>
|
|
|
</div>
|
|
|
</va-card-content>
|
|
|
</va-card>
|
|
|
</div>
|
|
|
|
|
|
<va-card id="map" class="content-container map-container">
|
|
|
<item-map-component v-if="detailsLoaded" :internal_id="this.itemDetails.internal_id"
|
|
|
:x_coord="this.xComputed" :y_coord="this.yComputed" />
|
|
|
<h3 v-else>Карта загружается</h3>
|
|
|
</va-card>
|
|
|
<va-card id="similar" class="content-container" v-if="detailsLoaded">
|
|
|
<va-scroll-container vertical>
|
|
|
<va-card-title>Похожие образцы</va-card-title>
|
|
|
<va-data-table :items="filteredSimilarItems" :columns="filteredColumns" :hoverable="true"
|
|
|
:clickable="true" select-mode="single" @row:click="(e) => handleClick(e)"
|
|
|
class="similar-items-table" />
|
|
|
</va-scroll-container>
|
|
|
</va-card>
|
|
|
<va-card id="contact" class="content-container">
|
|
|
<va-card-title>Связь с представителем</va-card-title>
|
|
|
</va-card>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
|
|
|
<script>
|
|
|
import ItemMapComponent from "@/components/ItemMapComponent.vue";
|
|
|
export default {
|
|
|
name: "item-details",
|
|
|
data() {
|
|
|
return {
|
|
|
itemDetails: {},
|
|
|
shownDetails: [
|
|
|
"internal_id",
|
|
|
"category",
|
|
|
"basin",
|
|
|
"stratum",
|
|
|
"form_dimentions",
|
|
|
"resolution",
|
|
|
"scanner",
|
|
|
"datalist",
|
|
|
"comment"
|
|
|
],
|
|
|
imageExtension: "bmp",
|
|
|
hasImage: false,
|
|
|
detailsLoaded: false,
|
|
|
columnHeaders: [],
|
|
|
showColumns: [
|
|
|
"description",
|
|
|
"deposit",
|
|
|
"category",
|
|
|
],
|
|
|
similarItems: [],
|
|
|
};
|
|
|
},
|
|
|
methods: {
|
|
|
async fetchItemDetails() {
|
|
|
fetch(`/api/v1/item/${this.$route.params.id}`)
|
|
|
.then(res => res.json())
|
|
|
.then(data => this.itemDetails = data)
|
|
|
.catch((e) => console.log(e));
|
|
|
},
|
|
|
async fetchColumnHeaders() {
|
|
|
fetch(`/api/v1/headers/`)
|
|
|
.then(res => res.json())
|
|
|
.then(data => this.columnHeaders = data)
|
|
|
.catch((e) => console.log(e));
|
|
|
},
|
|
|
getPreviewPath() {
|
|
|
for (let extension of ["jpg", "png", "bmp"]) {
|
|
|
const path = `/static/previews/${this.itemDetails.fadr}+/${this.itemDetails.internal_id}.${extension}`
|
|
|
fetch(path, { method: 'HEAD' })
|
|
|
.then(resp => {
|
|
|
if (resp.status === 200) {
|
|
|
this.hasImage = true;
|
|
|
this.imageExtension = extension;
|
|
|
}
|
|
|
})
|
|
|
.catch(e => {
|
|
|
// 404 are expected here, so ignore it
|
|
|
if (e.status !== 404) {
|
|
|
console.log(e)
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
|
|
|
return `/static/previews/${this.itemDetails.fadr}+/${this.itemDetails.internal_id}.${this.imageExtension}`
|
|
|
},
|
|
|
async fetchSimilarItems() {
|
|
|
fetch(`/api/v1/detailed_search/`,
|
|
|
{ method: 'POST', body: JSON.stringify({ fadr: this.itemDetails.fadr }), headers: { 'Content-Type': 'application/json' } })
|
|
|
.then(res => res.json())
|
|
|
.then(data => this.similarItems = data)
|
|
|
.then(() => this.detailsLoaded = true)
|
|
|
|
|
|
.catch((e) => console.log(e));
|
|
|
},
|
|
|
handleClick(e) {
|
|
|
if (e.event.ctrlKey) {
|
|
|
let routeData = this.$router.resolve({ name: 'item-details' });
|
|
|
window.open(routeData.href, '_blank');
|
|
|
} else {
|
|
|
this.detailsLoaded = false;
|
|
|
this.$router.replace(`/items/${e.item.id}`).then(a => {
|
|
|
this.fetchItemDetails();
|
|
|
});
|
|
|
}
|
|
|
},
|
|
|
},
|
|
|
mounted() {
|
|
|
this.fetchColumnHeaders();
|
|
|
this.fetchItemDetails().then(
|
|
|
this.fetchSimilarItems()
|
|
|
);
|
|
|
|
|
|
},
|
|
|
watch: {
|
|
|
'itemDetails': function (_oldval, _newval) {
|
|
|
this.fetchSimilarItems()
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
slotPreviewPath() {
|
|
|
return this.getPreviewPath()
|
|
|
},
|
|
|
internalID() {
|
|
|
return
|
|
|
},
|
|
|
dictHeaders() {
|
|
|
const dictheaders = {};
|
|
|
this.columnHeaders.forEach(header => dictheaders[header.database] = header.spreadsheet);
|
|
|
return dictheaders;
|
|
|
},
|
|
|
formattedDetails() {
|
|
|
return Object.entries(this.itemDetails)
|
|
|
.filter(([k, _v]) => (this.shownDetails.includes(k)))
|
|
|
.map(([k, v]) => [this.dictHeaders[k], v])
|
|
|
},
|
|
|
filteredColumns() {
|
|
|
return this.columnHeaders
|
|
|
.filter(header => this.showColumns.includes(header.database))
|
|
|
.sort((header1, header2) => {
|
|
|
return this.showColumns.indexOf(header1.database) - this.showColumns.indexOf(header2.database)
|
|
|
})
|
|
|
.map(header => {
|
|
|
return {
|
|
|
key: header.database,
|
|
|
label: header.spreadsheet,
|
|
|
sortable: true,
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
forceSimilarItemsReload() {
|
|
|
// https://stackoverflow.com/questions/48641295/async-computed-in-components-vuejs
|
|
|
this.fetchSimilarItems();
|
|
|
for (let _i in this.itemDetails) {
|
|
|
return this.itemDetails.fadr;
|
|
|
}
|
|
|
return false;
|
|
|
},
|
|
|
filteredSimilarItems() {
|
|
|
return [...this.similarItems].filter(item => item.internal_id !== this.itemDetails.internal_id)
|
|
|
},
|
|
|
xComputed() {
|
|
|
return this.itemDetails.x_coord ? this.itemDetails.x_coord : 55.75
|
|
|
},
|
|
|
yComputed() {
|
|
|
return this.itemDetails.y_coord ? this.itemDetails.y_coord : 37.62
|
|
|
},
|
|
|
},
|
|
|
components: { ItemMapComponent }
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
|
|
|
<style lang="css" scoped>
|
|
|
.details-deck {
|
|
|
flex-wrap: wrap;
|
|
|
margin: auto;
|
|
|
}
|
|
|
|
|
|
.content-container {
|
|
|
width: 45rem;
|
|
|
flex: 45rem;
|
|
|
margin: 1rem auto;
|
|
|
}
|
|
|
|
|
|
.similar-items-table {
|
|
|
width: 45rem;
|
|
|
overflow-x: hidden;
|
|
|
}
|
|
|
|
|
|
.item-description {
|
|
|
margin-top: 2rem;
|
|
|
margin-bottom: 2rem;
|
|
|
}
|
|
|
|
|
|
.map-container {
|
|
|
height: 28rem;
|
|
|
}
|
|
|
|
|
|
.va-table-responsive {
|
|
|
overflow: auto;
|
|
|
--va-card-display: flex;
|
|
|
}
|
|
|
|
|
|
.detail-table {
|
|
|
width: 100%;
|
|
|
}
|
|
|
</style> |