mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-22 16:23:50 +02:00
refactor: replace FullscreenImageDialog with FullscreenImagePreview component
This commit is contained in:
@@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
<GroupMemberModerationDialog></GroupMemberModerationDialog>
|
<GroupMemberModerationDialog></GroupMemberModerationDialog>
|
||||||
|
|
||||||
<FullscreenImageDialog></FullscreenImageDialog>
|
<FullscreenImagePreview></FullscreenImagePreview>
|
||||||
|
|
||||||
<PreviousInstancesInfoDialog></PreviousInstancesInfoDialog>
|
<PreviousInstancesInfoDialog></PreviousInstancesInfoDialog>
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
import AvatarDialog from './components/dialogs/AvatarDialog/AvatarDialog.vue';
|
import AvatarDialog from './components/dialogs/AvatarDialog/AvatarDialog.vue';
|
||||||
import GroupDialog from './components/dialogs/GroupDialog/GroupDialog.vue';
|
import GroupDialog from './components/dialogs/GroupDialog/GroupDialog.vue';
|
||||||
import GroupMemberModerationDialog from './components/dialogs/GroupDialog/GroupMemberModerationDialog.vue';
|
import GroupMemberModerationDialog from './components/dialogs/GroupDialog/GroupMemberModerationDialog.vue';
|
||||||
import FullscreenImageDialog from './components/dialogs/FullscreenImageDialog.vue';
|
import FullscreenImagePreview from './components/FullscreenImagePreview.vue';
|
||||||
import PreviousInstancesInfoDialog from './components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue';
|
import PreviousInstancesInfoDialog from './components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue';
|
||||||
import LaunchDialog from './components/dialogs/LaunchDialog.vue';
|
import LaunchDialog from './components/dialogs/LaunchDialog.vue';
|
||||||
import LaunchOptionsDialog from './views/Settings/dialogs/LaunchOptionsDialog.vue';
|
import LaunchOptionsDialog from './views/Settings/dialogs/LaunchOptionsDialog.vue';
|
||||||
|
|||||||
196
src/components/FullscreenImagePreview.vue
Normal file
196
src/components/FullscreenImagePreview.vue
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="fullscreenImageDialog.visible"
|
||||||
|
class="fullscreen-image-overlay"
|
||||||
|
:style="{ zIndex: overlayZIndex }"
|
||||||
|
@click.self="closeDialog">
|
||||||
|
<el-image
|
||||||
|
v-if="fullscreenImageDialog.imageUrl"
|
||||||
|
ref="imageRef"
|
||||||
|
class="fullscreen-image"
|
||||||
|
:src="fullscreenImageDialog.imageUrl"
|
||||||
|
:preview-src-list="[fullscreenImageDialog.imageUrl]"
|
||||||
|
:z-index="100000"
|
||||||
|
fit="contain"
|
||||||
|
preview-teleported
|
||||||
|
hide-on-click-modal
|
||||||
|
:initial-index="0"
|
||||||
|
@load="handleImageLoad"
|
||||||
|
@close="closeDialog">
|
||||||
|
<template #toolbar="{ actions }">
|
||||||
|
<el-icon @click="copyImageToClipboard(fullscreenImageDialog.imageUrl)" class="toolbar-icon">
|
||||||
|
<CopyDocument />
|
||||||
|
</el-icon>
|
||||||
|
<el-icon
|
||||||
|
@click="downloadAndSaveImage(fullscreenImageDialog.imageUrl, fullscreenImageDialog.fileName)"
|
||||||
|
class="toolbar-icon">
|
||||||
|
<Download />
|
||||||
|
</el-icon>
|
||||||
|
<el-icon @click="actions('zoomOut')" class="toolbar-icon"><ZoomOut /></el-icon>
|
||||||
|
<el-icon @click="actions('zoomIn')" class="toolbar-icon"><ZoomIn /></el-icon>
|
||||||
|
<el-icon @click="actions('clockwise')" class="toolbar-icon"><RefreshRight /></el-icon>
|
||||||
|
<el-icon @click="actions('anticlockwise')" class="toolbar-icon"><RefreshLeft /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { Download, CopyDocument, ZoomIn, ZoomOut, RefreshRight, RefreshLeft } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import Noty from 'noty';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { escapeTag, extractFileId } from '../shared/utils';
|
||||||
|
import { useGalleryStore } from '../stores';
|
||||||
|
|
||||||
|
import { ref, nextTick, watch } from 'vue';
|
||||||
|
|
||||||
|
import { getNextDialogIndex } from '../shared/utils/base/ui';
|
||||||
|
|
||||||
|
const galleryStore = useGalleryStore();
|
||||||
|
const { fullscreenImageDialog } = storeToRefs(galleryStore);
|
||||||
|
|
||||||
|
const imageRef = ref();
|
||||||
|
const overlayZIndex = ref(4000);
|
||||||
|
|
||||||
|
function showPreview() {
|
||||||
|
nextTick(() => {
|
||||||
|
imageRef.value?.showPreview?.();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleImageLoad() {
|
||||||
|
showPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => fullscreenImageDialog.value.visible,
|
||||||
|
(visible) => {
|
||||||
|
if (!visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
overlayZIndex.value = Math.max(getNextDialogIndex(), 4000);
|
||||||
|
showPreview();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => fullscreenImageDialog.value.imageUrl,
|
||||||
|
(url) => {
|
||||||
|
if (!url || !fullscreenImageDialog.value.visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showPreview();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function closeDialog() {
|
||||||
|
fullscreenImageDialog.value.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyImageToClipboard(url) {
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const msg = ElMessage({
|
||||||
|
message: 'Downloading image...',
|
||||||
|
type: 'info'
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const response = await webApiService.execute({
|
||||||
|
url,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
if (response.status !== 200 || !response.data.startsWith('data:image/png')) {
|
||||||
|
throw new Error(`Error: ${response.data}`);
|
||||||
|
}
|
||||||
|
await navigator.clipboard.write([
|
||||||
|
new ClipboardItem({
|
||||||
|
'image/png': await (await fetch(response.data)).blob()
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
ElMessage({
|
||||||
|
message: 'Image copied to clipboard',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading image:', error);
|
||||||
|
new Noty({
|
||||||
|
type: 'error',
|
||||||
|
text: escapeTag(`Failed to download image. ${url}`)
|
||||||
|
}).show();
|
||||||
|
} finally {
|
||||||
|
msg.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadAndSaveImage(url, fileName) {
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const msg = ElMessage({
|
||||||
|
message: 'Downloading image...',
|
||||||
|
type: 'info'
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const response = await webApiService.execute({
|
||||||
|
url,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
if (response.status !== 200 || !response.data.startsWith('data:image/png')) {
|
||||||
|
throw new Error(`Error: ${response.data}`);
|
||||||
|
}
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = response.data;
|
||||||
|
const fileId = extractFileId(url);
|
||||||
|
if (!fileName && fileId) {
|
||||||
|
fileName = `${fileId}.png`;
|
||||||
|
}
|
||||||
|
if (!fileName) {
|
||||||
|
fileName = `${url.split('/').pop()}.png`;
|
||||||
|
}
|
||||||
|
if (!fileName) {
|
||||||
|
fileName = 'image.png';
|
||||||
|
}
|
||||||
|
link.setAttribute('download', fileName);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading image:', error);
|
||||||
|
new Noty({
|
||||||
|
type: 'error',
|
||||||
|
text: escapeTag(`Failed to download image. ${url}`)
|
||||||
|
}).show();
|
||||||
|
} finally {
|
||||||
|
msg.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.toolbar-icon:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.toolbar-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.fullscreen-image-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.fullscreen-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
:deep(.el-image__preview) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-dialog class="x-dialog" v-model="fullscreenImageDialog.visible" append-to-body width="97vw">
|
|
||||||
<div>
|
|
||||||
<div style="margin: 0 0 5px 5px">
|
|
||||||
<el-tooltip :content="t('dialog.fullscreen_image.copy_image_to_clipboard')" placement="top">
|
|
||||||
<el-button
|
|
||||||
size="small"
|
|
||||||
:icon="CopyDocument"
|
|
||||||
circle
|
|
||||||
@click="copyImageToClipboard(fullscreenImageDialog.imageUrl)"></el-button>
|
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip :content="t('dialog.fullscreen_image.download_and_save_image')" placement="top">
|
|
||||||
<el-button
|
|
||||||
type="default"
|
|
||||||
size="small"
|
|
||||||
:icon="Download"
|
|
||||||
circle
|
|
||||||
style="margin-left: 5px"
|
|
||||||
:disabled="!fullscreenImageDialog.imageUrl.startsWith('http')"
|
|
||||||
@click="
|
|
||||||
downloadAndSaveImage(fullscreenImageDialog.imageUrl, fullscreenImageDialog.fileName)
|
|
||||||
"></el-button>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
<img
|
|
||||||
:src="fullscreenImageDialog.imageUrl"
|
|
||||||
style="width: 100%; height: 85vh; object-fit: contain"
|
|
||||||
loading="lazy" />
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ElMessage } from 'element-plus';
|
|
||||||
import { Download, CopyDocument } from '@element-plus/icons-vue';
|
|
||||||
|
|
||||||
import Noty from 'noty';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { escapeTag, extractFileId } from '../../shared/utils';
|
|
||||||
import { useGalleryStore } from '../../stores';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const { fullscreenImageDialog } = storeToRefs(useGalleryStore());
|
|
||||||
|
|
||||||
async function copyImageToClipboard(url) {
|
|
||||||
if (!url) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const msg = ElMessage({
|
|
||||||
message: 'Downloading image...',
|
|
||||||
type: 'info'
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
const response = await webApiService.execute({
|
|
||||||
url,
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
if (response.status !== 200 || !response.data.startsWith('data:image/png')) {
|
|
||||||
throw new Error(`Error: ${response.data}`);
|
|
||||||
}
|
|
||||||
await navigator.clipboard.write([
|
|
||||||
new ClipboardItem({
|
|
||||||
'image/png': await (await fetch(response.data)).blob()
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
ElMessage({
|
|
||||||
message: 'Image copied to clipboard',
|
|
||||||
type: 'success'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error downloading image:', error);
|
|
||||||
new Noty({
|
|
||||||
type: 'error',
|
|
||||||
text: escapeTag(`Failed to download image. ${url}`)
|
|
||||||
}).show();
|
|
||||||
} finally {
|
|
||||||
msg.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function downloadAndSaveImage(url, fileName) {
|
|
||||||
if (!url) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const msg = ElMessage({
|
|
||||||
message: 'Downloading image...',
|
|
||||||
type: 'info'
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
const response = await webApiService.execute({
|
|
||||||
url,
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
if (response.status !== 200 || !response.data.startsWith('data:image/png')) {
|
|
||||||
throw new Error(`Error: ${response.data}`);
|
|
||||||
}
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = response.data;
|
|
||||||
const fileId = extractFileId(url);
|
|
||||||
if (!fileName && fileId) {
|
|
||||||
fileName = `${fileId}.png`;
|
|
||||||
}
|
|
||||||
if (!fileName) {
|
|
||||||
fileName = `${url.split('/').pop()}.png`;
|
|
||||||
}
|
|
||||||
if (!fileName) {
|
|
||||||
fileName = 'image.png';
|
|
||||||
}
|
|
||||||
link.setAttribute('download', fileName);
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error downloading image:', error);
|
|
||||||
new Noty({
|
|
||||||
type: 'error',
|
|
||||||
text: escapeTag(`Failed to download image. ${url}`)
|
|
||||||
}).show();
|
|
||||||
} finally {
|
|
||||||
msg.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
Reference in New Issue
Block a user