1 parent
4d5e096e11
commit
da9d5e6782
17 files changed
+341
-209
No files matched your search
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PDL L3 - Galerie</title>
|
||||
</head>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#e4e6ea;}.cls-2{fill:#ffb85a;}.cls-3{opacity:0.12;}.cls-4{fill:#2b374f;}</style></defs><title>magic-wand-filled</title><path class="cls-1" d="M20.28,491.72h0a25.6,25.6,0,0,1,0-36.2L288.91,186.88l36.21,36.21L56.48,491.72A25.6,25.6,0,0,1,20.28,491.72Z"/><path class="cls-2" d="M445.95,213.51l-59.07-1.28-33.79,48.13a12.81,12.81,0,0,1-22.66-3.46l-18.18-57.15-57.15-18.18a12.81,12.81,0,0,1-3.46-22.66l48.13-33.79-1.28-59.07A12.82,12.82,0,0,1,319,55.55L366.78,91.4,423.1,72.71A12.78,12.78,0,0,1,439.29,88.9L420.6,145.22,456.45,193A12.82,12.82,0,0,1,445.95,213.51Z"/><g class="cls-3"><path d="M445.95,213.51l-59.07-1.28-33.79,48.13a12.81,12.81,0,0,1-22.66-3.46l-18.18-57.15,124-124a12.75,12.75,0,0,1,3.07,13.12L420.6,145.22,456.45,193A12.82,12.82,0,0,1,445.95,213.51Z"/></g><path class="cls-4" d="M434.87,142.92l16.57-50a25.6,25.6,0,0,0-32.35-32.35l-50,16.57L326.66,45.32a25.6,25.6,0,0,0-41,21l1.12,52.2-42.54,29.9a25.6,25.6,0,0,0,7,45.34l9.62,3.06L11.22,446.46a38.46,38.46,0,0,0,0,54.31h0a38.45,38.45,0,0,0,54.31,0L315.15,251.15l3.06,9.62a25.45,25.45,0,0,0,20.51,17.54,26.06,26.06,0,0,0,4,.3,25.43,25.43,0,0,0,20.88-10.89l29.9-42.54,52.2,1.12a25.6,25.6,0,0,0,21-41ZM47.44,482.67a12.8,12.8,0,1,1-18.11-18.1l259-259,1.8.57,15.74,15.74.57,1.79ZM387.18,199.44a12.81,12.81,0,0,0-10.75,5.44L342.61,253,324.41,195.9a12.81,12.81,0,0,0-8.31-8.31L259,169.39l48.12-33.82a12.8,12.8,0,0,0,5.44-10.75l-1.27-59,47.77,35.83a12.79,12.79,0,0,0,11.71,1.91l56.39-18.77,0,.08-18.68,56.36a12.81,12.81,0,0,0,1.91,11.71l35.84,47.78Z"/><circle class="cls-4" cx="486.4" cy="121.6" r="12.8"/><circle class="cls-4" cx="492.8" cy="19.2" r="19.2"/><circle class="cls-4" cx="364.79" cy="12.8" r="12.8"/><circle class="cls-4" cx="422.4" cy="281.61" r="19.2"/><circle class="cls-4" cx="223.99" cy="89.6" r="12.8"/></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
+102
-51
@@ -1,60 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="app">
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<router-link to="/">Home</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/gallery">Gallery</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/upload">Upload</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div>
|
||||
<h1>PDL - L3</h1>
|
||||
<router-view />
|
||||
</div>
|
||||
<div v-for="route in routes" class="button">
|
||||
<a :href="'#' + (routes[route] || '/')"> {{ route }}</a>
|
||||
</div>
|
||||
<div>
|
||||
<label>Files
|
||||
<input id="fileUpload" type="file" @change="handleFilesUpload($event as InputFileEvent)" />
|
||||
</label>
|
||||
<button :class="sent ? 'green' : 'normal'" @click="submitImage()">{{ sent ? 'Sent' : 'Submit' }}</button>
|
||||
</div>
|
||||
<component :is="currentView" :key="sent" :images="images" @delete="deleteFile" />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
<script setup lang="ts">
|
||||
import Home from './components/Home.vue'
|
||||
import Gallery from './components/Gallery.vue';
|
||||
import NotFound from './components/NotFound.vue'
|
||||
import { defineComponent } from 'vue'
|
||||
import { api } from './http-api'
|
||||
import { ImageType, ImageClass } from './image';
|
||||
</script>
|
||||
<script lang="ts">
|
||||
const routes: { [key: string]: string } = {
|
||||
'/': "Home",
|
||||
'Gallery': "Gallery"
|
||||
}
|
||||
interface InputFileEvent extends Event {
|
||||
target: HTMLInputElement;
|
||||
}
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Home,
|
||||
Gallery,
|
||||
NotFound
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
images: new Map<Number, ImageType>(),
|
||||
currentPath: window.location.hash,
|
||||
sent: false,
|
||||
file: null as FileList | null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentView(): string {
|
||||
return routes[this.currentPath.slice(1) || '/'] || "notFound";
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async submitImage() {
|
||||
if (this.sent == true || this.file == null)
|
||||
return;
|
||||
let formData = new FormData();
|
||||
formData.append('file', this.file[0]);
|
||||
await api.createImage(formData);
|
||||
this.sent = true;
|
||||
(document.getElementById('fileUpload') as HTMLInputElement).value = "";
|
||||
this.updateImageList(100);
|
||||
},
|
||||
handleFilesUpload(event: InputFileEvent) {
|
||||
this.file = event.target.files;
|
||||
this.sent = false;
|
||||
},
|
||||
async updateImageList(sleep: number) {
|
||||
setTimeout(() => {
|
||||
api.getImageList().then((newImages) => {
|
||||
const newImageList = new Map<number, ImageType>();
|
||||
for (const image of newImages.values()) {
|
||||
newImageList.set(image.id, image);
|
||||
}
|
||||
this.images = newImageList; console.log(this.images);
|
||||
})
|
||||
}, sleep);
|
||||
},
|
||||
deleteFile(id: number) {
|
||||
api.deleteImage(id).then(() => {
|
||||
this.updateImageList(100);
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.updateImageList(0);
|
||||
window.addEventListener('hashchange', () => {
|
||||
this.currentPath = window.location.hash;
|
||||
});
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.button {
|
||||
display: inline-block;
|
||||
color: #444;
|
||||
border: 1px solid #CCC;
|
||||
background: #DDD;
|
||||
box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
max-width: 100px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #2c3e50;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
li {
|
||||
float: left;
|
||||
}
|
||||
|
||||
li a {
|
||||
display: block;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 14px 16px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.error {
|
||||
.button:active {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
list-style-type: none;
|
||||
margin-top: 1em;
|
||||
box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.green {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
@@ -1,25 +1,41 @@
|
||||
<template>
|
||||
<div class="flex" v-if="images.keys != null">
|
||||
<Image v-for="[id, image] in images" :key="id" :id="id" />
|
||||
</div>
|
||||
<h1 v-else>No image found</h1>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { api } from '@/http-api';
|
||||
import { defineComponent } from 'vue'
|
||||
import { ImageType } from '@/image'
|
||||
import Image from './Image.vue';
|
||||
import Image from '@/components/Image.vue'
|
||||
</script>
|
||||
<script lang="ts">
|
||||
|
||||
const imageList = ref<ImageType[]>([]);
|
||||
|
||||
api.getImageList()
|
||||
.then((data) => {
|
||||
imageList.value = data;
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
export default defineComponent({
|
||||
props: {
|
||||
images: {
|
||||
type: Map<number, ImageType>,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Gallery</h3>
|
||||
<Image v-for="image in imageList" :id="image.id" />
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.flex {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
<style scoped></style>
|
||||
.flex>img {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 25%;
|
||||
max-height: 25%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,35 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import router from "@/router";
|
||||
import { api } from '@/http-api';
|
||||
import { ImageType } from '@/image'
|
||||
|
||||
const selectedId = ref(-1);
|
||||
const imageList = ref<ImageType[]>([]);
|
||||
getImageList();
|
||||
|
||||
function getImageList() {
|
||||
api.getImageList().then((data) => {
|
||||
imageList.value = data;
|
||||
}).catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
}
|
||||
|
||||
function showImage() {
|
||||
router.push({ name: 'image', params: { id: selectedId.value } })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Choose an image</h3>
|
||||
<div class="flex" v-if="images.keys != null">
|
||||
<div>
|
||||
<select v-model="selectedId" @change="showImage">
|
||||
<option v-for="image in imageList" :value="image.id" :key="image.id">{{ image.name }}</option>
|
||||
<select v-model="imageSelectedId" style="width: min-content;margin: 15px;">
|
||||
<option v-for="[id, image] in images" :key="id" :value="id">{{ image.name }}</option>
|
||||
</select>
|
||||
<button @click="$emit('delete', imageSelectedId)">Delete</button>
|
||||
</div>
|
||||
<Image :id="imageSelectedId"></Image>
|
||||
</div>
|
||||
<h1 v-else>No images found</h1>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Image from '@/components/Image.vue'
|
||||
import { defineComponent } from 'vue'
|
||||
import { ImageType } from '@/image'
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
emits: {
|
||||
delete(id: number) {
|
||||
return id >= 0;
|
||||
}
|
||||
},
|
||||
props: {
|
||||
images: {
|
||||
type: Map<number, ImageType>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imageSelectedId: 0 as number
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.imageSelectedId = this.images.keys().next().value;
|
||||
console.log(this.imageSelectedId);
|
||||
},
|
||||
watch: {
|
||||
images() {
|
||||
this.imageSelectedId = this.images.keys().next().value;
|
||||
console.log(this.imageSelectedId);
|
||||
},
|
||||
imageSelectedId() {
|
||||
console.log(this.imageSelectedId);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.flex {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
<style scoped></style>
|
||||
img {
|
||||
max-width: 90%;
|
||||
max-height: 60%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,31 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import { api } from '@/http-api';
|
||||
|
||||
const props = defineProps<{ id: number }>()
|
||||
|
||||
api.getImage(props.id)
|
||||
.then((data: Blob) => {
|
||||
const reader = new window.FileReader();
|
||||
reader.readAsDataURL(data);
|
||||
reader.onload = () => {
|
||||
const galleryElt = document.getElementById("gallery-" + props.id);
|
||||
if (galleryElt !== null) {
|
||||
const imgElt = document.createElement("img");
|
||||
galleryElt.appendChild(imgElt);
|
||||
if (imgElt !== null && reader.result as string) {
|
||||
imgElt.setAttribute("src", (reader.result as string));
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getImage(id: number) {
|
||||
const data = await api.getImage(id);
|
||||
const reader = new window.FileReader();
|
||||
reader.readAsDataURL(data);
|
||||
reader.onload = () => {
|
||||
const imgElt = document.getElementById("img-" + id) as HTMLImageElement;
|
||||
if (imgElt !== null) {
|
||||
if (reader.result as string) {
|
||||
imgElt.setAttribute("src", (reader.result as string));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getImage(this.id);
|
||||
},
|
||||
watch: {
|
||||
id() {
|
||||
this.getImage(this.id);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<figure :id="'gallery-' + id"></figure>
|
||||
<img :id="'img-' + id" />
|
||||
</template>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>404 NOT FOUND</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,40 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { api } from '@/http-api';
|
||||
|
||||
const target = ref<HTMLInputElement>();
|
||||
|
||||
function submitFile() {
|
||||
if (target.value !== null && target.value !== undefined && target.value.files !== null) {
|
||||
const file = target.value.files[0];
|
||||
if (file === undefined)
|
||||
return;
|
||||
let formData = new FormData();
|
||||
formData.append("file", file);
|
||||
api.createImage(formData).then(() => {
|
||||
if (target.value !== undefined)
|
||||
target.value.value = '';
|
||||
}).catch(e => {
|
||||
console.log(e.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleFileUpload(event: Event) {
|
||||
target.value = (event.target as HTMLInputElement);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Upload an image</h3>
|
||||
<div>
|
||||
<input type="file" id="file" ref="file" @change="handleFileUpload" />
|
||||
</div>
|
||||
<div>
|
||||
<button @click="submitFile">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
@@ -1,4 +1,13 @@
|
||||
export interface ImageType {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class ImageClass implements ImageType {
|
||||
id: number
|
||||
name: string
|
||||
constructor(id: number, title: string) {
|
||||
this.id = id
|
||||
this.name = title
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import router from './router';
|
||||
|
||||
createApp(App).use(router).mount('#app')
|
||||
createApp(App).mount('#app')
|
||||
@@ -1,36 +0,0 @@
|
||||
import { createWebHistory, createRouter } from "vue-router";
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
component: () => import("./components/Home.vue"),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: "/gallery",
|
||||
name: "gallery",
|
||||
component: () => import("./components/Gallery.vue"),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: "/image/:id",
|
||||
name: "image",
|
||||
component: () => import("./components/Image.vue"),
|
||||
props: ({ params }) => ({ id: Number(params.id) || 0 })
|
||||
},
|
||||
{
|
||||
path: "/upload",
|
||||
name: "upload",
|
||||
component: () => import("./components/Upload.vue"),
|
||||
props: true
|
||||
}
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,86 @@
|
||||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in new issue
Block a user