mirror of
https://github.com/MarcZierle/photo-log-frontend.git
synced 2025-04-07 13:04:37 +00:00
add delete, edit ocr and simple cropper to manage photos
This commit is contained in:
parent
7b6ad0c475
commit
a9c8e2cbda
58
package-lock.json
generated
58
package-lock.json
generated
@ -11,6 +11,7 @@
|
||||
"axios": "^0.24.0",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^3.0.0",
|
||||
"vue-advanced-cropper": "^2.8.0",
|
||||
"vue-meta": "^3.0.0-alpha.8",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
@ -4354,6 +4355,11 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/classnames": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
||||
},
|
||||
"node_modules/clean-css": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
|
||||
@ -5581,6 +5587,11 @@
|
||||
"date-fns": ">=2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
|
||||
@ -6171,6 +6182,11 @@
|
||||
"stream-shift": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/easy-bem": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/easy-bem/-/easy-bem-1.1.1.tgz",
|
||||
"integrity": "sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A=="
|
||||
},
|
||||
"node_modules/easy-stack": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz",
|
||||
@ -14729,6 +14745,23 @@
|
||||
"@vue/shared": "3.2.26"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-advanced-cropper": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-advanced-cropper/-/vue-advanced-cropper-2.8.0.tgz",
|
||||
"integrity": "sha512-whia8uYhAgxBnW8HtP5FVrNJqRkpzkje5OmyDKKipQRWfsHd9At0Sk6+0RBkKBI5x59Xin7b9AsuXuweCTQNXg==",
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.6",
|
||||
"debounce": "^1.2.0",
|
||||
"easy-bem": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-eslint-parser": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.0.1.tgz",
|
||||
@ -19544,6 +19577,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"classnames": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
||||
},
|
||||
"clean-css": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
|
||||
@ -20530,6 +20568,11 @@
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
|
||||
@ -20996,6 +21039,11 @@
|
||||
"stream-shift": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"easy-bem": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/easy-bem/-/easy-bem-1.1.1.tgz",
|
||||
"integrity": "sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A=="
|
||||
},
|
||||
"easy-stack": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz",
|
||||
@ -27874,6 +27922,16 @@
|
||||
"@vue/shared": "3.2.26"
|
||||
}
|
||||
},
|
||||
"vue-advanced-cropper": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-advanced-cropper/-/vue-advanced-cropper-2.8.0.tgz",
|
||||
"integrity": "sha512-whia8uYhAgxBnW8HtP5FVrNJqRkpzkje5OmyDKKipQRWfsHd9At0Sk6+0RBkKBI5x59Xin7b9AsuXuweCTQNXg==",
|
||||
"requires": {
|
||||
"classnames": "^2.2.6",
|
||||
"debounce": "^1.2.0",
|
||||
"easy-bem": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"vue-eslint-parser": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.0.1.tgz",
|
||||
|
@ -11,6 +11,7 @@
|
||||
"axios": "^0.24.0",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^3.0.0",
|
||||
"vue-advanced-cropper": "^2.8.0",
|
||||
"vue-meta": "^3.0.0-alpha.8",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
|
@ -84,4 +84,8 @@ export function addNewPhoto(original_image, cropped_image=null, group_id=null, o
|
||||
|
||||
export function updatePhoto(photo) {
|
||||
return apiClient.put('/updatephoto/'+photo.id+'/', photo)
|
||||
}
|
||||
|
||||
export function deletePhoto(id) {
|
||||
return apiClient.delete('/deletephoto/'+id+'/')
|
||||
}
|
51
src/components/PhotoCropper.vue
Normal file
51
src/components/PhotoCropper.vue
Normal file
@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div>
|
||||
<cropper
|
||||
class="cropper"
|
||||
:src="src"
|
||||
:stencil-props="{
|
||||
aspectRatio: 1/1.41421356
|
||||
}"
|
||||
@change="change"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Cropper} from 'vue-advanced-cropper'
|
||||
import 'vue-advanced-cropper/dist/style.css'
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Cropper,
|
||||
},
|
||||
props: {
|
||||
src: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.src)
|
||||
},
|
||||
methods: {
|
||||
change({ coordinates, canvas }) {
|
||||
console.log(coordinates, canvas)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cropper {
|
||||
height: 600px;
|
||||
width: 600px;
|
||||
background: #DDD;
|
||||
}
|
||||
</style>
|
@ -25,7 +25,7 @@
|
||||
<div class="options-item" v-if="can_change_group">
|
||||
<n-button text @click="$emit('update:group')">
|
||||
<template #icon><n-icon><SortFilled /></n-icon></template>
|
||||
Change Group
|
||||
Move Group
|
||||
</n-button>
|
||||
</div>
|
||||
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
getPhotoGroups,
|
||||
getPhotosByGroup,
|
||||
updatePhoto,
|
||||
deletePhoto,
|
||||
} from '@/api'
|
||||
|
||||
export default createStore({
|
||||
@ -49,13 +50,27 @@ export default createStore({
|
||||
let photo_index = group.findIndex(p => p.id == photo.id)
|
||||
if (photo_index > -1) {
|
||||
let state_photo = state.photos[idx].splice(photo_index, 1)[0]
|
||||
state_photo.group = photo.group
|
||||
state_photo.bbox_coords = photo.bbox_coords
|
||||
state_photo.ocr_text = photo.ocr_text
|
||||
if (!state.photos[photo.group] || state.photos[photo.group].length === 0) {
|
||||
state.photos[photo.group] = []
|
||||
Object.keys(photo).forEach(key => {
|
||||
state_photo[key] = photo[key]
|
||||
})
|
||||
if (photo['group']) {
|
||||
if (!state.photos[photo.group] || state.photos[photo.group].length === 0) {
|
||||
state.photos[photo.group] = []
|
||||
}
|
||||
state.photos[state_photo.group].push(state_photo)
|
||||
} else {
|
||||
state.photos[state_photo.group].splice(photo_index, 0, state_photo)
|
||||
}
|
||||
state.photos[photo.group].push(state_photo)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
DELETE_PHOTO(state, id) {
|
||||
for (const group_id in state.photos) {
|
||||
let group = state.photos[group_id]
|
||||
let photo_index = group.findIndex(p => p.id == id)
|
||||
if (photo_index > -1) {
|
||||
state.photos[group_id].splice(photo_index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,6 +157,16 @@ export default createStore({
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
deletePhoto({commit}, id) {
|
||||
new Promise((resolve, reject) => {
|
||||
deletePhoto(id).then(() => {
|
||||
commit('DELETE_PHOTO', id)
|
||||
resolve()
|
||||
}).catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
@ -164,7 +189,16 @@ export default createStore({
|
||||
},
|
||||
photos (state) {
|
||||
return state.photos
|
||||
}
|
||||
},
|
||||
photoById: (state) => (id) => {
|
||||
for (const group_idx in state.photos) {
|
||||
let found_photo = state.photos[group_idx].filter(p => p.id == id)
|
||||
if (found_photo.length > 0) {
|
||||
return found_photo[0]
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
},
|
||||
modules: {},
|
||||
})
|
||||
|
@ -212,7 +212,7 @@ export default {
|
||||
},
|
||||
takePhoto() {
|
||||
this.isPhotoTaken = true
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
this.photo.height = document.getElementById('camera_preview').offsetHeight
|
||||
|
||||
|
@ -22,18 +22,44 @@
|
||||
:can_change_ocr="true"
|
||||
:can_delete="true"
|
||||
@update:group="change_group_modal(photo.id)"
|
||||
@update:crop="change_crop_modal(photo.id)"
|
||||
@update:ocr="change_ocr_modal(photo.id)"
|
||||
@update:delete="change_delete_modal(photo.id)"
|
||||
/>
|
||||
</n-space>
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
<p v-else>No Photo Groups</p>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showCropModal"
|
||||
:mask-closable="false"
|
||||
>
|
||||
<n-card
|
||||
style="width: 95vw; height: 90vh;"
|
||||
title="Adjust Photo Cropping"
|
||||
:bordered="true"
|
||||
>
|
||||
<template #header-extra> Header </template>
|
||||
|
||||
<PhotoCropper :src="current_photo_src" />
|
||||
|
||||
<template #footer> Footer </template>
|
||||
<template #action>
|
||||
<n-space justify="end">
|
||||
<n-button @click="showCropModal = false">Cancel</n-button>
|
||||
<n-button type="primary">Save</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showChangeGroupModal"
|
||||
:mask-closable="true" >
|
||||
<n-card
|
||||
style="width: 400px;"
|
||||
title="Change Photo Group"
|
||||
title="Move to Photo Group"
|
||||
:bordered="false"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
@ -42,7 +68,57 @@
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-button @click="showChangeGroupModal=false">Cancel</n-button>
|
||||
<n-button type="success" @click="changePhotoGroup">Change Group</n-button>
|
||||
<n-button type="success" @click="changePhotoGroup">Move to Group</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showDeleteModal"
|
||||
:mask-closable="true" >
|
||||
<n-card
|
||||
style="width: 400px;"
|
||||
title="Delete Photo"
|
||||
:bordered="false"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
Do you want to permanently delete the photo?
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-button @click="showDeleteModal=false">Cancel</n-button>
|
||||
<n-button type="warning" @click="deletePhoto">Delete</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showChangeOCRModal"
|
||||
:mask-closable="true" >
|
||||
<n-card
|
||||
style="width: 400px;"
|
||||
title="Change OCR Text"
|
||||
:bordered="false"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
|
||||
<n-input
|
||||
placeholder="Enter the text on the photo"
|
||||
type="textarea"
|
||||
v-model:value="current_ocr_text"
|
||||
:autosize="{
|
||||
minRows: 3,
|
||||
maxRows: 5
|
||||
}"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-button @click="showChangeOCRModal=false">Cancel</n-button>
|
||||
<n-button type="success" @click="changeOCRText">Change Text</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-card>
|
||||
@ -55,10 +131,12 @@ import { useMeta } from 'vue-meta'
|
||||
import { useMessage } from 'naive-ui'
|
||||
|
||||
import PhotoItem from '@/components/PhotoItem'
|
||||
import PhotoCropper from '@/components/PhotoCropper'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PhotoItem,
|
||||
PhotoCropper,
|
||||
},
|
||||
setup() {
|
||||
useMeta({ title: 'Manage Photos' })
|
||||
@ -70,7 +148,13 @@ export default {
|
||||
current_photo: null,
|
||||
|
||||
showChangeGroupModal: false,
|
||||
showCropModal: false,
|
||||
showChangeOCRModal: false,
|
||||
showDeleteModal: false,
|
||||
|
||||
new_group: null,
|
||||
current_ocr_text: '',
|
||||
current_photo_src: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -127,7 +211,24 @@ export default {
|
||||
change_group_modal(photo_id) {
|
||||
this.current_photo = photo_id
|
||||
this.showChangeGroupModal = true
|
||||
this.new_group = null
|
||||
this.new_group = this.$store.getters.photoById(photo_id).group
|
||||
},
|
||||
change_crop_modal(photo_id) {
|
||||
this.current_photo = photo_id
|
||||
this.showCropModal = true
|
||||
this.current_photo_src = this.$store.getters.photoById(photo_id).original_image
|
||||
},
|
||||
change_ocr_modal(photo_id) {
|
||||
this.current_photo = photo_id
|
||||
this.showChangeOCRModal = true
|
||||
this.current_ocr_text = this.$store.getters.photoById(photo_id).ocr_text
|
||||
if (this.current_ocr_text == null) {
|
||||
this.current_ocr_text = ''
|
||||
}
|
||||
},
|
||||
change_delete_modal(photo_id) {
|
||||
this.current_photo = photo_id
|
||||
this.showDeleteModal = true
|
||||
},
|
||||
changePhotoGroup(){
|
||||
if (this.new_group && this.current_photo) {
|
||||
@ -140,6 +241,26 @@ export default {
|
||||
this.showChangeGroupModal = false
|
||||
})
|
||||
}
|
||||
},
|
||||
changeOCRText(){
|
||||
if (this.current_ocr_text.length !== null && this.current_photo) {
|
||||
this.$store.dispatch('updatePhoto', {
|
||||
id: this.current_photo,
|
||||
ocr_text: this.current_ocr_text
|
||||
}).then(()=>{
|
||||
this.current_photo = null
|
||||
this.current_ocr_text = null
|
||||
this.showChangeOCRModal = false
|
||||
})
|
||||
}
|
||||
},
|
||||
deletePhoto(){
|
||||
if (this.current_photo) {
|
||||
this.$store.dispatch('deletePhoto', this.current_photo).then(()=>{
|
||||
this.current_photo = null
|
||||
this.showDeleteModal = false
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user