11<script setup lang="ts">
22import { ref } from ' vue'
3- import { CircleCloseFilled , Paperclip , Promotion , VideoPause } from ' @element-plus/icons-vue'
3+ import { Headset } from ' @element-plus/icons-vue'
4+ import { getFileExtension , getFileIconUrl } from ' @/utils/icon'
5+ import { formatFileSize } from ' @/utils/number'
46import { inputShortcut } from ' ../../core/shortcuts'
57import { useMessageInputStore } from ' ./index'
68
79const {
810 question,
911 fileList,
10- showDelete,
1112 maxFiles,
1213 maxSizeMB,
1314 acceptList,
1415 loading,
1516 placeholder,
1617 canSend,
1718 imageFiles,
18- documentFiles,
1919 audioFiles,
2020 videoFiles,
21- getFileIcon,
2221 addFiles,
2322 removeFile,
2423 send,
@@ -50,91 +49,41 @@ const handleKeydown = (e: KeyboardEvent) => {
5049 </script >
5150
5251<template >
53- <div class =" mk-input-box conversation-message-input w-full border shadow-lg transition-colors focus-within:border-primary" >
54- <!-- 文件预览区域 -->
55- <div v-if =" fileList.length" class =" mb-3 space-y-2" >
56- <!-- 图片预览 -->
57- <el-space v-if =" imageFiles.length" wrap >
58- <template v-for =" (file , index ) in imageFiles " :key =" file .uid " >
59- <div class =" relative" @mouseenter =" showDelete = file.url || ''" @mouseleave =" showDelete = ''" >
60- <!-- 移除图片附件 -->
61- <button
62- v-if =" showDelete === file.url"
63- type =" button"
64- class =" attachment-remove absolute -top-[5px] -right-[5px] z-1"
65- @click =" removeFile(index)"
66- >
67- <MkIcon :icon =" CircleCloseFilled " />
68- </button >
69- <el-image v-if =" file.url" :src =" file.url" fit =" cover" class =" block h-10 w-10 rounded-md" />
70- <el-image v-else-if =" file.previewUrl" :src =" file.previewUrl" fit =" cover" class =" block h-10 w-10 rounded-md" />
71- </div >
72- </template >
73- </el-space >
74-
75- <!-- 文档预览 -->
76- <el-row v-if =" documentFiles.length" :gutter =" 8" class =" gap-y-2" >
77- <template v-for =" (file , index ) in documentFiles " :key =" file .uid " >
78- <el-col :xs =" 24" :sm =" 12" >
79- <el-card shadow =" never" body-class =" p-2!" class =" cursor-pointer" @mouseenter =" showDelete = file.url || ''" @mouseleave =" showDelete = ''" >
80- <div class =" flex-between gap-2" >
81- <div class =" flex-align-center min-w-0 gap-1" >
82- <img :src =" getFileIcon(file.name)" alt =" " class =" w-6 shrink-0" />
83- <span class =" truncate" :title =" file.name" >{{ file.name }}</span >
84- </div >
85- <!-- 移除文档附件 -->
86- <button v-if =" showDelete === file.url" type =" button" class =" attachment-remove" @click =" removeFile(index)" >
87- <MkIcon :icon =" CircleCloseFilled " />
88- </button >
52+ <div class =" mk-input-box mk-conversation-message-input w-full shadow-lg" >
53+ <!-- 文件预览区域:所有类型按上传顺序横向排列。 // TODO 未来会改成横向有左右按钮控制 -->
54+ <el-scrollbar v-if =" fileList.length" class =" mb-3 h-auto!" view-class =" flex-align-center gap-2" >
55+ <template v-for =" (file , index ) in fileList " :key =" file .uid " >
56+ <div class =" group relative shrink-0" >
57+ <el-image
58+ v-if =" imageFiles.includes(file) && (file.url || file.previewUrl)"
59+ :src =" file.url || file.previewUrl"
60+ :alt =" file.name"
61+ :title =" file.name"
62+ fit =" cover"
63+ />
64+ <video v-else-if =" videoFiles.includes(file) && file.url" :src =" file.url" :title =" file.name" controls autoplay class =" object-cover" />
65+ <el-card shadow =" never" v-else class =" small w-60 rounded-lg!" >
66+ <div class =" flex-align-center gap-2" >
67+ <MkIcon v-if =" audioFiles .includes (file )" :icon =" Headset " :size =" 24 " class="shrink-0 text-N600" />
68+ <img v-else :src =" getFileIconUrl(file.name)" alt =" " class =" w-8 shrink-0" />
69+ <div class =" min-w-0 flex-1" >
70+ <div class =" truncate" :title =" file.name" >{{ file.name }}</div >
71+ <div class =" text-sm text-N500" >{{ getFileExtension(file.name).toUpperCase() || '文件' }} – {{ formatFileSize(file.size) }}</div >
8972 </div >
90- </el-card >
91- </el-col >
92- </template >
93- </el-row >
94-
95- <!-- 音频预览 -->
96- <el-row v-if =" audioFiles.length" :gutter =" 8" class =" gap-y-2" >
97- <template v-for =" (file , index ) in audioFiles " :key =" file .uid " >
98- <el-col :xs =" 24" :sm =" 12" >
99- <el-card shadow =" never" body-class =" p-2!" class =" cursor-pointer" @mouseenter =" showDelete = file.url || ''" @mouseleave =" showDelete = ''" >
100- <div class =" flex-between gap-2" >
101- <div class =" flex-align-center min-w-0 gap-1" >
102- <img :src =" getFileIcon(file.name)" alt =" " class =" w-6 shrink-0" />
103- <span class =" truncate" :title =" file.name" >{{ file.name }}</span >
104- </div >
105- <!-- 移除音频附件 -->
106- <button v-if =" showDelete === file.url" type =" button" class =" attachment-remove" @click =" removeFile(index)" >
107- <MkIcon :icon =" CircleCloseFilled " />
108- </button >
109- </div >
110- </el-card >
111- </el-col >
112- </template >
113- </el-row >
114-
115- <!-- 视频预览 -->
116- <el-space v-if =" videoFiles.length" wrap >
117- <template v-for =" (file , index ) in videoFiles " :key =" file .uid " >
118- <div class =" relative" @mouseenter =" showDelete = file.url || ''" @mouseleave =" showDelete = ''" >
119- <!-- 移除视频附件 -->
120- <button
121- v-if =" showDelete === file.url"
122- type =" button"
123- class =" attachment-remove absolute -top-[5px] -right-[5px] z-1"
124- @click =" removeFile(index)"
125- >
126- <MkIcon :icon =" CircleCloseFilled " />
127- </button >
128- <video v-if =" file.url" :src =" file.url" controls class =" block w-25 rounded-md" autoplay />
73+ </div >
74+ </el-card >
75+ <!-- 移除附件 -->
76+ <div class =" group-hover-visible attachment-remove" @click =" removeFile(index)" >
77+ <MkIcon name="icon_close_bold_outlined" class="text-white!" :size =" 6 " />
12978 </div >
130- </template >
131- </el-space >
132- </div >
79+ </div >
80+ </template >
81+ </el-scrollbar >
13382
13483 <!-- 输入框 -->
13584 <el-input
13685 v-model =" question"
137- :autosize =" { minRows: 1, maxRows: 10 }"
86+ :autosize =" { minRows: 1, maxRows: 7 }"
13887 type =" textarea"
13988 resize =" none"
14089 :placeholder =" placeholder"
@@ -145,45 +94,35 @@ const handleKeydown = (e: KeyboardEvent) => {
14594 />
14695
14796 <!-- 操作栏 -->
148- <div class =" mt-2 flex-align-center justify-end " >
97+ <div class =" text-right mt-3 " >
14998 <input ref =" fileInputRef" type =" file" multiple :accept =" acceptList" class =" hidden" @change =" handleFileSelect" />
150- <MkTooltip placement="top" popper-class="max-w-75" >
99+ <MkTooltip placement="top">
151100 <template #content >
152- <div class =" break-all whitespace-pre-wrap" >支持上传图片、文档、音频、视频文件,最多{{ maxFiles }}个,单个文件最大{{ maxSizeMB }}MB</div >
101+ <div class =" break-all whitespace-pre-wrap" >
102+ 可拖拽到输入框内上传文件
103+ <br />
104+ 上传文件:最多{{ maxFiles }}个,每个文件限制{{ maxSizeMB }}MB
105+ <br />
106+ <!-- // TODO: 类型格式处理 -->
107+ 文件类型:{{ acceptList }}
108+ </div >
153109 </template >
154110 <!-- 上传文件 -->
155111 <el-button text :disabled =" loading || fileList.length >= maxFiles" @click =" fileInputRef?.click()" >
156- <MkIcon : icon = " Paperclip " :size =" 20 " />
112+ <MkIcon name="icon_attachment_outlined " :size =" 18 " class="text-N900! " />
157113 </el-button >
158114 </MkTooltip >
159- <el-divider direction =" vertical" />
160- <!-- 发送 -->
161- <el-button v-if =" !loading" text :disabled =" !canSend" @click =" send" >
162- <MkIcon :icon =" Promotion " :size =" 20 " />
163- </el-button >
115+ <el-divider direction =" vertical" class =" ml-3! mr-4!" />
164116 <!-- 停止回复 -->
165- <el-button v-else text @click =" stop" >
166- <MkIcon :icon =" VideoPause " :size =" 20 " />
117+ <el-button v-if =" loading" circle type =" primary" @click =" stop" >
118+ <MkIcon name="icon_square_filled" />
119+ </el-button >
120+ <!-- 发送 -->
121+ <el-button v-else circle type =" primary" :disabled =" !canSend" @click =" send()" >
122+ <MkIcon name="icon_arrow-up_outlined" />
167123 </el-button >
168124 </div >
169125 </div >
170126</template >
171127
172- <style scoped lang="scss">
173- /* 附件删除按钮,供图片、文档、音频和视频预览复用。 */
174- .attachment-remove {
175- align-items : center ;
176- background : transparent ;
177- border : 0 ;
178- color : var (--mk-N600 );
179- cursor : pointer ;
180- display : flex ;
181- flex-shrink : 0 ;
182- justify-content : center ;
183- padding : 0 ;
184-
185- & :hover {
186- color : var (--mk-danger );
187- }
188- }
189- </style >
128+ <style scoped lang="scss"></style >
0 commit comments