在下面的视频中,您可以看到我有一个带有缩略图单元格的表格,您可以在单击它时更改图片。如视频中所示,UI 没有正确更新,但数据可以。关闭表格时,正确的图片会显示在正确的行中。
此外,加载指示器也不会显示在正确的单元格上,即使它们都是通过数组迭代的相同组件。
我正在使用 svelte-headless-table 库制作这张表,数据是来自 supabase 的 JSON 格式。
https://youtube.com/shorts/8kakKWNrIMI?feature=share
这是我认为可能是问题的代码,但由于该数据已正确更新,因此可能是其他问题。我需要 UI 才能正确更新。
更新数据库
async function updateThumbnail() {
console.log('updating thumbnail', $rowIndex);
// This is the line that adds the new pictureURL to the database
$documentBody[$rowIndex].thumbnail_URL = pictureURL;
const { error } = await supabaseClient
.from('documents')
.update({ document_body: $documentBody })
.eq('document_id', $documentID);
if (error) {
console.log('THERE WAS AN ERROR');
showError = true;
errorMessage = 'There was an error updating your contact. Error: ' + error;
console.log(error);
} else if (error == null) {
showError = false;
console.log('Update successful');
getMaterialSchedule();
setTimeout(getAvatarURL, 3000);
} else {
showError = true;
errorMessage = 'There was an error updating your contact. Error: ' + error;
console.log('Update failed');
}
}
细胞成分
<script lang="ts">
import { supabaseClient } from '$lib/db';
import { slide, fade, fly } from 'svelte/transition';
import { onMount } from 'svelte';
import { loadingAction } from 'svelte-legos';
import {
documentID,
documentName,
documentProjectID,
documentFirmID,
documentType,
documentLastModified,
documentLastModifiedBy,
documentNotes,
documentBody,
showDocumentFrame,
showCreateNewDocument,
rowIndex
} from '/src/stores/documentStore';
export let pictureURL;
export let thumbnailFit;
//export let row; /*: BodyRow<Item>*/
let showError = false;
let errorMessage = '';
let showUI = false;
let showFitAndFill = false;
let src: string;
let loading = true;
let realPhotoUrl = '';
let hasPhoto = false;
let size = 10;
let url: string;
let uploading = false;
let files: FileList;
// handle file upload and only allow one file and for the file to be either .jpg or .png
// if the file is larger then 5mb then it will not be accepted
function handleFileChange(e) {
files = e.target.files;
if (files.length > 1) {
console.log('Only one file allowed');
return;
}
const file = files[0];
if (file.type !== 'image/jpeg' && file.type !== 'image/png') {
showError = true;
errorMessage = 'Only .jpg and .png files allowed';
console.log('Only .jpg and .png files allowed');
return;
}
if (file.size > 5000000) {
showError = true;
errorMessage = 'File size too large (5mb max)';
console.log('File size too large');
return;
}
uploadAvatar();
}
// GET URL
async function getAvatarURL() {
if (pictureURL == 'null' || pictureURL == undefined || pictureURL == '--') {
hasPhoto = false;
console.log('no picture');
} else {
const { data } = await supabaseClient.storage.from('documents').getPublicUrl(pictureURL);
if (data) {
realPhotoUrl = data.publicUrl;
src = realPhotoUrl;
console.log('HAS picture');
loading = false;
hasPhoto = true;
}
}
}
// HANDLE UPLOAD
async function uploadAvatar() {
console.log('UPLOADING', $rowIndex);
loading = true;
const file = files[0];
const fileExt = file.name.split('.').pop();
const filePath = `${$documentID}_${Date.now()}.${fileExt}`;
const { data, error } = await supabaseClient.storage
.from('documents')
.upload('materialScheduleRows/' + filePath, file, {
upsert: true
});
if (data) {
url = 'materialScheduleRows/' + filePath;
pictureURL = url;
updateThumbnail();
} else if (error) {
showError = true;
errorMessage = 'There was an error uploading your photo. Error: ' + error;
console.log(error);
}
}
async function updateThumbnail() {
console.log('updating thumbnail', $rowIndex);
console.log('OLD DOCUMENT BODY', $documentBody);
$documentBody[$rowIndex].thumbnail_URL = pictureURL;
console.log('NEW DOCUMENT BODY', $documentBody);
const { error } = await supabaseClient
.from('documents')
.update({ document_body: $documentBody })
.eq('document_id', $documentID);
if (error) {
console.log('THERE WAS AN ERROR UPDATING THE USER PROFILE');
showError = true;
errorMessage = 'There was an error updating your contact. Error: ' + error;
console.log(error);
} else if (error == null) {
showError = false;
console.log('Update successful');
getMaterialSchedule();
setTimeout(getAvatarURL, 3000);
} else {
showError = true;
errorMessage = 'There was an error updating your contact. Error: ' + error;
console.log('Update failed');
}
}
async function getMaterialSchedule() {
const { data: documentJSON, error } = await supabaseClient
.from('documents')
.select('document_body')
.eq('document_id', $documentID);
[];
if (error) {
// showEmptyState = true;
console.log('Error fetching contacts', error);
// rowData= roleArray
} else {
const [fetchedRows] = documentJSON;
documentBody.set(fetchedRows.document_body);
getAvatarURL();
}
}
onMount(() => {
getAvatarURL();
});
function handleMouseOver() {
showUI = true;
}
function handleMouseOut() {
showUI = false;
}
function showButtons() {
showFitAndFill = !showFitAndFill;
}
function fitImage() {
thumbnailFit = true;
$documentBody[$rowIndex].thumbnail_fit = true;
showFitAndFill = false;
updateThumbnail();
}
function fillImage() {
thumbnailFit = false;
$documentBody[$rowIndex].thumbnail_fit = false;
showFitAndFill = false;
updateThumbnail();
}
</script>
<div class="flex gap-4 flex-row align-middle content-center">
{#if pictureURL == '' || pictureURL == 'null' || pictureURL == undefined || pictureURL == '--'}
<div>
<label for="nonExisting">
<div
class=" transition-all bg-neutral-50 border-2 rounded-sm border-neutral-300 h-24 w-24 flex text-center text-neutral-700 hover:bg-neutral-100 hover:border-neutral-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-50 hover:text-neutral-700 "
>
<svg
class="self-center mx-auto "
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
opacity="0.3"
fill-rule="evenodd"
clip-rule="evenodd"
d="M9.99984 18.3332C14.6022 18.3332 18.3332 14.6022 18.3332 9.99984C18.3332 5.39746 14.6022 1.6665 9.99984 1.6665C5.39746 1.6665 1.6665 5.39746 1.6665 9.99984C1.6665 14.6022 5.39746 18.3332 9.99984 18.3332Z"
fill="currentColor"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M10.8333 5.83333C10.8333 5.3731 10.4602 5 10 5C9.53976 5 9.16667 5.3731 9.16667 5.83333V9.16667H5.83333C5.3731 9.16667 5 9.53976 5 10C5 10.4602 5.3731 10.8333 5.83333 10.8333H9.16667V14.1667C9.16667 14.6269 9.53976 15 10 15C10.4602 15 10.8333 14.6269 10.8333 14.1667V10.8333H14.1667C14.6269 10.8333 15 10.4602 15 10C15 9.53976 14.6269 9.16667 14.1667 9.16667H10.8333V5.83333Z"
fill="currentColor"
/>
<rect width="20" height="20" />
</svg>
</div>
</label>
<input
style="visibility: hidden; position:absolute;"
type="file"
id="nonExisting"
accept="image/*"
bind:files
on:change={handleFileChange}
disabled={uploading}
/>
</div>
{:else}
<div class="flex flex-col text-center">
<div
on:mouseover={handleMouseOver}
on:mouseout={handleMouseOut}
on:focus={handleMouseOver}
on:blur={handleMouseOut}
class="flex flex-row "
>
{#if showUI}
<button
on:click={showButtons}
transition:fade
class="flex text-neutral-400 transition-all hover:text-neutral-500 my-auto mx-auto"
>
<svg
class="self-center"
width="24"
height="24"
viewBox="0 0 48 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="24" cy="10" r="4" fill="currentColor" />
<circle cx="24" cy="24" r="4" fill="currentColor" />
<circle cx="24" cy="38" r="4" fill="currentColor" />
</svg>
</button>
{/if}
<label for="existing">
<div
use:loadingAction={loading}
class=" transition-all bg-neutral-50 border-2 rounded-sm border-neutral-300 h-24 w-24 flex text-center text-neutral-700 hover:bg-neutral-100 hover:border-neutral-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-50 hover:text-neutral-700 "
>
<img
{src}
alt={realPhotoUrl ? 'Avatar' : 'No image'}
class=" mx-auto transition-all {thumbnailFit === true
? 'object-contain '
: 'w-full h-full object-cover'} "
/>
</div>
</label>
<input
style="visibility: hidden; position:absolute;"
type="file"
id="existing"
accept="image/*"
bind:files
on:change={handleFileChange}
disabled={uploading}
/>
</div>
{#if showFitAndFill}
<div transition:slide class="flex w-full justify-center mx-auto gap-4 flex-row">
<button on:click={fitImage} class="p-2 mt-2 rounded-md hover:bg-neutral-200 ">
<svg
width="24"
height="24"
viewBox="0 0 48 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="4"
y="12"
width="40"
height="24"
rx="1.5"
stroke="currentColor"
stroke-width="4"
/>
<rect
x="14"
y="12"
width="20"
height="24"
rx="1.5"
fill="currentColor"
fill-opacity="0.3"
/>
</svg>
</button>
<button on:click={fillImage} class="p-2 mt-2 rounded-md hover:bg-neutral-200 ">
<svg
width="24"
height="24"
viewBox="0 0 48 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="4"
y="8"
width="40"
height="32"
rx="1.5"
fill="currentColor"
fill-opacity="0.3"
/>
<rect
x="4"
y="12"
width="40"
height="24"
rx="1.5"
stroke="currentColor"
stroke-width="4"
/>
</svg>
</button>
</div>
{/if}
</div>
{/if}
<!-- ERROR MESSAGE -->
{#if showError === true}
<div
transition:slide
class="p-4 mb-8 border gap-2 border-pink-700 border-t-4 bg-pink-100 flex flex-row"
>
<h1 class=" text-sm font-display uppercase text-pink-700">Error:</h1>
<p class="text-pink-700 text-sm ">{errorMessage}</p>
</div>
{/if}
<!-- END ERROR MESSAGE -->
</div>