kin: KIN-089 При попытке добавить креды прод сервера для проекта corelock вылетает 500 Internal Server Error
This commit is contained in:
parent
e80e50ba0c
commit
4a65d90218
13 changed files with 1215 additions and 4 deletions
55
web/frontend/src/components/AttachmentList.vue
Normal file
55
web/frontend/src/components/AttachmentList.vue
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { api, type Attachment } from '../api'
|
||||
|
||||
const props = defineProps<{ attachments: Attachment[]; taskId: string }>()
|
||||
const emit = defineEmits<{ deleted: [] }>()
|
||||
|
||||
const deletingId = ref<number | null>(null)
|
||||
|
||||
async function remove(id: number) {
|
||||
deletingId.value = id
|
||||
try {
|
||||
await api.deleteAttachment(props.taskId, id)
|
||||
emit('deleted')
|
||||
} catch {
|
||||
// silently ignore — parent will reload
|
||||
} finally {
|
||||
deletingId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function formatSize(bytes: number): string {
|
||||
if (bytes < 1024) return `${bytes}B`
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`
|
||||
return `${(bytes / 1024 / 1024).toFixed(1)}MB`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="attachments.length" class="flex flex-wrap gap-3 mb-3">
|
||||
<div
|
||||
v-for="att in attachments"
|
||||
:key="att.id"
|
||||
class="relative group border border-gray-700 rounded-lg overflow-hidden bg-gray-900 w-28"
|
||||
>
|
||||
<a :href="api.attachmentUrl(att.id)" target="_blank" rel="noopener">
|
||||
<img
|
||||
:src="api.attachmentUrl(att.id)"
|
||||
:alt="att.filename"
|
||||
class="w-28 h-20 object-cover block"
|
||||
/>
|
||||
</a>
|
||||
<div class="px-1.5 py-1">
|
||||
<p class="text-[10px] text-gray-400 truncate" :title="att.filename">{{ att.filename }}</p>
|
||||
<p class="text-[10px] text-gray-600">{{ formatSize(att.size) }}</p>
|
||||
</div>
|
||||
<button
|
||||
@click="remove(att.id)"
|
||||
:disabled="deletingId === att.id"
|
||||
class="absolute top-1 right-1 w-5 h-5 rounded-full bg-red-900/80 text-red-400 text-xs leading-none opacity-0 group-hover:opacity-100 transition-opacity disabled:opacity-50 flex items-center justify-center"
|
||||
title="Удалить"
|
||||
>✕</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
62
web/frontend/src/components/AttachmentUploader.vue
Normal file
62
web/frontend/src/components/AttachmentUploader.vue
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { api } from '../api'
|
||||
|
||||
const props = defineProps<{ taskId: string }>()
|
||||
const emit = defineEmits<{ uploaded: [] }>()
|
||||
|
||||
const dragging = ref(false)
|
||||
const uploading = ref(false)
|
||||
const error = ref('')
|
||||
const fileInput = ref<HTMLInputElement | null>(null)
|
||||
|
||||
async function upload(file: File) {
|
||||
if (!file.type.startsWith('image/')) {
|
||||
error.value = 'Поддерживаются только изображения'
|
||||
return
|
||||
}
|
||||
uploading.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
await api.uploadAttachment(props.taskId, file)
|
||||
emit('uploaded')
|
||||
} catch (e: any) {
|
||||
error.value = e.message
|
||||
} finally {
|
||||
uploading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onFileChange(event: Event) {
|
||||
const input = event.target as HTMLInputElement
|
||||
if (input.files?.[0]) upload(input.files[0])
|
||||
input.value = ''
|
||||
}
|
||||
|
||||
function onDrop(event: DragEvent) {
|
||||
dragging.value = false
|
||||
const file = event.dataTransfer?.files[0]
|
||||
if (file) upload(file)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="border-2 border-dashed rounded-lg p-3 text-center transition-colors cursor-pointer select-none"
|
||||
:class="dragging ? 'border-blue-500 bg-blue-950/20' : 'border-gray-700 hover:border-gray-500'"
|
||||
@dragover.prevent="dragging = true"
|
||||
@dragleave="dragging = false"
|
||||
@drop.prevent="onDrop"
|
||||
@click="fileInput?.click()"
|
||||
>
|
||||
<input ref="fileInput" type="file" accept="image/*" class="hidden" @change="onFileChange" />
|
||||
<div v-if="uploading" class="flex items-center justify-center gap-2 text-xs text-blue-400">
|
||||
<span class="inline-block w-3 h-3 border-2 border-blue-400 border-t-transparent rounded-full animate-spin"></span>
|
||||
Загрузка...
|
||||
</div>
|
||||
<div v-else class="text-xs text-gray-500">
|
||||
Перетащите изображение или <span class="text-blue-400">нажмите для выбора</span>
|
||||
</div>
|
||||
<p v-if="error" class="text-red-400 text-xs mt-1">{{ error }}</p>
|
||||
</div>
|
||||
</template>
|
||||
Loading…
Add table
Add a link
Reference in a new issue