save images
This commit is contained in:
parent
c66914239d
commit
4a6ad11d16
7 changed files with 182 additions and 8 deletions
7
src-tauri/Cargo.lock
generated
7
src-tauri/Cargo.lock
generated
|
@ -1512,6 +1512,12 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.0"
|
||||
|
@ -3770,6 +3776,7 @@ dependencies = [
|
|||
"gtk",
|
||||
"heck 0.5.0",
|
||||
"http",
|
||||
"http-range",
|
||||
"jni",
|
||||
"libc",
|
||||
"log",
|
||||
|
|
|
@ -18,7 +18,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
|||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri = { version = "2", features = ["protocol-asset"] }
|
||||
tauri-plugin-opener = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"core:image:allow-from-path",
|
||||
"opener:default",
|
||||
"dialog:default",
|
||||
"fs:allow-write-text-file",
|
||||
|
|
|
@ -18,7 +18,11 @@
|
|||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
"assetProtocol": {
|
||||
"enable": true,
|
||||
"scope": ["*/**"]
|
||||
},
|
||||
"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost"
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
|
|
|
@ -8,6 +8,11 @@ export const FieldTypes = [
|
|||
|
||||
export type FieldType = typeof FieldTypes[number]
|
||||
|
||||
export interface Image {
|
||||
src: string
|
||||
alt?: string
|
||||
}
|
||||
|
||||
export interface Column {
|
||||
name: string
|
||||
type: FieldType
|
||||
|
|
|
@ -11,6 +11,13 @@ import {
|
|||
type Row as DataRow,
|
||||
type Column as DataColumn,
|
||||
} from 'src/types/data'
|
||||
import ImageEditor from './ImageEditor.vue'
|
||||
|
||||
interface EditingRow {
|
||||
data: DataRow
|
||||
index: number
|
||||
field: string
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
columns: DataColumn[],
|
||||
|
@ -20,6 +27,8 @@ const emits = defineEmits<{
|
|||
}>()
|
||||
const model = defineModel<DataRow[]>({ required: true })
|
||||
const editingRows = ref([])
|
||||
const editingImage = ref(false)
|
||||
const editingImageRow = ref(null as EditingRow | null)
|
||||
|
||||
const onRowEditSave = (event: { newData: any, index: number }) => {
|
||||
let { newData, index } = event
|
||||
|
@ -45,9 +54,28 @@ const onDeleteRow = (event: Event, slotProps: { index: number }) => {
|
|||
model.value.splice(slotProps.index, 1)
|
||||
emits('dirty')
|
||||
}
|
||||
|
||||
const onEditImage = (event: Event, slotProps: { data: DataRow, index: number, field: string }) => {
|
||||
event.preventDefault()
|
||||
editingImage.value = true
|
||||
editingImageRow.value = slotProps
|
||||
}
|
||||
|
||||
const onSaveImage = (image: string) => {
|
||||
const row = { ...model.value[editingImageRow.value!.index] } as any
|
||||
row[editingImageRow.value!.field] = {
|
||||
src: image,
|
||||
}
|
||||
model.value[editingImageRow.value!.index] = row
|
||||
}
|
||||
</script>
|
||||
|
||||
<template lang="pug">
|
||||
ImageEditor(
|
||||
v-model:visible='editingImage'
|
||||
v-model:row='editingImageRow'
|
||||
@saved='onSaveImage'
|
||||
)
|
||||
DataTable.data-editor(
|
||||
@row-edit-save='onRowEditSave'
|
||||
v-model:editingRows='editingRows'
|
||||
|
@ -70,9 +98,8 @@ DataTable.data-editor(
|
|||
v-if='col.type === "image"'
|
||||
)
|
||||
Image(
|
||||
v-if='slotProps.data.image'
|
||||
:src='slotProps.data.image.src'
|
||||
:alt='slotProps.data.image.alt'
|
||||
v-if='slotProps.data[col.name]'
|
||||
:src='slotProps.data[col.name].src'
|
||||
preview
|
||||
)
|
||||
span(
|
||||
|
@ -83,17 +110,20 @@ DataTable.data-editor(
|
|||
)
|
||||
span {{ slotProps.data[col.name] }}
|
||||
template(
|
||||
#editor='{ data, field }'
|
||||
#editor='slotProps'
|
||||
)
|
||||
div(
|
||||
v-if='col.type === "image"'
|
||||
)
|
||||
span Not Implemented
|
||||
Button(
|
||||
label='Edit Image'
|
||||
:onClick='(e) => onEditImage(e, slotProps)'
|
||||
)
|
||||
div(
|
||||
v-else
|
||||
)
|
||||
InputText(
|
||||
v-model='data[field]'
|
||||
v-model='slotProps.data[slotProps.field]'
|
||||
fluid
|
||||
)
|
||||
Column(
|
||||
|
|
127
src/views/Editor/ImageEditor.vue
Normal file
127
src/views/Editor/ImageEditor.vue
Normal file
|
@ -0,0 +1,127 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
computed,
|
||||
ref,
|
||||
} from 'vue'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import Button from 'primevue/button'
|
||||
import Dialog from 'primevue/dialog'
|
||||
import ImageComponent from 'primevue/image'
|
||||
import InputGroup from 'primevue/inputgroup'
|
||||
import InputGroupAddon from 'primevue/inputgroupaddon'
|
||||
import InputText from 'primevue/inputtext'
|
||||
|
||||
import {
|
||||
type Row as DataRow,
|
||||
} from 'src/types/data'
|
||||
import { useAppStore } from 'src/store'
|
||||
|
||||
const store = useAppStore()
|
||||
|
||||
const visible = defineModel<boolean>('visible', { default: false })
|
||||
const emit = defineEmits<{
|
||||
(e: 'saved', val: string): void
|
||||
}>()
|
||||
const row = defineModel<DataRow>('row')
|
||||
const filePath = ref(store.currentInventory.filePath.replace('.json', '/'))
|
||||
const fileName = ref('')
|
||||
const currentSrc = ref('')
|
||||
const currentBlob = ref<Blob>()
|
||||
|
||||
const onBrowseForImage = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
|
||||
const val = await open({
|
||||
multiple: false,
|
||||
directory: false,
|
||||
filters: [{ name: 'Image (svg, png, jpg, webp)', extensions: ['svg', 'png', 'jpg', 'jpeg', 'webp'] }],
|
||||
})
|
||||
|
||||
if (!!val && !Array.isArray(val)) {
|
||||
let res = await fetch(convertFileSrc(val), {
|
||||
method: 'GET',
|
||||
})
|
||||
let blob = await res.blob()
|
||||
if (blob.type.includes('image')){
|
||||
currentSrc.value = URL.createObjectURL(blob)
|
||||
currentBlob.value = blob
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onCancel = (e: Event) => {
|
||||
e.preventDefault()
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
function convertToDataUrlViaCanvas(url: string, callback: (val: string) => void, outputFormat: string) {
|
||||
const image = new Image()
|
||||
image.onload = function() {
|
||||
let canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
let dataUrl
|
||||
canvas.height = (this as any).height
|
||||
canvas.width = (this as any).width
|
||||
ctx!.drawImage(this as any, 0, 0)
|
||||
dataUrl = canvas.toDataURL(outputFormat)
|
||||
callback(dataUrl)
|
||||
canvas = null as any
|
||||
}
|
||||
image.src = url
|
||||
}
|
||||
|
||||
const onSave = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
convertToDataUrlViaCanvas(currentSrc.value, (val) => {
|
||||
emit('saved', val)
|
||||
visible.value = false
|
||||
}, currentBlob.value!.type)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template lang="pug">
|
||||
Dialog(
|
||||
v-model:visible='visible'
|
||||
modal
|
||||
header='Edit Image'
|
||||
)
|
||||
.content(
|
||||
v-if='!currentSrc'
|
||||
)
|
||||
Button(
|
||||
label='Browse For Image'
|
||||
@click='onBrowseForImage'
|
||||
)
|
||||
.content(
|
||||
v-else
|
||||
)
|
||||
InputGroup.image-group
|
||||
ImageComponent(
|
||||
:src='currentSrc'
|
||||
)
|
||||
template(#footer)
|
||||
Button(
|
||||
label='Save'
|
||||
@click='onSave'
|
||||
:disabled='!currentSrc'
|
||||
)
|
||||
Button(
|
||||
label='Replace'
|
||||
@click='onBrowseForImage'
|
||||
:disabled='!currentSrc'
|
||||
)
|
||||
Button(
|
||||
label='Cancel'
|
||||
@click='onCancel'
|
||||
)
|
||||
</template>
|
||||
|
||||
<style scoped lang="sass">
|
||||
.image-group
|
||||
display: flex
|
||||
justify-content: center
|
||||
:deep(.p-image img)
|
||||
max-width: 33vw
|
||||
max-height: 33vh
|
||||
</style>
|
Loading…
Add table
Reference in a new issue