Skip to content

Commit cdf1fd9

Browse files
committed
bizybot增加查看和下载图片功能
1 parent e8a0033 commit cdf1fd9

3 files changed

Lines changed: 235 additions & 24 deletions

File tree

src/components/assistant/Sidebar.vue

Lines changed: 202 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,30 @@
4545

4646
<!-- 图片消息 -->
4747
<div v-if="message.hasImage" class="message-image">
48-
<img
49-
:src="message.image"
50-
alt="用户上传图片"
51-
@click="message.image && selectExistingImage(message.image)"
52-
class="clickable-image"
53-
/>
48+
<div class="image-container">
49+
<img
50+
:src="message.image"
51+
alt="用户上传图片"
52+
class="message-img"
53+
/>
54+
<div class="image-overlay">
55+
<button class="image-action-btn expand-btn" @click="expandImage(message.image)" title="查看大图">
56+
<svg t="1752053278648" fill="white" width="24" height="24" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2363" ><path d="M919.920093 725.414549q3.014188 26.122962 7.033105 58.776664t7.53547 66.814498 7.53547 67.819227 7.033105 60.786122q6.028376 47.222277-41.193901 44.208089-25.118232-2.009459-56.767205-5.526011t-64.805039-7.53547-65.809769-8.037834-59.781393-7.033105q-29.137149-3.014188-37.174984-16.578033t9.042564-30.644243q11.052022-10.047293 27.127691-27.630056t27.127691-28.634785q11.052022-12.056752 7.033105-22.104044t-16.075669-23.108774q-28.13242-27.127691-51.241194-49.231735t-51.241194-51.241194q-6.028376-6.028376-12.056752-13.061481t-9.042564-15.573304-1.004729-18.085127 13.061481-20.59695q6.028376-6.028376 10.047293-10.549658t8.037834-8.037834 8.540199-8.037834 11.554387-12.559116q20.094586-20.094586 37.174984-17.080398t37.174984 23.108774 41.193901 40.691536 47.222277 46.719912q19.089857 18.085127 32.653702 25.118232t26.625326-6.028376q9.042564-9.042564 22.606409-21.60168t23.611138-22.606409q17.080398-17.080398 30.644243-13.061481t16.578033 30.141879zM43.79615 383.80659q-3.014188-26.122962-7.033105-58.776664t-7.53547-66.814498-7.53547-67.819227-7.033105-60.786122q-3.014188-26.122962 6.53074-36.170255t33.658431-8.037834q25.118232 2.009459 56.767205 5.526011t64.805039 7.53547 65.809769 8.037834 59.781393 7.033105q30.141879 3.014188 37.677348 16.578033t-9.544928 30.644243q-10.047293 10.047293-24.615868 26.122962t-25.620597 27.127691q-12.056752 12.056752-8.037834 22.104044t17.080398 23.108774q13.061481 14.06621 24.615868 24.615868t22.606409 21.099315 23.108774 22.606409l25.118232 25.118232q6.028376 6.028376 11.554387 14.06621t8.037834 17.080398-0.502365 19.089857-13.061481 20.094586l-11.052022 11.052022q-4.018917 4.018917-7.53547 8.037834t-8.540199 8.037834l-11.052022 12.056752q-20.094586 20.094586-34.663161 15.070939t-34.663161-25.118232-38.179713-37.677348-44.208089-43.705724q-18.085127-18.085127-32.151337-25.118232t-27.127691 6.028376q-9.042564 10.047293-25.118232 24.615868t-26.122962 24.615868q-17.080398 17.080398-30.141879 13.061481t-16.075669-30.141879zM905.853883 84.397261q26.122962-3.014188 36.170255 6.53074t8.037834 34.663161-5.526011 56.767205-7.53547 64.805039-8.037834 65.809769-7.033105 59.781393q-3.014188 29.137149-16.578033 37.174984t-30.644243-10.047293q-10.047293-10.047293-26.122962-24.615868t-27.127691-25.620597q-12.056752-11.052022-22.104044-7.53547t-23.108774 16.578033q-27.127691 27.127691-47.724641 49.231735t-48.729371 50.236465q-6.028376 6.028376-14.06621 11.554387t-17.080398 8.037834-19.089857-0.502365-20.094586-14.06621q-6.028376-6.028376-10.549658-10.047293t-8.540199-8.037834-8.540199-8.037834-11.554387-12.056752q-20.094586-20.094586-16.075669-35.165525t25.118232-35.165525l38.179713-40.189172q19.089857-20.094586 45.212818-46.217547 19.089857-18.085127 26.122962-32.151337t-7.033105-26.122962q-9.042564-9.042564-23.108774-24.615868t-24.113503-25.620597q-17.080398-17.080398-13.061481-30.141879t30.141879-16.075669 58.776664-7.033105 67.316863-7.53547 67.819227-7.53547 60.283758-7.033105zM350.238584 640.012559q6.028376 6.028376 10.549658 10.047293t8.540199 8.037834l8.037834 9.042564 12.056752 11.052022q20.094586 20.094586 17.582763 36.672619t-23.611138 37.677348q-19.089857 19.089857-40.189172 40.691536t-47.222277 47.724641q-18.085127 18.085127-22.606409 29.639514t8.540199 24.615868q10.047293 9.042564 22.606409 22.606409t22.606409 23.611138q17.080398 17.080398 12.559116 30.141879t-30.644243 16.075669-58.274299 7.033105-66.814498 8.037834-68.321592 8.037834-60.786122 7.033105q-25.118232 2.009459-35.66789-7.53547t-8.540199-33.658431q2.009459-25.118232 5.526011-56.767205t7.53547-64.805039 8.037834-65.809769 7.033105-59.781393q3.014188-30.141879 16.578033-37.677348t30.644243 9.544928q10.047293 10.047293 27.630056 26.122962t28.634785 27.127691q12.056752 12.056752 20.094586 10.549658t20.094586-14.568575q13.061481-13.061481 25.118232-25.620597t24.113503-24.615868 24.615868-25.118232 26.625326-27.127691q6.028376-6.028376 13.061481-12.056752t15.573304-9.042564 18.085127-0.502365 20.59695 13.563845z" p-id="2364"></path></svg>
57+
</button>
58+
<div class="top-right-actions">
59+
<button class="image-action-btn" @click="selectExistingImage(message.image||'')" title="添加到输入">
60+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
61+
<path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
62+
</svg>
63+
</button>
64+
<button class="image-action-btn" @click="downloadImage(message.image||'')" title="下载图片">
65+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
66+
<path fill="currentColor" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
67+
</svg>
68+
</button>
69+
</div>
70+
</div>
71+
</div>
5472
<div
5573
v-if="
5674
message.role === 'assistant' &&
@@ -163,6 +181,14 @@
163181
</div>
164182
</div>
165183
</div>
184+
185+
<!-- 图片查看弹窗 -->
186+
<div class="image-modal" v-if="showImageModal" @click="closeImageModal">
187+
<div class="modal-content" @click.stop>
188+
<img :src="modalImageSrc" alt="大图查看" class="modal-image" />
189+
<button class="modal-close-btn" @click="closeImageModal">×</button>
190+
</div>
191+
</div>
166192
</Teleport>
167193
</template>
168194

@@ -178,7 +204,7 @@
178204
import { useI18n } from 'vue-i18n'
179205
import { useToaster } from '@/components/modules/toats/index'
180206
import { v4 as uuidv4 } from 'uuid'
181-
207+
import {downloadImage} from '@/utils/tool'
182208
const { t } = useI18n()
183209
const sidebarStore = useSidebarStore()
184210
@@ -801,6 +827,24 @@
801827
}, 0)
802828
}
803829
830+
// 图片弹窗相关状态
831+
const showImageModal = ref(false)
832+
const modalImageSrc = ref('')
833+
834+
// 放大查看图片
835+
const expandImage = (imageSrc: string | undefined) => {
836+
if (!imageSrc) return
837+
modalImageSrc.value = imageSrc
838+
showImageModal.value = true
839+
}
840+
841+
// 关闭图片弹窗
842+
const closeImageModal = () => {
843+
showImageModal.value = false
844+
}
845+
846+
847+
804848
onMounted(() => {
805849
// 从本地存储加载宽度设置
806850
const savedWidth = localStorage.getItem('bizyair-sidebar-width')
@@ -1128,11 +1172,143 @@
11281172
.message-image {
11291173
margin-bottom: 8px;
11301174
}
1131-
1132-
.message-image img {
1175+
1176+
.image-container {
1177+
position: relative;
1178+
display: inline-block;
1179+
overflow: hidden;
1180+
border-radius: 6px;
1181+
}
1182+
1183+
.message-img {
11331184
max-width: 100%;
11341185
max-height: 200px;
11351186
border-radius: 6px;
1187+
display: block;
1188+
}
1189+
1190+
.image-overlay {
1191+
position: absolute;
1192+
top: 0;
1193+
left: 0;
1194+
width: 100%;
1195+
height: 100%;
1196+
background: rgba(0, 0, 0, 0.5);
1197+
display: flex;
1198+
align-items: center;
1199+
justify-content: center;
1200+
opacity: 0;
1201+
transition: opacity 0.2s ease;
1202+
}
1203+
1204+
.image-container:hover .image-overlay {
1205+
opacity: 1;
1206+
}
1207+
1208+
.image-action-btn {
1209+
background: rgba(0, 0, 0, 0.6);
1210+
border: none;
1211+
color: white;
1212+
width: 36px;
1213+
height: 36px;
1214+
border-radius: 50%;
1215+
display: flex;
1216+
align-items: center;
1217+
justify-content: center;
1218+
cursor: pointer;
1219+
transition: background-color 0.2s;
1220+
margin: 0 4px;
1221+
}
1222+
1223+
.image-action-btn:hover {
1224+
background: rgba(0, 0, 0, 0.8);
1225+
}
1226+
1227+
.expand-btn {
1228+
width: 48px;
1229+
height: 48px;
1230+
}
1231+
1232+
.top-right-actions {
1233+
position: absolute;
1234+
top: 8px;
1235+
right: 8px;
1236+
display: flex;
1237+
gap: 8px;
1238+
}
1239+
1240+
/* 弹窗样式 */
1241+
.image-modal {
1242+
position: fixed;
1243+
top: 0;
1244+
left: 0;
1245+
width: 100%;
1246+
height: 100%;
1247+
background: rgba(0, 0, 0, 0.8);
1248+
display: flex;
1249+
align-items: center;
1250+
justify-content: center;
1251+
z-index: 100000;
1252+
}
1253+
1254+
.modal-content {
1255+
position: relative;
1256+
max-width: 90%;
1257+
max-height: 90%;
1258+
}
1259+
1260+
.modal-image {
1261+
max-width: 100%;
1262+
max-height: 90vh;
1263+
border-radius: 8px;
1264+
}
1265+
1266+
.modal-close-btn {
1267+
position: absolute;
1268+
top: -20px;
1269+
right: -20px;
1270+
background: rgba(0, 0, 0, 0.7);
1271+
color: white;
1272+
border: none;
1273+
width: 32px;
1274+
height: 32px;
1275+
border-radius: 50%;
1276+
font-size: 18px;
1277+
cursor: pointer;
1278+
display: flex;
1279+
align-items: center;
1280+
justify-content: center;
1281+
}
1282+
1283+
.clickable-image {
1284+
cursor: pointer;
1285+
transition:
1286+
transform 0.2s,
1287+
box-shadow 0.2s;
1288+
}
1289+
1290+
.clickable-image:hover {
1291+
transform: scale(1.02);
1292+
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
1293+
}
1294+
1295+
.clickable-image::after {
1296+
content: '点击复用此图片';
1297+
position: absolute;
1298+
bottom: 8px;
1299+
left: 50%;
1300+
transform: translateX(-50%);
1301+
background-color: rgba(0, 0, 0, 0.7);
1302+
color: white;
1303+
padding: 4px 8px;
1304+
border-radius: 4px;
1305+
font-size: 12px;
1306+
opacity: 0;
1307+
transition: opacity 0.2s;
1308+
pointer-events: none;
1309+
}
1310+
.clickable-image:hover::after {
1311+
opacity: 1;
11361312
}
11371313
11381314
.chat-input-area {
@@ -1149,16 +1325,21 @@
11491325
display: flex;
11501326
align-items: center;
11511327
gap: 8px;
1328+
width: 100%;
1329+
margin-top: 8px;
11521330
}
11531331
11541332
.textarea-container {
11551333
flex: 1;
11561334
position: relative;
1335+
height: 40px;
1336+
display: flex;
1337+
align-items: center;
11571338
}
11581339
11591340
.textarea-container textarea {
11601341
width: 100%;
1161-
padding: 10px 12px;
1342+
padding: 8px 12px;
11621343
border-radius: 18px;
11631344
background-color: #444;
11641345
border: 1px solid #555;
@@ -1168,23 +1349,28 @@
11681349
line-height: 20px;
11691350
outline: none;
11701351
transition: border-color 0.2s;
1352+
box-sizing: border-box;
11711353
}
11721354
11731355
.textarea-container textarea:focus {
11741356
border-color: #7c3aed;
11751357
}
11761358
1177-
.upload-image-btn {
1178-
width: 40px;
1359+
.upload-image-btn, .send-message-btn, .control-btn {
1360+
min-width: 40px;
11791361
height: 40px;
11801362
border-radius: 50%;
11811363
display: flex;
11821364
justify-content: center;
11831365
align-items: center;
11841366
cursor: pointer;
1185-
background-color: #444;
11861367
border: none;
11871368
color: white;
1369+
flex-shrink: 0;
1370+
}
1371+
1372+
.upload-image-btn {
1373+
background-color: #444;
11881374
transition: background-color 0.2s;
11891375
}
11901376
@@ -1199,16 +1385,7 @@
11991385
}
12001386
12011387
.send-message-btn {
1202-
width: 40px;
1203-
height: 40px;
1204-
border-radius: 50%;
1205-
display: flex;
1206-
justify-content: center;
1207-
align-items: center;
1208-
cursor: pointer;
12091388
background-color: #7c3aed;
1210-
border: none;
1211-
color: white;
12121389
transition: background-color 0.2s;
12131390
}
12141391
@@ -1244,8 +1421,10 @@
12441421
}
12451422
12461423
.image-preview-area {
1247-
margin-bottom: 12px;
1424+
margin-bottom: 8px;
12481425
flex-shrink: 0;
1426+
display: flex;
1427+
align-items: center;
12491428
}
12501429
12511430
.preview-image-container {

src/utils/tool.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,35 @@ export const getDictData = <K extends keyof CommonDict>(
128128
export const setDictData = (key: string, data: CommonDict, expires?: number): void => {
129129
setLocalStorage<CommonDict>(key, data, expires)
130130
}
131+
132+
/**
133+
* 下载图片工具函数
134+
* @param imageSrc 图片源URL或base64
135+
* @param fileName 可选的文件名,默认使用时间戳
136+
* @returns Promise<boolean> 下载是否成功
137+
*/
138+
export const downloadImage = async (imageSrc: string, fileName?: string): Promise<boolean> => {
139+
if (!imageSrc) return false
140+
// 创建一个临时链接
141+
const a = document.createElement('a')
142+
143+
// 如果是base64格式
144+
if (imageSrc.startsWith('data:')) {
145+
a.href = imageSrc
146+
} else {
147+
// 如果是URL,需要先获取图片内容
148+
const response = await fetch(imageSrc)
149+
const blob = await response.blob()
150+
a.href = URL.createObjectURL(blob)
151+
}
152+
153+
// 设置下载文件名
154+
a.download = fileName || `bizyair-image-${Date.now()}.png`
155+
156+
// 触发点击下载
157+
document.body.appendChild(a)
158+
a.click()
159+
document.body.removeChild(a)
160+
161+
return true
162+
}

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.2.26
1+
1.2.27

0 commit comments

Comments
 (0)