Model photo upload fixes; network devices linked to catalog models
This commit is contained in:
@@ -138,34 +138,51 @@
|
||||
<div class="image-manage">
|
||||
<img v-if="form.imageurl" :src="withBase(form.imageurl)" alt="Model image" class="image-thumb" />
|
||||
<div class="image-actions">
|
||||
<template v-if="editingModel">
|
||||
<input
|
||||
ref="imageFileInput"
|
||||
type="file"
|
||||
accept=".png,.jpg,.jpeg,.gif,.webp,.svg"
|
||||
style="display: none"
|
||||
@change="onImageSelected"
|
||||
/>
|
||||
<button type="button" class="btn btn-secondary btn-sm" :disabled="uploadingImage" @click="triggerImageUpload">
|
||||
{{ uploadingImage ? 'Uploading...' : (form.imageurl ? 'Replace' : 'Upload') }}
|
||||
</button>
|
||||
<button v-if="form.imageurl" type="button" class="btn btn-danger btn-sm" @click="removeImage">Remove</button>
|
||||
</template>
|
||||
<small v-else class="text-muted">Save the model first, then upload a photo.</small>
|
||||
<!--
|
||||
Offered when CREATING too. The photo is stored as
|
||||
model-<id>.<ext>, so it cannot be sent before the model has
|
||||
an id - but making the operator save, reopen and come back
|
||||
just to add a picture reads as a missing feature. On a new
|
||||
model the file is held here and uploaded immediately after
|
||||
the model is created.
|
||||
-->
|
||||
<input
|
||||
ref="imageFileInput"
|
||||
type="file"
|
||||
accept=".png,.jpg,.jpeg,.gif,.webp,.svg"
|
||||
style="display: none"
|
||||
@change="onImageSelected"
|
||||
/>
|
||||
<button type="button" class="btn btn-secondary btn-sm" :disabled="uploadingImage" @click="triggerImageUpload">
|
||||
{{ uploadingImage ? 'Uploading...' : ((form.imageurl || pendingImageFile) ? 'Replace' : 'Upload') }}
|
||||
</button>
|
||||
<button v-if="form.imageurl || pendingImageFile" type="button" class="btn btn-danger btn-sm" @click="removeImage">Remove</button>
|
||||
<small v-if="pendingImageFile" class="text-muted pending-note">
|
||||
{{ pendingImageFile.name }} will be uploaded when you save.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="imageurl">Image URL</label>
|
||||
<!--
|
||||
type="text", NOT type="url". An uploaded photo sets this field to
|
||||
an application path such as /api/models/image/model-120.png, and
|
||||
native url validation demands an absolute URL with a scheme - so
|
||||
the browser refused to save the form with "Please enter a URL"
|
||||
for a value this page had just written itself.
|
||||
-->
|
||||
<input
|
||||
id="imageurl"
|
||||
v-model="form.imageurl"
|
||||
type="url"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="https://..."
|
||||
placeholder="https://... or /api/models/image/..."
|
||||
/>
|
||||
<small class="text-muted">Manual alternative. Uploading a photo overwrites this URL.</small>
|
||||
<small class="text-muted">
|
||||
Set automatically when you upload a photo. Accepts a full web address or a path on this server.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@@ -224,6 +241,8 @@ const perPage = ref(20)
|
||||
|
||||
const showModal = ref(false)
|
||||
const editingModel = ref(null)
|
||||
// A photo chosen while CREATING a model, uploaded once the model has an id.
|
||||
const pendingImageFile = ref(null)
|
||||
const saving = ref(false)
|
||||
const error = ref('')
|
||||
|
||||
@@ -337,6 +356,9 @@ function openModal(m = null) {
|
||||
function closeModal() {
|
||||
showModal.value = false
|
||||
editingModel.value = null
|
||||
// Or a photo picked for a model that was never saved would be uploaded onto
|
||||
// the NEXT model created in this session.
|
||||
pendingImageFile.value = null
|
||||
}
|
||||
|
||||
async function saveModel() {
|
||||
@@ -351,7 +373,20 @@ async function saveModel() {
|
||||
if (editingModel.value) {
|
||||
await modelsApi.update(editingModel.value.modelnumberid, data)
|
||||
} else {
|
||||
await modelsApi.create(data)
|
||||
const created = await modelsApi.create(data)
|
||||
// Send a photo chosen before the model existed. A failure here must not
|
||||
// read as "the model was not saved" - it was.
|
||||
if (pendingImageFile.value) {
|
||||
const newId = created?.data?.data?.modelnumberid
|
||||
if (newId) {
|
||||
try {
|
||||
await modelsApi.uploadImage(newId, pendingImageFile.value)
|
||||
} catch (imgErr) {
|
||||
console.error('Error uploading image:', imgErr)
|
||||
toast.error(apiError(imgErr, 'Model saved, but the photo could not be uploaded'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closeModal()
|
||||
loadModels()
|
||||
@@ -369,7 +404,14 @@ function triggerImageUpload() {
|
||||
|
||||
async function onImageSelected(event) {
|
||||
const file = event.target.files?.[0]
|
||||
if (!file || !editingModel.value) return
|
||||
if (!file) return
|
||||
// No model id yet, so hold the file and send it once saveModel has created
|
||||
// the record. Nothing is written to the server until the operator saves.
|
||||
if (!editingModel.value) {
|
||||
pendingImageFile.value = file
|
||||
if (imageFileInput.value) imageFileInput.value.value = ''
|
||||
return
|
||||
}
|
||||
uploadingImage.value = true
|
||||
try {
|
||||
const response = await modelsApi.uploadImage(editingModel.value.modelnumberid, file)
|
||||
@@ -387,7 +429,12 @@ async function onImageSelected(event) {
|
||||
}
|
||||
|
||||
async function removeImage() {
|
||||
if (!editingModel.value) return
|
||||
// On a new model there is nothing on the server yet; just drop the choice.
|
||||
if (!editingModel.value) {
|
||||
pendingImageFile.value = null
|
||||
form.value.imageurl = ''
|
||||
return
|
||||
}
|
||||
if (!confirm('Remove this model image?')) return
|
||||
try {
|
||||
await modelsApi.removeImage(editingModel.value.modelnumberid)
|
||||
|
||||
Reference in New Issue
Block a user