feat(ChatPage): 添加图像预览

This commit is contained in:
IGCrystal
2025-07-05 11:15:54 +08:00
committed by Soulter
parent 008e49d144
commit 3bb01fa52c
3 changed files with 83 additions and 3 deletions
@@ -33,6 +33,7 @@
"longPress": "Long press",
"yes": "Yes",
"no": "No",
"imagePreview": "Image Preview",
"dialog": {
"confirmTitle": "Confirm Action",
"confirmMessage": "Are you sure you want to perform this action?",
@@ -33,6 +33,7 @@
"longPress": "长按",
"yes": "是",
"no": "否",
"imagePreview": "图片预览",
"dialog": {
"confirmTitle": "确认操作",
"confirmMessage": "你确定要执行此操作吗?",
+81 -3
View File
@@ -157,7 +157,7 @@
<div class="image-attachments" v-if="msg.image_url && msg.image_url.length > 0">
<div v-for="(img, index) in msg.image_url" :key="index"
class="image-attachment">
<img :src="img" class="attached-image" />
<img :src="img" class="attached-image" @click="openImagePreview(img)" />
</div>
</div>
@@ -257,6 +257,19 @@
</v-card-actions>
</v-card>
</v-dialog>
<!-- 图片预览对话框 -->
<v-dialog v-model="imagePreviewDialog" max-width="90vw" max-height="90vh">
<v-card class="image-preview-card" elevation="8">
<v-card-title class="d-flex justify-space-between align-center pa-4">
<span>{{ t('core.common.imagePreview') }}</span>
<v-btn icon="mdi-close" variant="text" @click="imagePreviewDialog = false" />
</v-card-title>
<v-card-text class="text-center pa-4">
<img :src="previewImageUrl" class="preview-image-large" />
</v-card-text>
</v-card>
</v-dialog>
</template>
<script>
@@ -358,6 +371,10 @@ export default {
copySuccessMessage: null,
copySuccessTimeout: null,
copiedMessages: new Set(), // 存储已复制的消息索引
// 图片预览相关变量
imagePreviewDialog: false,
previewImageUrl: ''
}
},
@@ -563,6 +580,25 @@ export default {
this.stagedAudioUrl = null;
},
openImagePreview(imageUrl) {
this.previewImageUrl = imageUrl;
this.imagePreviewDialog = true;
},
initImageClickEvents() {
this.$nextTick(() => {
// 查找所有动态生成的图片(在markdown-content中)
const images = document.querySelectorAll('.markdown-content img');
images.forEach((img) => {
if (!img.hasAttribute('data-click-enabled')) {
img.style.cursor = 'pointer';
img.setAttribute('data-click-enabled', 'true');
img.onclick = () => this.openImagePreview(img.src);
}
});
});
},
checkStatus() {
axios.get('/api/chat/status').then(response => {
console.log(response.data);
@@ -709,6 +745,7 @@ export default {
}
this.messages = message;
this.initCodeCopyButtons();
this.initImageClickEvents();
}).catch(err => {
console.error(err);
});
@@ -927,8 +964,9 @@ export default {
}
} else if (chunk_json.type === 'end') {
in_streaming = false;
// 在消息流结束后初始化代码复制按钮
// 在消息流结束后初始化代码复制按钮和图片点击事件
this.initCodeCopyButtons();
this.initImageClickEvents();
continue;
} else if (chunk_json.type === 'update_title') {
// 更新对话标题
@@ -968,8 +1006,9 @@ export default {
this.$nextTick(() => {
const container = this.$refs.messageContainer;
container.scrollTop = container.scrollHeight;
// 在滚动后初始化代码复制按钮
// 在滚动后初始化代码复制按钮和图片点击事件
this.initCodeCopyButtons();
this.initImageClickEvents();
});
},
handleInputKeyDown(e) {
@@ -1522,6 +1561,45 @@ export default {
.attached-image:hover {
transform: scale(1.02);
cursor: pointer;
}
/* 图片预览对话框样式 */
.image-preview-card {
background-color: var(--v-theme-surface) !important;
border: 1px solid var(--v-theme-border);
}
/* 亮色主题下的图片预览对话框 */
.v-theme--light .image-preview-card,
.v-theme--PurpleTheme .image-preview-card {
background-color: #ffffff !important;
border-color: #e0e0e0 !important;
}
/* 暗色主题下的图片预览对话框 */
.v-theme--dark .image-preview-card,
.v-theme--PurpleThemeDark .image-preview-card {
background-color: #1e1e1e !important;
border-color: #333333 !important;
}
/* 确保对话框标题栏和内容区域的背景色 */
.image-preview-card .v-card-title {
background-color: inherit;
}
.image-preview-card .v-card-text {
background-color: inherit;
}
.preview-image-large {
max-width: 100%;
max-height: 75vh;
width: auto;
height: auto;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.audio-attachment {