mirror of
https://github.com/MarcZierle/photo-log-frontend.git
synced 2025-04-11 14:44:38 +00:00
implement photo cropping - simple bbox
This commit is contained in:
parent
a9c8e2cbda
commit
2fa9b70bc2
@ -88,4 +88,8 @@ export function updatePhoto(photo) {
|
|||||||
|
|
||||||
export function deletePhoto(id) {
|
export function deletePhoto(id) {
|
||||||
return apiClient.delete('/deletephoto/'+id+'/')
|
return apiClient.delete('/deletephoto/'+id+'/')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cropPhoto(id, mode) {
|
||||||
|
return apiClient.get('/cropphoto/'+id+'/?mode='+mode)
|
||||||
}
|
}
|
@ -1,13 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<cropper
|
<n-space justify="start">
|
||||||
class="cropper"
|
<cropper
|
||||||
:src="src"
|
class="cropper"
|
||||||
:stencil-props="{
|
:src="src"
|
||||||
aspectRatio: 1/1.41421356
|
:stencil-props="aspect_ratio"
|
||||||
}"
|
:auto-zoom="true"
|
||||||
@change="change"
|
@change="change"
|
||||||
/>
|
ref="cropper"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<n-space vertical justify="space-between">
|
||||||
|
<div>
|
||||||
|
<h2>Preview</h2>
|
||||||
|
<n-image
|
||||||
|
class="preview"
|
||||||
|
height="200"
|
||||||
|
:src="preview_data_url"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-checkbox v-model:checked="lock_aspect_ratio">lock aspect ratio to flip chart size?</n-checkbox>
|
||||||
|
</n-space>
|
||||||
|
</n-space>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -28,15 +43,48 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
preview_data_url: null,
|
||||||
|
lock_aspect_ratio: true,
|
||||||
|
bbox: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
computed: {
|
||||||
console.log(this.src)
|
aspect_ratio() {
|
||||||
|
if (this.lock_aspect_ratio) {
|
||||||
|
return {
|
||||||
|
aspectRatio: 1/1.41421356
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
coordinates_to_bbox(coordinates) {
|
||||||
|
let top_left = [
|
||||||
|
coordinates.left,
|
||||||
|
coordinates.top
|
||||||
|
]
|
||||||
|
let top_right = [
|
||||||
|
coordinates.left + coordinates.width,
|
||||||
|
coordinates.top
|
||||||
|
]
|
||||||
|
let bottom_right = [
|
||||||
|
coordinates.left + coordinates.width,
|
||||||
|
coordinates.top + coordinates.height
|
||||||
|
]
|
||||||
|
let bottom_left = [
|
||||||
|
coordinates.left,
|
||||||
|
coordinates.top + coordinates.height
|
||||||
|
]
|
||||||
|
|
||||||
|
return [top_left, top_right, bottom_right, bottom_left]
|
||||||
|
},
|
||||||
change({ coordinates, canvas }) {
|
change({ coordinates, canvas }) {
|
||||||
console.log(coordinates, canvas)
|
this.preview_data_url = canvas.toDataURL()
|
||||||
|
this.bbox = this.coordinates_to_bbox(coordinates)
|
||||||
|
},
|
||||||
|
getResult() {
|
||||||
|
return JSON.parse(JSON.stringify(this.bbox))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,4 +96,8 @@ export default {
|
|||||||
width: 600px;
|
width: 600px;
|
||||||
background: #DDD;
|
background: #DDD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -105,11 +105,16 @@ export default {
|
|||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: 150,
|
default: 150,
|
||||||
}
|
},
|
||||||
|
init_selection: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
'update:select',
|
'update:select',
|
||||||
'update:delet',
|
'update:delete',
|
||||||
'update:crop',
|
'update:crop',
|
||||||
'update:ocr',
|
'update:ocr',
|
||||||
'update:group',
|
'update:group',
|
||||||
@ -126,6 +131,9 @@ export default {
|
|||||||
beforeMount() {
|
beforeMount() {
|
||||||
this.imgSrcObj = new Image()
|
this.imgSrcObj = new Image()
|
||||||
this.imgSrcObj.src = this.src
|
this.imgSrcObj.src = this.src
|
||||||
|
if (this.can_select && this.init_selection) {
|
||||||
|
this.selected = true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
imgSrcObj: {
|
imgSrcObj: {
|
||||||
@ -202,6 +210,7 @@ export default {
|
|||||||
toggleSelection() {
|
toggleSelection() {
|
||||||
if (this.can_select) {
|
if (this.can_select) {
|
||||||
this.selected = !this.selected
|
this.selected = !this.selected
|
||||||
|
this.$emit('update:select', this.selected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -27,18 +27,18 @@
|
|||||||
:title="group.name"
|
:title="group.name"
|
||||||
:name="group.id">
|
:name="group.id">
|
||||||
|
|
||||||
<n-image-group>
|
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-image
|
<PhotoItem
|
||||||
v-for="photo in photos[group.id]"
|
v-for="photo in photos[group.id]"
|
||||||
:key="photo.id"
|
:key="photo.id"
|
||||||
width="100"
|
width="100"
|
||||||
:src="photo.cropped_image !== null ? photo.cropped_image : photo.original_image"
|
:src="photo.cropped_image !== null ? photo.cropped_image : photo.original_image"
|
||||||
@click="toggle_select_photo(photo.id)"
|
:can_select="true"
|
||||||
:class="{selected: is_photo_selected(photo.id)}"
|
:init_selection="is_photo_selected(photo.id)"
|
||||||
preview-disabled />
|
@update:select="toggle_select_photo(photo.id)"
|
||||||
|
:ref="'photoitem-'+photo.id"
|
||||||
|
/>
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-image-group>
|
|
||||||
|
|
||||||
<template #header-extra>{{group.date}}</template>
|
<template #header-extra>{{group.date}}</template>
|
||||||
</n-collapse-item>
|
</n-collapse-item>
|
||||||
@ -57,13 +57,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui'
|
||||||
|
|
||||||
//import PhotoItem from '@/components/PhotoItem'
|
import PhotoItem from '@/components/PhotoItem'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PhotoSelectModal',
|
name: 'PhotoSelectModal',
|
||||||
emits: [ 'closed', 'selected' ],
|
emits: [ 'closed', 'selected' ],
|
||||||
components: {
|
components: {
|
||||||
//PhotoItem
|
PhotoItem
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
showSelection: {
|
showSelection: {
|
||||||
@ -140,6 +140,7 @@ export default {
|
|||||||
if (this.selecions_left > 0) {
|
if (this.selecions_left > 0) {
|
||||||
this.selection.push(photo_id)
|
this.selection.push(photo_id)
|
||||||
} else {
|
} else {
|
||||||
|
this.$refs['photoitem-'+photo_id][0].selected = false
|
||||||
this.message.error('You can only select ' + this.max_select + ' photo(s)')
|
this.message.error('You can only select ' + this.max_select + ' photo(s)')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
getPhotosByGroup,
|
getPhotosByGroup,
|
||||||
updatePhoto,
|
updatePhoto,
|
||||||
deletePhoto,
|
deletePhoto,
|
||||||
|
cropPhoto,
|
||||||
} from '@/api'
|
} from '@/api'
|
||||||
|
|
||||||
export default createStore({
|
export default createStore({
|
||||||
@ -167,7 +168,20 @@ export default createStore({
|
|||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
|
cropPhoto({commit}, {id, mode}) {
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
cropPhoto(id, mode).then((response) => {
|
||||||
|
commit('UPDATE_PHOTO', {
|
||||||
|
id: response.data.id,
|
||||||
|
cropped_image: response.data.cropped_image
|
||||||
|
})
|
||||||
|
resolve()
|
||||||
|
}).catch((error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
photoLogById: (state) => (id) => {
|
photoLogById: (state) => (id) => {
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
<n-space justify="space-around" >
|
<n-space justify="space-around" >
|
||||||
<n-space vertical>
|
<n-space vertical>
|
||||||
<h2>Documents</h2>
|
<h2>Documents</h2>
|
||||||
<n-button type="primary">All Documents</n-button>
|
<a target="_blank" href='https://dev.marczierle.com/zierle-training/generate_document/modify_document.php?selection=0'>
|
||||||
|
<n-button type="primary">All Documents</n-button>
|
||||||
|
</a>
|
||||||
</n-space>
|
</n-space>
|
||||||
<n-space vertical>
|
<n-space vertical>
|
||||||
<h2>Photo Logs</h2>
|
<h2>Photo Logs</h2>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<n-table :bordered="false" :single-line="true">
|
<n-table :bordered="false" :single-line="true">
|
||||||
<thead>
|
<thead>
|
||||||
<th>Photo Log Title</th>
|
<th>Photo Log Title</th>
|
||||||
|
<th>PDF</th>
|
||||||
<th>Date created</th>
|
<th>Date created</th>
|
||||||
<th>delete</th>
|
<th>delete</th>
|
||||||
</thead>
|
</thead>
|
||||||
@ -12,6 +13,9 @@
|
|||||||
<router-link :to="{name: 'CreateLog', params: {e: photolog.id}}">
|
<router-link :to="{name: 'CreateLog', params: {e: photolog.id}}">
|
||||||
<td>{{ photolog.title }}</td>
|
<td>{{ photolog.title }}</td>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<td>
|
||||||
|
<a :href="photolog.pdf" target="_blank">PDF</a>
|
||||||
|
</td>
|
||||||
<td>{{ photolog.date }}</td>
|
<td>{{ photolog.date }}</td>
|
||||||
<td>
|
<td>
|
||||||
<n-button type="error" @click="askDeleteLog(photolog.id)">Delete</n-button>
|
<n-button type="error" @click="askDeleteLog(photolog.id)">Delete</n-button>
|
||||||
|
@ -40,15 +40,17 @@
|
|||||||
title="Adjust Photo Cropping"
|
title="Adjust Photo Cropping"
|
||||||
:bordered="true"
|
:bordered="true"
|
||||||
>
|
>
|
||||||
<template #header-extra> Header </template>
|
|
||||||
|
|
||||||
<PhotoCropper :src="current_photo_src" />
|
<PhotoCropper
|
||||||
|
style="max-width:900px;margin:auto;"
|
||||||
<template #footer> Footer </template>
|
:src="current_photo_src"
|
||||||
|
ref="cropper"
|
||||||
|
/>
|
||||||
|
|
||||||
<template #action>
|
<template #action>
|
||||||
<n-space justify="end">
|
<n-space justify="end">
|
||||||
<n-button @click="showCropModal = false">Cancel</n-button>
|
<n-button @click="showCropModal = false">Cancel</n-button>
|
||||||
<n-button type="primary">Save</n-button>
|
<n-button type="primary" @click="changeCrop">Save</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
</n-card>
|
</n-card>
|
||||||
@ -242,6 +244,25 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
changeCrop() {
|
||||||
|
let bbox = this.$refs.cropper.getResult()
|
||||||
|
if (bbox && this.current_photo) {
|
||||||
|
this.$store.dispatch('updatePhoto', {
|
||||||
|
id: this.current_photo,
|
||||||
|
bbox_coords: bbox
|
||||||
|
}).then(()=>{
|
||||||
|
setTimeout(()=>{
|
||||||
|
this.$store.dispatch('cropPhoto', {
|
||||||
|
id: this.current_photo,
|
||||||
|
mode: 'bbox'
|
||||||
|
}).then(()=>{
|
||||||
|
this.current_photo = null
|
||||||
|
this.showCropModal = false
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
changeOCRText(){
|
changeOCRText(){
|
||||||
if (this.current_ocr_text.length !== null && this.current_photo) {
|
if (this.current_ocr_text.length !== null && this.current_photo) {
|
||||||
this.$store.dispatch('updatePhoto', {
|
this.$store.dispatch('updatePhoto', {
|
||||||
|
Loading…
Reference in New Issue
Block a user