mirror of
https://github.com/MarcZierle/photo-log-frontend.git
synced 2025-04-11 14:44:38 +00:00
add websockets and react to updates
This commit is contained in:
parent
7b2d9a7d6c
commit
3db2070ef3
43
src/App.vue
43
src/App.vue
@ -22,6 +22,8 @@ import {darkTheme} from 'naive-ui'
|
|||||||
|
|
||||||
import NavBar from '@/components/NavBar.vue'
|
import NavBar from '@/components/NavBar.vue'
|
||||||
|
|
||||||
|
import store from '@/store/index.js'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
NavBar,
|
NavBar,
|
||||||
@ -41,7 +43,46 @@ export default defineComponent({
|
|||||||
//return !(currentPage == 'Home' || currentPage == 'CameraCapture')
|
//return !(currentPage == 'Home' || currentPage == 'CameraCapture')
|
||||||
return !(currentPage == 'CameraCapture')
|
return !(currentPage == 'CameraCapture')
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
created() {
|
||||||
|
console.log("Starting connection to WebSocket Server")
|
||||||
|
this.connection = new WebSocket("wss://zierle-training-staging.riezel.com/ws/notifications/")
|
||||||
|
|
||||||
|
this.connection.onmessage = function(event) {
|
||||||
|
console.log(event);
|
||||||
|
|
||||||
|
let data = JSON.parse(event.data)
|
||||||
|
|
||||||
|
if (data.type === 'update_photolog_pdf') {
|
||||||
|
store.dispatch('setPhotoLogField',
|
||||||
|
{id: data.content.id, field: 'status', value: null}
|
||||||
|
)
|
||||||
|
store.dispatch('setPhotoLogField',
|
||||||
|
{
|
||||||
|
id: data.content.id, field: 'pdf',
|
||||||
|
value: 'https://minio.riezel.com/zierle-training/' + data.content.pdf
|
||||||
|
}
|
||||||
|
)
|
||||||
|
store.dispatch('loadPhotoLogList')
|
||||||
|
} else if (data.type === 'update_photo') {
|
||||||
|
store.dispatch('loadPhoto', data.content.id)
|
||||||
|
store.dispatch('setPhotoStatus', {
|
||||||
|
id: data.content.id,
|
||||||
|
status: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connection.onopen = function(event) {
|
||||||
|
console.log(event)
|
||||||
|
console.log("Successfully connected to the echo websocket server...")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connection.onerror = function(event) {
|
||||||
|
console.error("Error while connecting to the websocket:")
|
||||||
|
console.error(event)
|
||||||
|
}
|
||||||
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: 'https://zierle-training.riezel.com/api/v1',
|
baseURL: 'https://zierle-training-staging.riezel.com/api/v1',
|
||||||
withCredentials: false,
|
withCredentials: false,
|
||||||
timeout: 120000,
|
timeout: 10000,
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -125,6 +125,10 @@ export function addNewPhoto(original_image, cropped_image=null, group_id=null, o
|
|||||||
return apiClient.post('/addphoto/', data, config)
|
return apiClient.post('/addphoto/', data, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function loadPhoto(id) {
|
||||||
|
return apiClient.get('/photo/' + id+'/')
|
||||||
|
}
|
||||||
|
|
||||||
export function updatePhoto(photo) {
|
export function updatePhoto(photo) {
|
||||||
return apiClient.put('/updatephoto/'+photo.id+'/', photo)
|
return apiClient.put('/updatephoto/'+photo.id+'/', photo)
|
||||||
}
|
}
|
||||||
|
@ -1,64 +1,66 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<n-spin :show="loading">
|
||||||
class="card"
|
<div
|
||||||
:style="card_styles"
|
class="card"
|
||||||
@mouseover="setHoverIfLoaded"
|
:style="card_styles"
|
||||||
@mouseleave="hover=false"
|
@mouseover="setHoverIfLoaded"
|
||||||
>
|
@mouseleave="hover=false"
|
||||||
<div
|
>
|
||||||
class="cover"
|
<div
|
||||||
:style="cover_styles"
|
class="cover"
|
||||||
ref="cover"
|
:style="cover_styles"
|
||||||
@click="toggleSelection"
|
ref="cover"
|
||||||
>
|
@click="toggleSelection"
|
||||||
<n-skeleton v-if="!isImgLoaded" :height="height" :width="width" />
|
>
|
||||||
<div v-if="isImgLoaded" class="selection" :style="selection_styles">
|
<n-skeleton v-if="!isImgLoaded" :height="height" :width="width" />
|
||||||
<n-icon size="4em" color="rgba(255,255,255,0.75)"><Check /></n-icon>
|
<div v-if="isImgLoaded" class="selection" :style="selection_styles">
|
||||||
</div>
|
<n-icon size="4em" color="rgba(255,255,255,0.75)"><Check /></n-icon>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="options"
|
|
||||||
:style="options_styles"
|
|
||||||
>
|
|
||||||
<div class="content">
|
|
||||||
<div class="options-item" v-if="can_change_group">
|
|
||||||
<n-button text @click="$emit('update:group')">
|
|
||||||
<template #icon><n-icon><SortFilled /></n-icon></template>
|
|
||||||
Move Group
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="options-item" v-if="can_crop">
|
|
||||||
<n-button text @click="$emit('update:crop')">
|
|
||||||
<template #icon><n-icon><CropRotateFilled /></n-icon></template>
|
|
||||||
Crop Photo
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="options-item" v-if="can_change_ocr">
|
|
||||||
<n-button text @click="$emit('update:ocr')">
|
|
||||||
<template #icon><n-icon><TextAnnotationToggle /></n-icon></template>
|
|
||||||
Change OCR
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="options-item" v-if="can_change_tag">
|
|
||||||
<n-button text @click="$emit('update:tag')">
|
|
||||||
<template #icon><n-icon><MdPricetag /></n-icon></template>
|
|
||||||
Change Tag
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="options-item" v-if="can_delete">
|
|
||||||
<n-button text type="error" @click="$emit('update:delete')">
|
|
||||||
<template #icon><n-icon><TrashBinSharp /></n-icon></template>
|
|
||||||
Delete Photo
|
|
||||||
</n-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
<div
|
||||||
|
class="options"
|
||||||
|
:style="options_styles"
|
||||||
|
>
|
||||||
|
<div class="content">
|
||||||
|
<div class="options-item" v-if="can_change_group">
|
||||||
|
<n-button text @click="$emit('update:group')">
|
||||||
|
<template #icon><n-icon><SortFilled /></n-icon></template>
|
||||||
|
Move Group
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="options-item" v-if="can_crop">
|
||||||
|
<n-button text @click="$emit('update:crop')">
|
||||||
|
<template #icon><n-icon><CropRotateFilled /></n-icon></template>
|
||||||
|
Crop Photo
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="options-item" v-if="can_change_ocr">
|
||||||
|
<n-button text @click="$emit('update:ocr')">
|
||||||
|
<template #icon><n-icon><TextAnnotationToggle /></n-icon></template>
|
||||||
|
Change OCR
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="options-item" v-if="can_change_tag">
|
||||||
|
<n-button text @click="$emit('update:tag')">
|
||||||
|
<template #icon><n-icon><MdPricetag /></n-icon></template>
|
||||||
|
Change Tag
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="options-item" v-if="can_delete">
|
||||||
|
<n-button text type="error" @click="$emit('update:delete')">
|
||||||
|
<template #icon><n-icon><TrashBinSharp /></n-icon></template>
|
||||||
|
Delete Photo
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -125,6 +127,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
'update:select',
|
'update:select',
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
getPhotoGroups,
|
getPhotoGroups,
|
||||||
getPhotoTags,
|
getPhotoTags,
|
||||||
getPhotosByGroup,
|
getPhotosByGroup,
|
||||||
|
loadPhoto,
|
||||||
updatePhoto,
|
updatePhoto,
|
||||||
deletePhoto,
|
deletePhoto,
|
||||||
cropPhoto,
|
cropPhoto,
|
||||||
@ -24,9 +25,13 @@ export default createStore({
|
|||||||
photoLogs: [],
|
photoLogs: [],
|
||||||
photoGroups: null,
|
photoGroups: null,
|
||||||
photoTags: null,
|
photoTags: null,
|
||||||
photos: []
|
photos: [],
|
||||||
|
photosStatus: {},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
SET_PHOTO_STATUS(state, {id, status}) {
|
||||||
|
state.photosStatus[id] = status
|
||||||
|
},
|
||||||
SET_PHOTO_LOG_LIST(state, newPhotoLogList) {
|
SET_PHOTO_LOG_LIST(state, newPhotoLogList) {
|
||||||
state.photoLogList = newPhotoLogList
|
state.photoLogList = newPhotoLogList
|
||||||
},
|
},
|
||||||
@ -57,6 +62,12 @@ export default createStore({
|
|||||||
let log_index = state.photoLogTemplateList.findIndex(log => log.id === id)
|
let log_index = state.photoLogTemplateList.findIndex(log => log.id === id)
|
||||||
state.photoLogTemplateList.splice(log_index, 1)
|
state.photoLogTemplateList.splice(log_index, 1)
|
||||||
},
|
},
|
||||||
|
SET_PHOTO_LOG_FIELD(state, {id, field, value}) {
|
||||||
|
let log_index = state.photoLogList.findIndex(log => log.id === id)
|
||||||
|
if (log_index > -1) {
|
||||||
|
state.photoLogList[log_index][field] = value
|
||||||
|
}
|
||||||
|
},
|
||||||
SET_PHOTO_GROUPS(state, groups) {
|
SET_PHOTO_GROUPS(state, groups) {
|
||||||
state.photoGroups = groups
|
state.photoGroups = groups
|
||||||
},
|
},
|
||||||
@ -79,14 +90,14 @@ export default createStore({
|
|||||||
Object.keys(photo).forEach(key => {
|
Object.keys(photo).forEach(key => {
|
||||||
state_photo[key] = photo[key]
|
state_photo[key] = photo[key]
|
||||||
})
|
})
|
||||||
if (photo['group']) {
|
/*if (photo['group']) {
|
||||||
if (!state.photos[photo.group] || state.photos[photo.group].length === 0) {
|
if (!state.photos[photo.group] || state.photos[photo.group].length === 0) {
|
||||||
state.photos[photo.group] = []
|
state.photos[photo.group] = []
|
||||||
}
|
}
|
||||||
state.photos[state_photo.group].push(state_photo)
|
state.photos[state_photo.group].push(state_photo)
|
||||||
} else {
|
} else {*/
|
||||||
state.photos[state_photo.group].splice(photo_index, 0, state_photo)
|
state.photos[state_photo.group].splice(photo_index, 0, state_photo)
|
||||||
}
|
//}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,6 +113,9 @@ export default createStore({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
setPhotoStatus({commit}, {id, status}) {
|
||||||
|
commit('SET_PHOTO_STATUS', {id, status})
|
||||||
|
},
|
||||||
loadPhotoLogList({commit}) {
|
loadPhotoLogList({commit}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getPhotoLogList().then((response) => {
|
getPhotoLogList().then((response) => {
|
||||||
@ -183,6 +197,9 @@ export default createStore({
|
|||||||
return updateLogTemplate(photologtemplate)
|
return updateLogTemplate(photologtemplate)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setPhotoLogField({commit}, {id, field, value}) {
|
||||||
|
commit('SET_PHOTO_LOG_FIELD', {id, field, value})
|
||||||
|
},
|
||||||
loadPhotoGroups({commit}) {
|
loadPhotoGroups({commit}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getPhotoGroups().then((response) => {
|
getPhotoGroups().then((response) => {
|
||||||
@ -219,6 +236,13 @@ export default createStore({
|
|||||||
dispatch('loadPhotosInGroup', group_id)
|
dispatch('loadPhotosInGroup', group_id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
loadPhoto({commit}, id) {
|
||||||
|
loadPhoto(id).then((response) => {
|
||||||
|
commit('UPDATE_PHOTO', response.data)
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
|
},
|
||||||
updatePhoto({commit}, photo) {
|
updatePhoto({commit}, photo) {
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
updatePhoto(photo).then(() => {
|
updatePhoto(photo).then(() => {
|
||||||
@ -241,11 +265,11 @@ export default createStore({
|
|||||||
},
|
},
|
||||||
cropPhoto({commit}, {id, mode}) {
|
cropPhoto({commit}, {id, mode}) {
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
cropPhoto(id, mode).then((response) => {
|
cropPhoto(id, mode).then((/*response*/) => {
|
||||||
commit('UPDATE_PHOTO', {
|
/*commit('UPDATE_PHOTO', {
|
||||||
id: response.data.id,
|
id: response.data.id,
|
||||||
cropped_image: response.data.cropped_image
|
cropped_image: response.data.cropped_image
|
||||||
})
|
})*/
|
||||||
resolve()
|
resolve()
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
reject(error)
|
reject(error)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
type="primary" style="margin: 1em;"
|
type="primary" style="margin: 1em;"
|
||||||
@click="getPDF"
|
@click="getPDF"
|
||||||
:disabled="!isValidId"
|
:disabled="!isValidId"
|
||||||
:loading="isGeneratingPDF"
|
:loading="currently_saving"
|
||||||
>
|
>
|
||||||
Save & Generate
|
Save & Generate
|
||||||
</n-button>
|
</n-button>
|
||||||
@ -562,8 +562,9 @@ export default {
|
|||||||
this.saveChanges().then(() => {
|
this.saveChanges().then(() => {
|
||||||
this.isGeneratingPDF = true
|
this.isGeneratingPDF = true
|
||||||
getPhotoLogPDF(this.id).then((response) => {
|
getPhotoLogPDF(this.id).then((response) => {
|
||||||
let url = response.data.pdf
|
this.$store.dispatch('setPhotoLogField',
|
||||||
window.open(url, '_blank')
|
{id:this.id, field:'status', value:'generating'})
|
||||||
|
this.$router.push({name: 'LogsList'})
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.message.error('Cannot generate PDF file: ' + error)
|
this.message.error('Cannot generate PDF file: ' + error)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
|
@ -9,17 +9,28 @@
|
|||||||
<th>delete</th>
|
<th>delete</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="photolog in photoLogList" :key="photolog.id">
|
<tr
|
||||||
<router-link :to="{name: 'CreateLog', params: {e: photolog.id}}">
|
v-for="photolog in photoLogList"
|
||||||
<td>{{ photolog.title }}</td>
|
:key="photolog.id"
|
||||||
</router-link>
|
:class="{
|
||||||
<td>
|
'opacity-25': photolog.status === 'generating'
|
||||||
<a :href="photolog.pdf" target="_blank">PDF</a>
|
}"
|
||||||
</td>
|
>
|
||||||
<td>{{ photolog.date }}</td>
|
<router-link :to="{name: 'CreateLog', params: {e: photolog.id}}">
|
||||||
<td>
|
<td>{{ photolog.title }}</td>
|
||||||
<n-button type="error" @click="askDeleteLog(photolog.id)">Delete</n-button>
|
</router-link>
|
||||||
</td>
|
<td>
|
||||||
|
<n-spin :show="photolog.status === 'generating'">
|
||||||
|
<p
|
||||||
|
@click="openPdfModal(photolog.title, photolog.pdf)"
|
||||||
|
class="cursor-pointer"
|
||||||
|
>PDF</p>
|
||||||
|
</n-spin>
|
||||||
|
</td>
|
||||||
|
<td>{{ photolog.date }}</td>
|
||||||
|
<td>
|
||||||
|
<n-button type="error" @click="askDeleteLog(photolog.id)">Delete</n-button>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</n-table>
|
</n-table>
|
||||||
@ -35,6 +46,21 @@
|
|||||||
negative-text="Delete"
|
negative-text="Delete"
|
||||||
@negative-click="deleteLog"
|
@negative-click="deleteLog"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<n-modal
|
||||||
|
v-model:show="showPdfModal"
|
||||||
|
:mask-closable="true"
|
||||||
|
:title="pdfModalTitle"
|
||||||
|
preset="card"
|
||||||
|
style="max-width: 90vw; max-height: 90vh;"
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
:src="pdfModalLink"
|
||||||
|
width="100%"
|
||||||
|
style="height: 75vh;"
|
||||||
|
>
|
||||||
|
</iframe>
|
||||||
|
</n-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -51,10 +77,16 @@ export default {
|
|||||||
showDeleteModal: false,
|
showDeleteModal: false,
|
||||||
deleteId: null,
|
deleteId: null,
|
||||||
deleteModalContent: '',
|
deleteModalContent: '',
|
||||||
|
|
||||||
|
showPdfModal: false,
|
||||||
|
pdfModalTitle: '',
|
||||||
|
pdfModalLink: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.dispatch('loadPhotoLogList')
|
if (this.$store.state.photoLogList.length == 0) {
|
||||||
|
this.$store.dispatch('loadPhotoLogList')
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
photoLogList() {
|
photoLogList() {
|
||||||
@ -82,7 +114,12 @@ export default {
|
|||||||
this.$store.dispatch('deletePhotoLog', this.deleteId)
|
this.$store.dispatch('deletePhotoLog', this.deleteId)
|
||||||
this.deleteId = null
|
this.deleteId = null
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
openPdfModal(title, link) {
|
||||||
|
this.showPdfModal = true
|
||||||
|
this.pdfModalTitle = title
|
||||||
|
this.pdfModalLink = link
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
@ -25,6 +25,7 @@
|
|||||||
:can_change_ocr="true"
|
:can_change_ocr="true"
|
||||||
:can_change_tag="true"
|
:can_change_tag="true"
|
||||||
:can_delete="true"
|
:can_delete="true"
|
||||||
|
:loading="photosStatus[photo.id] === 'cropping'"
|
||||||
@update:group="change_group_modal(photo.id)"
|
@update:group="change_group_modal(photo.id)"
|
||||||
@update:crop="change_crop_modal(photo.id)"
|
@update:crop="change_crop_modal(photo.id)"
|
||||||
@update:ocr="change_ocr_modal(photo.id)"
|
@update:ocr="change_ocr_modal(photo.id)"
|
||||||
@ -216,6 +217,9 @@ export default {
|
|||||||
photos() {
|
photos() {
|
||||||
return this.$store.state.photos
|
return this.$store.state.photos
|
||||||
},
|
},
|
||||||
|
photosStatus() {
|
||||||
|
return this.$store.state.photosStatus
|
||||||
|
},
|
||||||
groups_select_options() {
|
groups_select_options() {
|
||||||
if (this.photoGroups.length > 0) {
|
if (this.photoGroups.length > 0) {
|
||||||
let options = []
|
let options = []
|
||||||
@ -338,6 +342,10 @@ export default {
|
|||||||
rotate: rotate
|
rotate: rotate
|
||||||
}).then(()=>{
|
}).then(()=>{
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
|
this.$store.dispatch('setPhotoStatus', {
|
||||||
|
id: this.current_photo,
|
||||||
|
status: 'cropping'
|
||||||
|
})
|
||||||
this.$store.dispatch('cropPhoto', {
|
this.$store.dispatch('cropPhoto', {
|
||||||
id: this.current_photo,
|
id: this.current_photo,
|
||||||
mode: 'bbox'
|
mode: 'bbox'
|
||||||
|
Loading…
Reference in New Issue
Block a user