From a9c8e2cbda12e32e4aa22a05523b8cb65ee24624 Mon Sep 17 00:00:00 2001 From: MarcZierle Date: Sat, 22 Jan 2022 14:20:45 +0100 Subject: [PATCH] add delete, edit ocr and simple cropper to manage photos --- package-lock.json | 58 +++++++++++++++ package.json | 1 + src/api/index.js | 4 + src/components/PhotoCropper.vue | 51 +++++++++++++ src/components/PhotoItem.vue | 2 +- src/store/index.js | 48 ++++++++++-- src/views/CameraCapture.vue | 2 +- src/views/ManagePhotos.vue | 127 +++++++++++++++++++++++++++++++- 8 files changed, 281 insertions(+), 12 deletions(-) create mode 100644 src/components/PhotoCropper.vue diff --git a/package-lock.json b/package-lock.json index 8710629..d99f847 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 704be44..3b80aee 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/api/index.js b/src/api/index.js index d4cefef..523120b 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -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+'/') } \ No newline at end of file diff --git a/src/components/PhotoCropper.vue b/src/components/PhotoCropper.vue new file mode 100644 index 0000000..d251701 --- /dev/null +++ b/src/components/PhotoCropper.vue @@ -0,0 +1,51 @@ + + + + + \ No newline at end of file diff --git a/src/components/PhotoItem.vue b/src/components/PhotoItem.vue index 71d4ffa..be7f64f 100644 --- a/src/components/PhotoItem.vue +++ b/src/components/PhotoItem.vue @@ -25,7 +25,7 @@
- Change Group + Move Group
diff --git a/src/store/index.js b/src/store/index.js index a97a5b5..433aa31 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -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: {}, }) diff --git a/src/views/CameraCapture.vue b/src/views/CameraCapture.vue index 0ca0462..f9941ff 100644 --- a/src/views/CameraCapture.vue +++ b/src/views/CameraCapture.vue @@ -212,7 +212,7 @@ export default { }, takePhoto() { this.isPhotoTaken = true - + setTimeout(() => { this.photo.height = document.getElementById('camera_preview').offsetHeight diff --git a/src/views/ManagePhotos.vue b/src/views/ManagePhotos.vue index 632b3e4..e1c7af2 100644 --- a/src/views/ManagePhotos.vue +++ b/src/views/ManagePhotos.vue @@ -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)" />

No Photo Groups

+ + + + + + + + + + + Cancel - Change Group + Move to Group + + + + + + + + Do you want to permanently delete the photo? + + + + + + + + + + @@ -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 + }) + } } }, }