feat(activities): attach voice and files on activity notes
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -57,7 +57,7 @@ const loading = ref(true);
|
||||
const saving = ref(false);
|
||||
const error = ref('');
|
||||
const activity = ref<ActivityDto | null>(null);
|
||||
const imageUrls = ref<Array<{ id: string; name: string; url: string }>>([]);
|
||||
const imageUrls = ref<Array<{ id: string; name: string; url: string; kind: 'image' | 'audio' | 'file' }>>([]);
|
||||
const moreOpen = ref(false);
|
||||
const commentOpen = ref(false);
|
||||
const postponeOpen = ref(false);
|
||||
@@ -174,15 +174,22 @@ function clearImageUrls() {
|
||||
|
||||
async function loadActivityImages(row: ActivityDto) {
|
||||
clearImageUrls();
|
||||
const next: Array<{ id: string; name: string; url: string }> = [];
|
||||
const next: Array<{ id: string; name: string; url: string; kind: 'image' | 'audio' | 'file' }> = [];
|
||||
for (const image of row.images ?? []) {
|
||||
const mime = image.mimeType.toLowerCase();
|
||||
const kind = mime.startsWith('audio/') ? 'audio' : mime.startsWith('image/') ? 'image' : 'file';
|
||||
try {
|
||||
const { data } = await api.get<Blob>(`/activities/${row.id}/images/${image.id}`, {
|
||||
responseType: 'blob',
|
||||
});
|
||||
next.push({ id: image.id, name: image.fileName, url: URL.createObjectURL(data) });
|
||||
next.push({
|
||||
id: image.id,
|
||||
name: image.fileName,
|
||||
url: URL.createObjectURL(data),
|
||||
kind,
|
||||
});
|
||||
} catch {
|
||||
/* عکس در دسترس نیست */
|
||||
/* فایل در دسترس نیست */
|
||||
}
|
||||
}
|
||||
imageUrls.value = next;
|
||||
@@ -560,11 +567,31 @@ onMounted(() => {
|
||||
<div v-show="noteOpen">
|
||||
<p v-if="activity.description?.trim()">{{ activity.description.trim() }}</p>
|
||||
<p v-else-if="!imageUrls.length" class="empty">یادداشتی ثبت نشده.</p>
|
||||
<div v-if="imageUrls.length" class="note-photos">
|
||||
<a v-for="image in imageUrls" :key="image.id" :href="image.url" target="_blank" rel="noopener">
|
||||
<div v-if="imageUrls.some((image) => image.kind === 'image')" class="note-photos">
|
||||
<a
|
||||
v-for="image in imageUrls.filter((item) => item.kind === 'image')"
|
||||
:key="image.id"
|
||||
:href="image.url"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img :src="image.url" :alt="image.name" />
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="imageUrls.some((image) => image.kind === 'audio')" class="note-voices">
|
||||
<audio
|
||||
v-for="image in imageUrls.filter((item) => item.kind === 'audio')"
|
||||
:key="image.id"
|
||||
:src="image.url"
|
||||
controls
|
||||
preload="metadata"
|
||||
/>
|
||||
</div>
|
||||
<ul v-if="imageUrls.some((image) => image.kind === 'file')" class="note-files">
|
||||
<li v-for="image in imageUrls.filter((item) => item.kind === 'file')" :key="image.id">
|
||||
<a :href="image.url" :download="image.name">{{ image.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -967,6 +994,28 @@ dd {
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.note-voices {
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.note-voices audio {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.note-files {
|
||||
list-style: none;
|
||||
margin: 0.75rem 0 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.note-files a {
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.dock {
|
||||
position: fixed;
|
||||
inset-inline: 0.85rem;
|
||||
|
||||
Reference in New Issue
Block a user