Skip to content

Commit 2f03409

Browse files
feat: 对话上传文件功能前端样式更新
1 parent d958db0 commit 2f03409

File tree

2 files changed

+191
-65
lines changed

2 files changed

+191
-65
lines changed

ui/src/components/ai-chat/component/chat-input-operate/index.vue

Lines changed: 144 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,127 @@
11
<template>
22
<div class="ai-chat__operate p-16-24">
33
<slot name="operateBefore" />
4-
<div class="operate-textarea flex">
5-
<el-input
6-
ref="quickInputRef"
7-
v-model="inputValue"
8-
:placeholder="
9-
startRecorderTime
10-
? '说话中...'
11-
: recorderLoading
12-
? '转文字中...'
13-
: '请输入问题,Ctrl+Enter 换行,Enter发送'
14-
"
15-
:autosize="{ minRows: 1, maxRows: isMobile ? 4 : 10 }"
16-
type="textarea"
17-
:maxlength="100000"
18-
@keydown.enter="sendChatHandle($event)"
19-
/>
4+
<div class="operate-textarea">
5+
<el-scrollbar max-height="136">
6+
<div
7+
class="p-8-12"
8+
v-loading="localLoading"
9+
v-if="uploadDocumentList.length || uploadImageList.length"
10+
>
11+
<el-space wrap>
12+
<!-- <template v-for="(item, index) in uploadDocumentList" :key="index">
13+
<el-card shadow="never" style="--el-card-padding: 8px" class="file cursor">
14+
<div
15+
class="flex align-center"
16+
@mouseenter.stop="mouseenter(item)"
17+
@mouseleave.stop="mouseleave()"
18+
>
19+
<div
20+
@click="deleteFile(index, 'document')"
21+
class="delete-icon color-secondary"
22+
v-if="showDelete === item.url"
23+
>
24+
<el-icon><CircleCloseFilled /></el-icon>
25+
</div>
26+
<img :src="getImgUrl(item && item?.name)" alt="" width="24" />
27+
<div class="ml-4 ellipsis" :title="item && item?.name">
28+
{{ item && item?.name }}
29+
</div>
30+
</div>
31+
</el-card>
32+
</template> -->
33+
<template v-for="(item, index) in uploadImageList" :key="index">
34+
<div
35+
class="file cursor border border-r-4"
36+
v-if="item.url"
37+
@mouseenter.stop="mouseenter(item)"
38+
@mouseleave.stop="mouseleave()"
39+
>
40+
<div
41+
@click="deleteFile(index, 'image')"
42+
class="delete-icon color-secondary"
43+
v-if="showDelete === item.url"
44+
>
45+
<el-icon><CircleCloseFilled /></el-icon>
46+
</div>
47+
<el-image
48+
:src="item.url"
49+
alt=""
50+
fit="cover"
51+
style="width: 40px; height: 40px; display: block"
52+
class="border-r-4"
53+
/>
54+
</div>
55+
</template>
56+
</el-space>
57+
</div>
58+
</el-scrollbar>
59+
<div class="flex">
60+
<el-input
61+
ref="quickInputRef"
62+
v-model="inputValue"
63+
:placeholder="
64+
startRecorderTime
65+
? '说话中...'
66+
: recorderLoading
67+
? '转文字中...'
68+
: '请输入问题,Ctrl+Enter 换行,Enter发送'
69+
"
70+
:autosize="{ minRows: 1, maxRows: isMobile ? 4 : 10 }"
71+
type="textarea"
72+
:maxlength="100000"
73+
@keydown.enter="sendChatHandle($event)"
74+
/>
2075

21-
<div class="operate flex align-center">
22-
<span v-if="props.applicationDetails.file_upload_enable" class="flex align-center">
23-
<!-- accept="image/jpeg, image/png, image/gif"-->
24-
<el-upload
25-
action="#"
26-
:auto-upload="false"
27-
:show-file-list="false"
28-
:accept="[...imageExtensions, ...documentExtensions].map((ext) => '.' + ext).join(',')"
29-
:on-change="(file: any, fileList: any) => uploadFile(file, fileList)"
30-
>
31-
<el-button text>
32-
<el-icon><Paperclip /></el-icon>
33-
</el-button>
34-
</el-upload>
35-
<el-divider direction="vertical" />
36-
</span>
37-
<span v-if="props.applicationDetails.stt_model_enable" class="flex align-center">
38-
<el-button text v-if="mediaRecorderStatus" @click="startRecording">
39-
<el-icon>
40-
<Microphone />
41-
</el-icon>
42-
</el-button>
43-
<div v-else class="operate flex align-center">
44-
<el-text type="info"
45-
>00:{{ recorderTime < 10 ? `0${recorderTime}` : recorderTime }}</el-text
76+
<div class="operate flex align-center">
77+
<span v-if="props.applicationDetails.file_upload_enable" class="flex align-center">
78+
<!-- accept="image/jpeg, image/png, image/gif"-->
79+
<el-upload
80+
action="#"
81+
:auto-upload="false"
82+
:show-file-list="false"
83+
:accept="
84+
[...imageExtensions, ...documentExtensions].map((ext) => '.' + ext).join(',')
85+
"
86+
:on-change="(file: any, fileList: any) => uploadFile(file, fileList)"
4687
>
47-
<el-button text type="primary" @click="stopRecording" :loading="recorderLoading">
48-
<AppIcon iconName="app-video-stop"></AppIcon>
88+
<el-button text>
89+
<el-icon><Paperclip /></el-icon>
90+
</el-button>
91+
</el-upload>
92+
<el-divider direction="vertical" />
93+
</span>
94+
<span v-if="props.applicationDetails.stt_model_enable" class="flex align-center">
95+
<el-button text v-if="mediaRecorderStatus" @click="startRecording">
96+
<el-icon>
97+
<Microphone />
98+
</el-icon>
4999
</el-button>
50-
</div>
51-
<el-divider v-if="!startRecorderTime && !recorderLoading" direction="vertical" />
52-
</span>
100+
<div v-else class="operate flex align-center">
101+
<el-text type="info"
102+
>00:{{ recorderTime < 10 ? `0${recorderTime}` : recorderTime }}</el-text
103+
>
104+
<el-button text type="primary" @click="stopRecording" :loading="recorderLoading">
105+
<AppIcon iconName="app-video-stop"></AppIcon>
106+
</el-button>
107+
</div>
108+
<el-divider v-if="!startRecorderTime && !recorderLoading" direction="vertical" />
109+
</span>
53110

54-
<el-button
55-
v-if="!startRecorderTime && !recorderLoading"
56-
text
57-
class="sent-button"
58-
:disabled="isDisabledChart || loading"
59-
@click="sendChatHandle"
60-
>
61-
<img v-show="isDisabledChart || loading" src="@/assets/icon_send.svg" alt="" />
62-
<SendIcon v-show="!isDisabledChart && !loading" />
63-
</el-button>
111+
<el-button
112+
v-if="!startRecorderTime && !recorderLoading"
113+
text
114+
class="sent-button"
115+
:disabled="isDisabledChart || loading"
116+
@click="sendChatHandle"
117+
>
118+
<img v-show="isDisabledChart || loading" src="@/assets/icon_send.svg" alt="" />
119+
<SendIcon v-show="!isDisabledChart && !loading" />
120+
</el-button>
121+
</div>
64122
</div>
65123
</div>
66-
<div
67-
class="text-center"
68-
v-if="applicationDetails.disclaimer"
69-
style="margin-top: 8px"
70-
>
124+
<div class="text-center" v-if="applicationDetails.disclaimer" style="margin-top: 8px">
71125
<el-text type="info" v-if="applicationDetails.disclaimer" style="font-size: 12px">
72126
<auto-tooltip :content="applicationDetails.disclaimer_value">
73127
{{ applicationDetails.disclaimer_value }}
@@ -83,7 +137,9 @@ import applicationApi from '@/api/application'
83137
import { MsgAlert } from '@/utils/message'
84138
import { type chatType } from '@/api/type/application'
85139
import { useRoute } from 'vue-router'
140+
import { getImgUrl } from '@/utils/utils'
86141
import 'recorder-core/src/engine/mp3'
142+
87143
import 'recorder-core/src/engine/mp3-engine'
88144
import { MsgWarning } from '@/utils/message'
89145
const route = useRoute()
@@ -130,7 +186,6 @@ const localLoading = computed({
130186
}
131187
})
132188
133-
134189
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp']
135190
const documentExtensions = ['pdf', 'docx', 'txt', 'xls', 'xlsx', 'md', 'html', 'csv']
136191
const videoExtensions = ['mp4', 'avi', 'mov', 'mkv', 'flv']
@@ -209,6 +264,8 @@ const inputValue = ref<string>('')
209264
const uploadImageList = ref<Array<any>>([])
210265
const uploadDocumentList = ref<Array<any>>([])
211266
const mediaRecorderStatus = ref(true)
267+
const showDelete = ref('')
268+
212269
// 定义响应式引用
213270
const mediaRecorder = ref<any>(null)
214271
const isDisabledChart = computed(
@@ -346,6 +403,21 @@ function sendChatHandle(event: any) {
346403
inputValue.value += '\n'
347404
}
348405
}
406+
407+
function deleteFile(index: number, val: string) {
408+
if (val === 'image') {
409+
uploadImageList.value.splice(index, 1)
410+
} else if (val === 'document') {
411+
uploadDocumentList.value.splice(index, 1)
412+
}
413+
}
414+
function mouseenter(row: any) {
415+
showDelete.value = row.url
416+
}
417+
function mouseleave() {
418+
showDelete.value = ''
419+
}
420+
349421
onMounted(() => {
350422
setTimeout(() => {
351423
if (quickInputRef.value && mode === 'embed') {
@@ -356,4 +428,14 @@ onMounted(() => {
356428
</script>
357429
<style lang="scss" scope>
358430
@import '../../index.scss';
431+
.file {
432+
position: relative;
433+
overflow: inherit;
434+
.delete-icon {
435+
position: absolute;
436+
right: -5px;
437+
top: -5px;
438+
z-index: 1;
439+
}
440+
}
359441
</style>

ui/src/components/ai-chat/component/question-content/index.vue

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,70 @@
1515
</div>
1616
<div class="content">
1717
<div class="text break-all pre-wrap">
18+
<div class="mb-8" v-if="document_list.length">
19+
<el-space wrap>
20+
<template v-for="(item, index) in document_list" :key="index">
21+
<el-card shadow="never" style="--el-card-padding: 8px" class="file cursor">
22+
<div class="flex align-center">
23+
<img :src="getImgUrl(item && item?.name)" alt="" width="24" />
24+
<div class="ml-4 ellipsis" :title="item && item?.name">
25+
{{ item && item?.name }}
26+
</div>
27+
</div>
28+
</el-card>
29+
</template>
30+
</el-space>
31+
</div>
32+
<div class="mb-8" v-if="image_list.length">
33+
<el-space wrap>
34+
<template v-for="(item, index) in image_list" :key="index">
35+
<div class="file cursor border border-r-4" v-if="item.url">
36+
<el-image
37+
:src="item.url"
38+
:zoom-rate="1.2"
39+
:max-scale="7"
40+
:min-scale="0.2"
41+
:preview-src-list="getAttrsArray(image_list, 'url')"
42+
alt=""
43+
fit="cover"
44+
style="width: 170px; height: 170px; display: block"
45+
class="border-r-4"
46+
/>
47+
</div>
48+
</template>
49+
</el-space>
50+
</div>
1851
{{ chatRecord.problem_text }}
1952
</div>
2053
</div>
2154
</div>
2255
</template>
2356
<script setup lang="ts">
2457
import { type chatType } from '@/api/type/application'
25-
import { onMounted } from 'vue'
58+
import { getImgUrl, getAttrsArray } from '@/utils/utils'
59+
import { onMounted, computed } from 'vue'
2660
const props = defineProps<{
2761
application: any
2862
chatRecord: chatType
2963
}>()
64+
const document_list = computed(() => {
65+
return []
66+
})
67+
const image_list = computed(() => {
68+
if (props.chatRecord.execution_details?.length > 0) {
69+
return props.chatRecord.execution_details[0].image_list
70+
} else {
71+
return []
72+
}
73+
})
3074
3175
onMounted(() => {
76+
console.log(props.chatRecord.execution_details)
3277
if (props.chatRecord.execution_details?.length > 0) {
3378
props.chatRecord.execution_details[0].image_list?.forEach((image: any) => {
3479
console.log('image', image.name, image.url)
3580
})
3681
}
3782
})
3883
</script>
39-
<style lang="scss" scoped>
40-
</style>
84+
<style lang="scss" scoped></style>

0 commit comments

Comments
 (0)