mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-04 21:58:54 -05:00
file upload in UserFileEditor and RecipeEditor
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<model-editor-base
|
||||
:loading="loading"
|
||||
:loading="loading || fileApiLoading"
|
||||
:dialog="dialog"
|
||||
@save="saveObject"
|
||||
@save="saveRecipe"
|
||||
@delete="deleteObject"
|
||||
@close="emit('close')"
|
||||
:is-update="isUpdate()"
|
||||
:model-class="modelClass"
|
||||
:object-name="editingObjName()">
|
||||
|
||||
<v-tabs v-model="tab" :disabled="loading" grow>
|
||||
<v-tabs v-model="tab" :disabled="loading || fileApiLoading" grow>
|
||||
<v-tab value="recipe">{{ $t('Recipe') }}</v-tab>
|
||||
<v-tab value="steps">{{ $t('Steps') }}</v-tab>
|
||||
<v-tab value="properties">{{ $t('Properties') }}</v-tab>
|
||||
@@ -20,18 +20,23 @@
|
||||
<v-tabs-window v-model="tab">
|
||||
<v-tabs-window-item value="recipe">
|
||||
|
||||
<v-form :disabled="loading">
|
||||
<v-form :disabled="loading || fileApiLoading">
|
||||
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
|
||||
<v-textarea :label="$t('Description')" v-model="editingObj.description" clearable counter="512" rows="2"></v-textarea>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-textarea :label="$t('Description')" v-model="editingObj.description" clearable counter="512"></v-textarea>
|
||||
<v-file-upload v-model="file" @update:modelValue="updateUserFileName"
|
||||
:title="$t('DragToUpload')"
|
||||
:browse-text="$t('Select_File')"
|
||||
:divider-text="$t('or')"
|
||||
></v-file-upload>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-label>{{ $t('Image')}}</v-label>
|
||||
<v-img style="max-height: 150px" :src="editingObj.image"></v-img>
|
||||
<v-file-input></v-file-input>
|
||||
<v-btn color="delete" prepend-icon="$delete" v-if="editingObj.image">{{$t('Delete')}}</v-btn>
|
||||
<v-btn color="success" prepend-icon="$upload">{{$t('Select')}}</v-btn>
|
||||
<v-label>{{ $t('Image') }}</v-label>
|
||||
<v-img style="max-height: 150px" class="mb-2" :src="editingObj.image">
|
||||
<v-btn color="delete" class="float-right" prepend-icon="$delete" v-if="editingObj.image" @click="deleteImage()">{{ $t('Delete') }}</v-btn>
|
||||
</v-img>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -56,7 +61,7 @@
|
||||
|
||||
</v-tabs-window-item>
|
||||
<v-tabs-window-item value="steps">
|
||||
<v-form :disabled="loading">
|
||||
<v-form :disabled="loading || fileApiLoading">
|
||||
<v-row v-for="(s,i ) in editingObj.steps" :key="s.id">
|
||||
<v-col>
|
||||
<step-editor v-model="editingObj.steps[i]" :step-index="i"></step-editor>
|
||||
@@ -76,13 +81,13 @@
|
||||
</v-form>
|
||||
</v-tabs-window-item>
|
||||
<v-tabs-window-item value="properties">
|
||||
<v-form :disabled="loading">
|
||||
<v-form :disabled="loading || fileApiLoading">
|
||||
<v-alert class="mb-2" icon="$help">{{ $t('PropertiesFoodHelp') }}</v-alert>
|
||||
<properties-editor v-model="editingObj.properties" :amount-for="$t('Serving')"></properties-editor>
|
||||
</v-form>
|
||||
</v-tabs-window-item>
|
||||
<v-tabs-window-item value="settings">
|
||||
<v-form :disabled="loading">
|
||||
<v-form :disabled="loading || fileApiLoading">
|
||||
<v-checkbox :label="$t('Ingredient Overview')" :hint="$t('show_ingredient_overview')" persistent-hint
|
||||
v-model="editingObj.showIngredientOverview"></v-checkbox>
|
||||
|
||||
@@ -118,7 +123,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, ref} from "vue";
|
||||
import {onMounted, PropType, ref, shallowRef} from "vue";
|
||||
import {Recipe, Step} from "@/openapi";
|
||||
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
|
||||
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
|
||||
@@ -127,8 +132,9 @@ import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
import StepEditor from "@/components/inputs/StepEditor.vue";
|
||||
import {VueDraggable} from "vue-draggable-plus";
|
||||
import PropertiesEditor from "@/components/inputs/PropertiesEditor.vue";
|
||||
import {useFileApi} from "@/composables/useFileApi";
|
||||
import {VFileUpload} from 'vuetify/labs/VFileUpload'
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
const props = defineProps({
|
||||
item: {type: {} as PropType<Recipe>, required: false, default: null},
|
||||
@@ -143,6 +149,9 @@ const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading,
|
||||
const tab = ref("recipe")
|
||||
const dialogStepManager = ref(false)
|
||||
|
||||
const {fileApiLoading, updateRecipeImage} = useFileApi()
|
||||
const file = shallowRef<File | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
setupState(props.item, props.itemId, {
|
||||
newItemFunction: () => {
|
||||
@@ -151,6 +160,29 @@ onMounted(() => {
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* save recipe via normal saveMethod and update image afterward if it was changed
|
||||
*/
|
||||
function saveRecipe() {
|
||||
saveObject().then(() => {
|
||||
if (file.value != null && editingObj.value.id) {
|
||||
updateRecipeImage(editingObj.value.id, file.value).then(r => {
|
||||
file.value = null
|
||||
setupState(props.item, props.itemId)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* remove image if delete was manually triggered
|
||||
*/
|
||||
function deleteImage() {
|
||||
updateRecipeImage(editingObj.value.id!, null).then(r => {
|
||||
setupState(props.item, props.itemId)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* called by draggable in step manager dialog when steps are sorted
|
||||
*/
|
||||
|
||||
@@ -16,19 +16,26 @@
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-file-upload v-model="file"></v-file-upload>
|
||||
<v-file-upload v-model="file" @update:modelValue="updateUserFileName"
|
||||
:title="$t('DragToUpload')"
|
||||
:browse-text="$t('Select_File')"
|
||||
:divider-text="$t('or')"
|
||||
></v-file-upload>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-label> {{ $t('Preview') }}</v-label>
|
||||
<v-img max-height="25vh" rounded :src="editingObj.preview"></v-img>
|
||||
<v-btn :href="editingObj.fileDownload" target="_blank" color="success" class="float-right" prepend-icon="fa-solid fa-file-arrow-down">{{ $t('Download') }}</v-btn>
|
||||
<v-btn :href="editingObj.fileDownload" target="_blank" color="success" class="float-right" prepend-icon="fa-solid fa-file-arrow-down"
|
||||
v-if="editingObj.fileDownload != undefined">
|
||||
{{ $t('Download') }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-alert class="mt-2" v-if="!loading && !fileApiLoading && Object.keys(editingObj).length > 0">
|
||||
{{ $n(editingObj.fileSizeKb / 1000) }} MB <br/>
|
||||
{{ editingObj.createdBy.displayName }} <br/>
|
||||
{{ DateTime.fromJSDate(editingObj.createdAt).toLocaleString(DateTime.DATETIME_SHORT) }}
|
||||
<v-alert class="mt-2" v-if="!loading && !fileApiLoading && Object.keys(editingObj).length > 0 && Number(editingObj.fileSizeKb)">
|
||||
<p v-if="Number(editingObj.fileSizeKb)">{{ $n(editingObj.fileSizeKb / 1000) }} MB <br/></p>
|
||||
<p v-if="editingObj.createdBy"> {{ editingObj.createdBy.displayName }}</p>
|
||||
<p v-if="editingObj.createdAt"> {{ DateTime.fromJSDate(editingObj.createdAt).toLocaleString(DateTime.DATETIME_SHORT) }}</p>
|
||||
</v-alert>
|
||||
|
||||
</v-form>
|
||||
@@ -56,26 +63,35 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['create', 'save', 'delete', 'close'])
|
||||
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, modelClass} = useModelEditorFunctions<UserFile>('UserFile', emit)
|
||||
const {fileApiLoading, createOrUpdateUserFile} = useFileApi()
|
||||
|
||||
// object specific data (for selects/display)
|
||||
|
||||
const {fileApiLoading, createOrUpdateUserFile} = useFileApi()
|
||||
const file = shallowRef<File | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
setupState(props.item, props.itemId)
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* save file to database via fileApi composable
|
||||
*/
|
||||
function saveFile() {
|
||||
|
||||
createOrUpdateUserFile(editingObj.value.name, file.value, editingObj.value.id).then(r => {
|
||||
editingObj.value = r
|
||||
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
|
||||
}).catch(err => {
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* set name based on file name if name is empty
|
||||
*/
|
||||
function updateUserFileName() {
|
||||
if (file.value != null && (editingObj.value.name == '' || editingObj.value.name == undefined)) {
|
||||
editingObj.value.name = file.value.name
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user