Add a viewer for scans with delete and download buttons
This commit is contained in:
parent
7959b34c44
commit
858e99a47f
2 changed files with 232 additions and 6 deletions
|
|
@ -187,6 +187,7 @@ const imjoyContent = () =>
|
||||||
// Import modal components for device initialisation
|
// Import modal components for device initialisation
|
||||||
import calibrationModal from "./modalComponents/calibrationModal.vue";
|
import calibrationModal from "./modalComponents/calibrationModal.vue";
|
||||||
import TabIcon from "./genericComponents/tabIcon.vue";
|
import TabIcon from "./genericComponents/tabIcon.vue";
|
||||||
|
import ScanListContent from "./tabContentComponents/scanListContent.vue";
|
||||||
|
|
||||||
// Export main app
|
// Export main app
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -266,22 +267,26 @@ export default {
|
||||||
icon: "visibility",
|
icon: "visibility",
|
||||||
component: viewContent
|
component: viewContent
|
||||||
},
|
},
|
||||||
/*{
|
|
||||||
id: "gallery",
|
|
||||||
icon: "photo_library",
|
|
||||||
component: galleryContent,
|
|
||||||
divide: true // Add a divider after this tab icon
|
|
||||||
},*/
|
|
||||||
{
|
{
|
||||||
id: "navigate",
|
id: "navigate",
|
||||||
icon: "gamepad",
|
icon: "gamepad",
|
||||||
component: navigateContent
|
component: navigateContent
|
||||||
},
|
},
|
||||||
|
/*{
|
||||||
|
id: "gallery",
|
||||||
|
icon: "photo_library",
|
||||||
|
component: galleryContent
|
||||||
|
},*/
|
||||||
{
|
{
|
||||||
id: "slidescan",
|
id: "slidescan",
|
||||||
icon: "settings_overscan",
|
icon: "settings_overscan",
|
||||||
component: slideScanContent
|
component: slideScanContent
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "scanlist",
|
||||||
|
icon: "photo_library",
|
||||||
|
component: ScanListContent
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "background_detect",
|
id: "background_detect",
|
||||||
icon: "background_replace",
|
icon: "background_replace",
|
||||||
|
|
|
||||||
221
webapp/src/components/tabContentComponents/scanListContent.vue
Normal file
221
webapp/src/components/tabContentComponents/scanListContent.vue
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
<template>
|
||||||
|
<div class="galleryDisplay uk-padding uk-padding-remove-top">
|
||||||
|
<!-- Gallery nav bar -->
|
||||||
|
<nav
|
||||||
|
class="gallery-navbar uk-navbar-container uk-navbar-transparent"
|
||||||
|
uk-navbar="mode: click"
|
||||||
|
>
|
||||||
|
<!-- Right side buttons -->
|
||||||
|
<div class="uk-navbar-right">
|
||||||
|
<div class="uk-grid">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="uk-button uk-button-default uk-width-1-1"
|
||||||
|
type="button"
|
||||||
|
@click="deleteAllScans()"
|
||||||
|
>
|
||||||
|
Delete All Scans
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="uk-button uk-button-default uk-width-1-1"
|
||||||
|
type="button"
|
||||||
|
@click="updateScans()"
|
||||||
|
>
|
||||||
|
Refresh Scans
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Gallery -->
|
||||||
|
<div
|
||||||
|
v-if="$store.getters.ready"
|
||||||
|
class="uk-padding-remove-top"
|
||||||
|
uk-lightbox="toggle: .lightbox-link"
|
||||||
|
>
|
||||||
|
<!-- Gallery capture cards -->
|
||||||
|
<div class="gallery-grid uk-grid-match" uk-grid>
|
||||||
|
<div v-for="item in scans" :key="item.id">
|
||||||
|
<div class="uk-card">
|
||||||
|
<div class="uk-card-body">
|
||||||
|
<h3 class="uk-card-title">{{ item.name }}</h3>
|
||||||
|
<a
|
||||||
|
:href="`${scansUri}/${item.name}/images.zip`"
|
||||||
|
:download="`${item.name}_images.zip`"
|
||||||
|
class="uk-button uk-button-default"
|
||||||
|
>
|
||||||
|
Download images
|
||||||
|
</a>
|
||||||
|
<button class="uk-button" @click="deleteScan(item.name)">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
<ul>
|
||||||
|
<li>{{ item.number_of_images }} images</li>
|
||||||
|
<li>created {{ formatDate(item.created) }}</li>
|
||||||
|
<li>modified {{ formatDate(item.modified) }}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="uk-text-meta uk-margin-remove-top uk-padding-remove uk-width-expand"
|
||||||
|
>
|
||||||
|
<time>{{ item.created }}</time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
// Export main app
|
||||||
|
export default {
|
||||||
|
name: "ScanListContent",
|
||||||
|
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
scans: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
scansUri() {
|
||||||
|
return this.$store.getters["wot/thingPropertyUrl"](
|
||||||
|
"smart_scan",
|
||||||
|
"scans",
|
||||||
|
"readproperty",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
// Update on mount (does nothing if not connected)
|
||||||
|
await this.updateScans();
|
||||||
|
// A global signal listener to perform a gallery refresh
|
||||||
|
this.$root.$on("globalUpdateScans", () => {
|
||||||
|
this.updateScans();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
created: function() {
|
||||||
|
// Watch for host 'ready', then update status
|
||||||
|
this.unwatchStoreFunction = this.$store.watch(
|
||||||
|
(state, getters) => {
|
||||||
|
return getters.ready;
|
||||||
|
},
|
||||||
|
ready => {
|
||||||
|
if (ready) {
|
||||||
|
// If the connection is now ready, update capture list
|
||||||
|
this.updateScans();
|
||||||
|
} else {
|
||||||
|
// If the connection is now disconnected, empty capture list
|
||||||
|
this.captures = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
// Remove global signal listener to perform a gallery refresh
|
||||||
|
this.$root.$off("globalUpdateScans");
|
||||||
|
// Then we call that function here to unwatch
|
||||||
|
if (this.unwatchStoreFunction) {
|
||||||
|
this.unwatchStoreFunction();
|
||||||
|
this.unwatchStoreFunction = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async updateScans() {
|
||||||
|
let scans = await this.readThingProperty("smart_scan", "scans");
|
||||||
|
scans.forEach(scan => {
|
||||||
|
scan.modified = Date.parse(scan.modified);
|
||||||
|
scan.created = Date.parse(scan.created);
|
||||||
|
});
|
||||||
|
scans.sort((a, b) => {
|
||||||
|
return b.modified - a.modified;
|
||||||
|
});
|
||||||
|
this.scans = scans;
|
||||||
|
},
|
||||||
|
formatDate(timestamp) {
|
||||||
|
let d = new Date(timestamp);
|
||||||
|
let yyyy = d.getFullYear();
|
||||||
|
let mm = d.getMonth() + 1;
|
||||||
|
let dd = d.getDate();
|
||||||
|
let HH = d
|
||||||
|
.getHours()
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0");
|
||||||
|
let MM = d
|
||||||
|
.getMinutes()
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0");
|
||||||
|
return `${HH}:${MM} ${dd}/${mm}/${yyyy}`;
|
||||||
|
},
|
||||||
|
async deleteScan(name) {
|
||||||
|
try {
|
||||||
|
await this.modalConfirm(`Are you sure you want to delete ${name}?`);
|
||||||
|
await axios.delete(`${this.scansUri}/${name}`);
|
||||||
|
await this.updateScans();
|
||||||
|
this.modalNotify(`Deleted ${name}`);
|
||||||
|
} catch (e) {
|
||||||
|
// if the confirmation was cancelled, it's rejected with null error
|
||||||
|
if (e) this.modalError(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async deleteAllScans() {
|
||||||
|
try {
|
||||||
|
await this.modalConfirm(
|
||||||
|
"Are you sure you want to delete all scans from the microscope? " +
|
||||||
|
"This is <b>irreversible</b>!"
|
||||||
|
);
|
||||||
|
await axios.delete(`${this.scansUri}`);
|
||||||
|
await this.updateScans();
|
||||||
|
this.modalNotify("Deleted all scans.");
|
||||||
|
} catch (e) {
|
||||||
|
// if the confirmation was cancelled, it's rejected with null error
|
||||||
|
if (e) this.modalError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.gallery-navbar {
|
||||||
|
border-width: 0 0 1px 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgba(180, 180, 180, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-navbar,
|
||||||
|
.gallery-folder-heading {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
.gallery-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, 320px);
|
||||||
|
justify-content: center;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-grid > div {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .capture-card {
|
||||||
|
width: 300px;
|
||||||
|
height: 100%; // Used to have all cards in a row match their heights
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}*/
|
||||||
|
</style>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue