You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
geodata-catalog/frontend/src/components/ItemDetails.vue

243 lines
8.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>