159 lines
4.9 KiB
Vue
159 lines
4.9 KiB
Vue
<template>
|
|
<div>
|
|
<transition appear mode="out-in" name="fade">
|
|
<Skeleton v-if="!loaded" />
|
|
<template v-else>
|
|
<component :is="openable ? 'a' : 'div'" :href="url" target="_blank">
|
|
<svg
|
|
v-if="error"
|
|
class="error-image"
|
|
viewBox="0 0 280 162"
|
|
x="0px"
|
|
xml:space="preserve"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
y="0px"
|
|
>
|
|
<rect height="162" width="280" />
|
|
<path
|
|
d="M140,30.59c-27.85,0-50.41,22.56-50.41,50.41s22.56,50.41,50.41,50.41s50.41-22.56,50.41-50.41
|
|
S167.85,30.59,140,30.59z M140,121.65c-22.42,0-40.65-18.23-40.65-40.65S117.58,40.35,140,40.35S180.65,58.58,180.65,81
|
|
S162.42,121.65,140,121.65z M123.74,77.75c3.6,0,6.5-2.91,6.5-6.5s-2.91-6.5-6.5-6.5s-6.5,2.91-6.5,6.5S120.14,77.75,123.74,77.75z
|
|
M156.26,64.74c-3.6,0-6.5,2.91-6.5,6.5s2.91,6.5,6.5,6.5c3.6,0,6.5-2.91,6.5-6.5S159.86,64.74,156.26,64.74z M140,90.76
|
|
c-8.17,0-15.85,3.6-21.1,9.88c-1.73,2.07-1.44,5.14,0.63,6.87c2.07,1.71,5.14,1.44,6.87-0.63c3.37-4.05,8.33-6.38,13.6-6.38
|
|
s10.22,2.32,13.6,6.38c1.65,1.97,4.7,2.42,6.87,0.63c2.07-1.73,2.34-4.8,0.63-6.87C155.85,94.35,148.17,90.76,140,90.76z"
|
|
/>
|
|
</svg>
|
|
<lazy-component v-else-if="lazy">
|
|
<img :src="url" alt="screenshot" @click="$emit('click', $event)" @error="handleError" />
|
|
</lazy-component>
|
|
<img v-else :src="url" alt="screenshot" @click="$emit('click', $event)" @error="handleError" />
|
|
</component>
|
|
</template>
|
|
</transition>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import axios from '@/config/app';
|
|
import { Skeleton } from 'vue-loading-skeleton';
|
|
|
|
export default {
|
|
name: 'AppImage',
|
|
props: {
|
|
src: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
lazy: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
openable: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
data() {
|
|
const baseUrl =
|
|
this.src.indexOf('http') === 0
|
|
? ''
|
|
: (process.env.VUE_APP_API_URL !== 'null'
|
|
? process.env.VUE_APP_API_URL
|
|
: `${window.location.origin}/api`) + '/';
|
|
|
|
const url = baseUrl + this.src;
|
|
|
|
return {
|
|
error: this.src === 'none',
|
|
loaded: this.src === 'none',
|
|
url,
|
|
baseUrl,
|
|
};
|
|
},
|
|
components: {
|
|
Skeleton,
|
|
},
|
|
methods: {
|
|
load() {
|
|
if (this.error) return;
|
|
|
|
if (this.src === 'none') {
|
|
this.error = true;
|
|
return;
|
|
}
|
|
|
|
this.loaded = false;
|
|
|
|
if (this.url) {
|
|
URL.revokeObjectURL(this.url);
|
|
this.url = null;
|
|
}
|
|
|
|
if (this.src) {
|
|
axios
|
|
.get(this.src, {
|
|
responseType: 'blob',
|
|
muteError: true,
|
|
})
|
|
.then(({ data }) => {
|
|
this.url = URL.createObjectURL(data);
|
|
})
|
|
.catch(() => {
|
|
this.error = true;
|
|
})
|
|
.finally(() => {
|
|
this.loaded = true;
|
|
});
|
|
}
|
|
},
|
|
handleError() {
|
|
this.error = true;
|
|
},
|
|
},
|
|
mounted() {
|
|
this.load();
|
|
},
|
|
beforeDestroy() {
|
|
if (this.url) {
|
|
URL.revokeObjectURL(this.url);
|
|
this.url = null;
|
|
}
|
|
},
|
|
watch: {
|
|
src() {
|
|
this.error = false;
|
|
this.load();
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
img {
|
|
width: 100%;
|
|
object-fit: cover;
|
|
background-color: $gray-5;
|
|
}
|
|
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.4s;
|
|
}
|
|
|
|
.fade-enter,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
.error-image {
|
|
rect {
|
|
fill: $gray-4;
|
|
}
|
|
|
|
path {
|
|
fill: $red-1;
|
|
}
|
|
}
|
|
</style>
|