You've already forked agentic-coding-workflow
90 lines
2.3 KiB
Vue
90 lines
2.3 KiB
Vue
<template>
|
|
<div class="flex gap-2 items-center flex-wrap">
|
|
<NUpload :custom-request="upload" :show-file-list="false" multiple>
|
|
<NButton size="small"> 添加附件 </NButton>
|
|
</NUpload>
|
|
<div
|
|
v-for="a in attachments"
|
|
:key="a.id"
|
|
class="text-xs px-2 py-1 bg-neutral-100 rounded flex items-center gap-1"
|
|
>
|
|
<span>{{ a.filename }} ({{ Math.round(a.size_bytes / 1024) }} KB)</span>
|
|
<NButton text size="tiny" @click="remove(a.id)"> × </NButton>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, watch } from 'vue'
|
|
import { NUpload, NButton, useMessage } from 'naive-ui'
|
|
import type { UploadCustomRequestOptions } from 'naive-ui'
|
|
|
|
import { chatApi, type Attachment } from '@/api/chat'
|
|
|
|
const props = defineProps<{
|
|
modelCapabilities?: { image: boolean; pdf: boolean; audio?: boolean }
|
|
}>()
|
|
const emit = defineEmits<{ change: [ids: string[]] }>()
|
|
|
|
const attachments = ref<Attachment[]>([])
|
|
const msg = useMessage()
|
|
|
|
const MAX_ATTACHMENT_SIZE_MB = 20
|
|
const MAX_ATTACHMENT_SIZE_BYTES = MAX_ATTACHMENT_SIZE_MB * 1024 * 1024
|
|
|
|
async function upload(opts: UploadCustomRequestOptions) {
|
|
const f = opts.file.file
|
|
if (!f) {
|
|
opts.onError()
|
|
return
|
|
}
|
|
if (f.type.startsWith('image/') && !props.modelCapabilities?.image) {
|
|
msg.error('当前模型不支持图片')
|
|
opts.onError()
|
|
return
|
|
}
|
|
if (f.type === 'application/pdf' && !props.modelCapabilities?.pdf) {
|
|
msg.error('当前模型不支持 PDF')
|
|
opts.onError()
|
|
return
|
|
}
|
|
if (f.type.startsWith('audio/') && !props.modelCapabilities?.audio) {
|
|
msg.error('当前模型不支持音频')
|
|
opts.onError()
|
|
return
|
|
}
|
|
if (f.size > MAX_ATTACHMENT_SIZE_BYTES) {
|
|
msg.error(`单文件不能超过 ${MAX_ATTACHMENT_SIZE_MB} MB`)
|
|
opts.onError()
|
|
return
|
|
}
|
|
try {
|
|
const a = await chatApi.uploadAttachment(f)
|
|
attachments.value.push(a)
|
|
opts.onFinish()
|
|
} catch (e) {
|
|
msg.error(e instanceof Error ? e.message : String(e))
|
|
opts.onError()
|
|
}
|
|
}
|
|
|
|
function remove(id: string) {
|
|
attachments.value = attachments.value.filter((a) => a.id !== id)
|
|
}
|
|
|
|
watch(
|
|
() => attachments.value.map((a) => a.id),
|
|
() =>
|
|
emit(
|
|
'change',
|
|
attachments.value.map((a) => a.id),
|
|
),
|
|
)
|
|
|
|
defineExpose({
|
|
clear: () => {
|
|
attachments.value = []
|
|
},
|
|
})
|
|
</script>
|