file upload start

This commit is contained in:
Thomas Mack 2022-03-31 08:24:10 +02:00
parent c67fcba7f2
commit f09870a42e
34 changed files with 4539 additions and 3118 deletions

6337
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,14 @@
"dependencies": {
"@dsb-norge/vue-keycloak-js": "*",
"@fortawesome/fontawesome-pro": "^6.1.1",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/pro-duotone-svg-icons": "^6.1.1",
"@fortawesome/pro-light-svg-icons": "^6.1.1",
"@fortawesome/pro-regular-svg-icons": "^6.1.1",
"@fortawesome/pro-solid-svg-icons": "^6.1.1",
"@fortawesome/vue-fontawesome": "^3.0.0-4",
"animate.css": "^4.1.1",
"axios": "^0.26.1",
"bootstrap": "^5.1.3",
"core-js": "^3.8.3",
"fs": "^0.0.1-security",

View File

@ -1,10 +1,15 @@
<template>
<router-view></router-view>
</template>
<script lang="ts">
import { Options, Vue } from 'vue-class-component';
import TheTwoColumnsLayout from '@/layouts/TheTwoColumnsLayout.vue';
@Options({
created() {
// this.$store.dispatch("autoSignIn");
@ -43,4 +48,64 @@ export default class App extends Vue {}
.text-vue2 {
color: rgb(65, 184, 131);
}
.billi-primary {
background-color: #21ae0e;
}
.billi-secondary {
background-color: #b2b1b1;
}
.billi-primary-50 {
background-color: #21ae0e75;
}
.billi-secondary-50 {
background-color: #b2b1b175;
}
.billi-bg {
background-color: #2a67dc;
}
.billi-bg-50 {
background-color: #2a67dc75
}
.billi-bg-transparent {
background-color: white;
background-color: rgb(255 255 255 / 40%);
}
.billi-read-detail {
background-color: transparent;
margin-top: 1rem;
display: grid;
grid-template-columns: 33% 66%;
grid-template-areas:
"seite haupt"
;
}
.billi-read-detail aside {
margin-right: 1rem;
grid-area: seite
}
.billi-read-detail section {
padding-left: 1rem;
border-left: 1px solid grey;
z-index: 0;
grid-area: haupt
}
.billi-icon-as-bg {
height: 600px;
position: absolute;
bottom: -50px;
left: -50px;
}
</style>

View File

@ -1,6 +1,7 @@
<template>
<nav class="navbar navbar-expand-lg bg-vue navbar-dark">
<div class="container">
<nav class="navbar navbar-default "></nav>
<!-- The Nav above helps to start the content below the fixed bar -->
<nav class="container-xxl flex-wrap flex-md-nowrap billi-bg navbar-dark fixed-top">
<ul class="navbar-nav me-auto">
<li class="nav-item active">
<router-link to="/list" class="nav-link">Startseite</router-link>
@ -11,13 +12,17 @@
<li class="nav-item">
<router-link to="/auth" class="nav-link">Auth</router-link>
</li>
<li class="nav-item">
<router-link to="/admin/namekey" class="nav-link">Admin</router-link>
</li>
</ul>
<div class="text-white" v-if="user && user.id"> angemeldet als: {{user.firstName}} {{user.lastName}} </div>
<button class="btn bg-vue2" @click="$keycloak.logoutFn" v-if="$keycloak.authenticated">
<div class="text-white mx-2" v-if="user && user.id"> angemeldet als: {{user.firstName}} {{user.lastName}} </div>
<button class="btn billi-primary" @click="$keycloak.logoutFn" v-if="$keycloak.authenticated">
<i class="fas fa-sign-out-alt"></i> Logout
</button>
</div>
</nav>
</template>

View File

@ -1,42 +1,27 @@
<template>
<transition
enter-active-class="animate__animated animate__fadeIn"
mode="out-in"
appear>
<font-awesome-icon class="billi-icon-as-bg" :icon="getIconName(item.item_type)" color="#eee" size="xxl"/>
</transition>
<transition
enter-active-class="animate__animated animate__lightSpeedInRight"
mode="out-in"
appear
>
<div class="row" :key="item.id">
<div class="col-12">
<h1 class="mt-4">
Zubehör
<button class="btn btn-lg bg-vue float-end"
>Bearbeiten</button>
</h1>
<div class="card mt-4">
<div class="row no-gutters">
<div class="col-md-4">
<img
src="https://dummyimage.com/600x400/34495e/fff"
class="card-img"
/>
</div>
<div class="col-md-8">
<div class="card-body">
<article class="billi-read-detail" :key="item.id">
<aside>
<img src="https://dummyimage.com/600x400/b2b1b1/fff" class="card-img" />
</aside>
<section>
<InventoryItemHead :item="item"></InventoryItemHead>
<InventoryItemCollectionInfo :item="item"></InventoryItemCollectionInfo>
<InventoryItemDescription :item="item"></InventoryItemDescription>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</article>
</transition>
</template>
@ -61,7 +46,7 @@ export default {
}
},
computed: {
...mapGetters(["activeItem"]),
...mapGetters(["activeItem","getIconName"]),
},
methods: {

View File

@ -1,57 +1,46 @@
<template>
<transition
enter-active-class="animate__animated animate__fadeIn"
mode="out-in"
appear>
<font-awesome-icon class="billi-icon-as-bg" :icon="getIconName(item.item_type)" color="#eee" size="xxl"/>
</transition>
<transition
enter-active-class="animate__animated animate__lightSpeedInRight"
mode="out-in"
appear
>
<div class="row" :key="item.id">
<div class="col-12">
<h1 class="mt-4">
Kamera
<button class="btn btn-lg bg-vue float-end"
@click="editCamera"
>Bearbeiten</button>
</h1>
<div class="card mt-4">
<div class="row no-gutters">
<div class="col-md-4">
<img
src="https://dummyimage.com/600x400/34495e/fff"
class="card-img"
/>
</div>
<div class="col-md-8">
<div class="card-body">
<article class="billi-read-detail" :key="item.id">
<aside>
<img src="https://dummyimage.com/600x400/b2b1b1/fff" class="card-img" />
</aside>
<section>
<InventoryItemHead :item="item"></InventoryItemHead>
<CameraTechnicalDetail :item="item"></CameraTechnicalDetail>
<InventoryItemCollectionInfo :item="item"></InventoryItemCollectionInfo>
<InventoryItemDescription :item="item"></InventoryItemDescription>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</article>
</transition>
</template>
<script>
import { mapGetters} from "vuex";
import { mapGetters } from "vuex";
import Camera from "@/store/classes/Camera";
import InventoryItemDescription from "@/components/camera/parts/InventoryItemDescription";
import InventoryItemHead from "@/components/camera/parts/InventoryItemHead";
import InventoryItemCollectionInfo from "@/components/camera/parts/InventoryItemCollectionInfo";
import CameraTechnicalDetail from "@/components/camera/parts/CameraTechnicalDetail";
export default {
name: "CameraReadDetail",
components: {
InventoryItemDescription,
InventoryItemCollectionInfo,
InventoryItemHead
InventoryItemHead,
CameraTechnicalDetail
},
props: {
item: {
@ -60,16 +49,21 @@ export default {
}
},
computed: {
...mapGetters(["activeItem"]),
...mapGetters(["activeItem","getIconName"]),
},
methods: {
editCamera() {
this.$store.dispatch("setCameraEditState", {cameraId:this.activeCamera.id, edit:true})
}
},
}
}
</script>
<style scoped>
</style>

View File

@ -91,8 +91,9 @@ export default {
},
methods: {
setFilterByType(itemType) {
console.log("setFilterByType", itemType)
if(this.filterByType === itemType) this.filterByType = "";
this.filterByType = itemType;
else this.filterByType = itemType;
},
isFilterByType(itemType) {
return this.filterByType === itemType

View File

@ -11,7 +11,7 @@
:key="item.id"
>
<div class="row no-gutters">
<div class="col-3 text-center ">
<div class="col-3 text-center text-white ">
<i class="mx-2 mt-3 text-sm-center " :class="`${getIconClass}`"></i>
</div>
<div class="col-9 bg-white text-black">
@ -19,7 +19,7 @@
>
<div class="row mt-2 bottom-50">
<div class="col-md-8 "> {{ item.name }}</div>
<div class="col-md-8 "> {{ item.name }} {{ setCounter }}</div>
<div class="col-md-4 text-end"><i class="fa-solid fa-tag text-black-50"></i> {{ item.inventory_number }}</div>
</div>
@ -55,8 +55,8 @@ export default {
return icons.join(' ');
},
getActiveStyle() {
if(this.isActive) return ["bg-primary"] ;
else return ["bg-secondary"];
if(this.isActive) return ["billi-primary"] ;
else return ["billi-secondary"];
},
isActive() {
@ -69,6 +69,13 @@ export default {
else if(this.item.description.length > 40) return this.item.description.slice(0, 40)+ "...";
else if( this.item.description) return this.item.description;
return "";
},
setCounter() {
if(this.item.item_type ==="set" && this.item.items) {
return `[${this.item.items.length}]`;
} else {
return "";
}
}
},

View File

@ -1,130 +1,47 @@
<template>
<transition
enter-active-class="animate__animated animate__fadeIn"
mode="out-in"
appear>
<font-awesome-icon class="billi-icon-as-bg" :icon="getIconName(item.item_type)" color="#eee" size="xxl"/>
</transition>
<transition
enter-active-class="animate__animated animate__lightSpeedInRight"
mode="out-in"
appear
>
<div class="row" :key="item.id">
<div class="col-12">
<h1 class="mt-4">
Objektiv
<button class="btn btn-lg bg-vue float-end"
@click="editCamera"
>Bearbeiten
</button>
</h1>
<div class="card mt-4">
<div class="row no-gutters">
<div class="col-md-4">
<img
src="https://dummyimage.com/600x400/34495e/fff"
class="card-img"
/>
</div>
<div class="col-md-8">
<div class="card-body">
<article class="billi-read-detail" :key="item.id">
<aside>
<img src="https://dummyimage.com/600x400/#b2b1b1/fff" class="card-img" />
</aside>
<section>
<InventoryItemHead :item="item"></InventoryItemHead>
<div v-if="true" class="card">
<div class="card-header"> Technische Details</div>
<div class="card-body">
<div v-if="true" class="row">
<div class="col-6">
Marke
</div>
<div class="col-6">
{{ brand(item.brand_key).name }}
</div>
</div>
<div v-if="true" class="row">
<div class="col-6">
Hersteller
</div>
<div class="col-6">
{{ manufacturer(item.manufacturer_key).name }}
</div>
</div>
<div v-if="false" class="row">
<div class="col-6">
Lichtstärke
</div>
<div class="col-6">
{{ item.aperture_max }}
</div>
</div>
<div v-if="true" class="row">
<div class="col-6">
Brennweite
</div>
<div class="col-3">
{{ item.focal_length_min }}
</div>
<div class="col-3">
{{ item.focal_length_max }}
</div>
</div>
<div v-if="true" class="row">
<div class="col-6">
Autofocus
</div>
<div class="col-6">
{{ item.autofocus }}
</div>
</div>
<div v-if="item.mount_key" class="row">
<div class="col-6">
Anschluss
</div>
<div class="col-6">
{{ mount(item.mount_key).name }}
</div>
</div>
<div v-if="true" class="row">
<div class="col-6">
Baujahr
</div>
<div class="col-6">
{{ item.year_of_production }}
</div>
</div>
</div>
</div>
<LensTechnicalDetail :item="item"></LensTechnicalDetail>
<InventoryItemCollectionInfo :item="item"></InventoryItemCollectionInfo>
<InventoryItemDescription :item="item"></InventoryItemDescription>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</article>
</transition>
</template>
<script>
import { mapGetters} from "vuex";
import Lens from "@/store/classes/Lens";
import InventoryItemDescription from "@/components/camera/parts/InventoryItemDescription";
import InventoryItemCollectionInfo from "@/components/camera/parts/InventoryItemCollectionInfo";
import InventoryItemHead from "@/components/camera/parts/InventoryItemHead";
import LensTechnicalDetail from "@/components/camera/parts/LensTechnicalDetail";
export default {
name: "CameraReadDetail",
components: {
InventoryItemHead,
InventoryItemDescription,
InventoryItemCollectionInfo
InventoryItemCollectionInfo,
LensTechnicalDetail
},
props: {
item: {
@ -133,7 +50,7 @@ export default {
}
},
computed: {
...mapGetters(["activeItem", "brand", "manufacturer", "mount"]),
...mapGetters(["activeItem", "brand", "manufacturer", "mount", "getIconName"]),
getConditionName() {
if(this.item.condition_key) return this.$store.getters.condition(this.item.condition_key).name || "";

View File

@ -0,0 +1,54 @@
<template>
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th>Schlüssel</th>
<th>Name</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<NameKeyListItem v-for="item of items" :item="item" :storeType="storeType" :key="item.key"></NameKeyListItem>
<!-- <tr >-->
<!-- <td >{{item.key}}</td>-->
<!-- <td >{{item.name}}</td>-->
<!-- <td >{{item.description}}</td>-->
<!-- </tr>-->
</tbody>
</table>
</div>
</template>
<script>
import NameKeyListItem from "@/components/camera/parts/NameKeyListItem";
export default {
name: "NameKeyReadList",
components: {
NameKeyListItem
},
props: {
items: {
type: Array,
required: true
},
storeType: {
type: String,
required: true
}
},
}
</script>
<style scoped>
</style>

View File

@ -1,42 +1,27 @@
<template>
<transition
enter-active-class="animate__animated animate__fadeIn"
mode="out-in"
appear>
<font-awesome-icon class="billi-icon-as-bg" :icon="getIconName(item.item_type)" color="#eee" size="xxl"/>
</transition>
<transition
enter-active-class="animate__animated animate__lightSpeedInRight"
mode="out-in"
appear
>
<div class="row" :key="item.id">
<div class="col-12">
<h1 class="mt-4">
Set
<button class="btn btn-lg bg-vue float-end"
>Bearbeiten</button>
</h1>
<div class="card mt-4">
<div class="row no-gutters">
<div class="col-md-4">
<img
src="https://dummyimage.com/600x400/34495e/fff"
class="card-img"
/>
</div>
<div class="col-md-8">
<div class="card-body">
<article class="billi-read-detail" :key="item.id">
<aside>
<img src="https://dummyimage.com/600x400/b2b1b1/fff" class="card-img" />
</aside>
<section>
<InventoryItemHead :item="item"></InventoryItemHead>
<InventoryItemCollectionInfo :item="item"></InventoryItemCollectionInfo>
<InventoryItemDescription :item="item"></InventoryItemDescription>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</article>
</transition>
</template>
@ -61,7 +46,7 @@ export default {
}
},
computed: {
...mapGetters(["activeItem"]),
...mapGetters(["activeItem","getIconName"]),
},
methods: {

View File

@ -0,0 +1,105 @@
<template>
<div class="card billi-bg-transparent">
<div class="card-header billi-bg-50"> Technische Details</div>
<div v-if="true" class="card-body billi-bg-transparent">
<div v-if="item.brand_key" class="row">
<div class="col-6">
Marke
</div>
<div class="col-6">
{{ brand(item.brand_key).name }}
</div>
</div>
<div v-if="item.manufacturer_key" class="row">
<div class="col-6">
Hersteller
</div>
<div class="col-6">
{{ manufacturer(item.manufacturer_key).name }}
</div>
</div>
<div v-if="item.buildtype_key" class="row">
<div class="col-6">
Bauform
</div>
<div class="col-6">
{{ buildtype(item.buildtype_key).name }}
</div>
</div>
<div v-if="item.mount_key" class="row">
<div class="col-6">
Anschluss
</div>
<div class="col-6">
{{ mount(item.mount_key).name }}
</div>
</div>
<div v-if="item.media_key" class="row">
<div class="col-6">
Medium
</div>
<div class="col-6">
{{ media(item.media_key).name }}
</div>
</div>
<div v-if="true" class="row">
<div class="col-6">
Belichtungsmessung
</div>
<div class="col-6">
{{ item.light_measure_method }}
</div>
</div>
<div v-if="true" class="row">
<div class="col-6">
Baujahr
</div>
<div class="col-6">
{{ item.year_of_production }}
</div>
</div>
<div v-if="item.year_manufactured_from || item.year_manufactured_to " class="row">
<div class="col-6">
Produktionszeitraum
</div>
<div class="col-6">
{{ item.year_manufactured_from }} - {{item.year_manufactured_to}}
</div>
</div>
</div>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import Camera from "@/store/classes/Camera";
export default {
name: "LensTechnicalDetail",
props: {
item: {
type:Camera,
required: true
}
},
computed: {
...mapGetters(["activeItem", "brand", "manufacturer", "mount", "buildtype", "media"]),
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,53 @@
<template>
<div>
<label for="fileUpload">Datei</label>
<input id="fileUpload" type="file"
@change="handleFileUpload( $event) "
/><br>
<button v-on:click="submitFile()">Upload</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "FileUpload",
data() {
return {
file: ''
}
},
methods: {
handleFileUpload(event) {
this.file = event.target.files[0];
},
submitFile() {
let formData = new FormData();
formData.append('file',this.file);
axios.post( '^/api/fs/billibox/assets',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then((res) => {
console.log("FileUpload Success", res.data);
}).catch((err) => {
console.log(err);
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -35,8 +35,8 @@ export default {
computed: {
cardStyle() {
console.log(this.itemType, this.active);
if(!this.active) return ["bg-secondary"]
else return ["bg-primary"]
if(!this.active) return ["billi-secondary"]
else return ["billi-primary"]
}
}
}

View File

@ -1,7 +1,7 @@
<template>
<div class="card mt-2">
<div class="card-header">Sammlung</div>
<div class="card-body">
<div class="card mt-2 billi-bg-transparent">
<div class="card-header billi-bg-50">Sammlung</div>
<div class="card-body billi-bg-transparent">
<div class="row">
<div class="col-6">
Jahr des Kaufs

View File

@ -2,15 +2,15 @@
<div class="row">
<div class="col-12">
<div v-if="true" class="card mt-2">
<div class="card-header">Besonderheiten</div>
<div class="card-body">
<div v-if="true" class="card mt-2 billi-bg-transparent">
<div class="card-header billi-bg-50 ">Besonderheiten</div>
<div class="card-body billi-bg-transparent">
{{ item.description }}
</div>
</div>
<div class="card mt-2">
<div class="card-header">Ausgeführte Arbeiten</div>
<div class="card-body">
<div class="card mt-2 billi-bg-transparent">
<div class="card-header billi-bg-50">Ausgeführte Arbeiten</div>
<div class="card-body billi-bg-transparent">
{{ item.work_done }}
</div>
</div>

View File

@ -1,12 +1,12 @@
<template>
<div class="row">
<div class="col-9">
<h5 class="card-title mb-4">{{ item.name }}</h5>
<h2 class="card-title mb-4">{{ item.name }}</h2>
</div>
<div class="col-3">
<div class="d-grid">
<button class="btn bg-primary text-white">
<i class="fa-solid fa-tag"></i> {{ item.inventory_number}}
<button class="btn billi-primary text-white">
<i class="fa-solid fa-tag white"></i> {{ item.inventory_number}}
</button>
</div>
</div>
@ -30,4 +30,7 @@ export default {
<style scoped>
i:before {
color:white;
}
</style>

View File

@ -1,8 +1,8 @@
<template>
<div class="card">
<div class="card-header"> Technische Details</div>
<div class="card-body">
<div class="card billi-bg-transparent">
<div class="card-header billi-bg-50"> Technische Details</div>
<div class="card-body billi-bg-transparent">
<div v-if="item.brand_key" class="row">
<div class="col-6">
Marke
@ -73,9 +73,16 @@
<script>
import {mapGetters} from "vuex";
import Lens from "@/store/classes/Lens";
export default {
name: "LensTechnicalDetail",
props: {
item: {
type:Lens,
required: true
}
},
computed: {
...mapGetters(["activeItem", "brand", "manufacturer", "mount"]),
}

View File

@ -0,0 +1,73 @@
<template>
<tr class="billi-primary-50" v-if="edit">
<td >
{{item.key}}
</td>
<td >
<input type="text" v-model="newItem.name" name="key" >
</td>
<td >
<input type="text" v-model="newItem.description" name="key" >
</td>
<td class="billi-primary"
@click="toggleEdit"
><i class="fa-solid fa-floppy-disk"></i></td>
</tr>
<tr v-else>
<td >{{item.key}}</td>
<td >{{item.name}}</td>
<td >{{item.description}}</td>
<td class="bg-secondary text-white"
@click="toggleEdit"
><i class="fa-solid fa-pen"></i></td>
</tr>
</template>
<script>
import NameKey from "@/store/classes/NameKey";
export default {
name: "NameKeyListItem",
data() {
return {
edit: false,
newItem: {},
}
},
props: {
item: {
type: NameKey,
required: true
},
storeType: {
type: String,
required: true
}
},
methods: {
toggleEdit() {
this.edit = !this.edit;
if(this.edit) {
this.newItem = {...this.item};
} else {
this.$store.dispatch("storeNameKeyObject", {
storeType: this.storeType,
item: this.newItem,
itemOld: this.item
})
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,154 @@
<template>
<div class="card mt-2 ">
<div class="card-header" :class="activeClass">
<div class="row">
<div class="col-11"> {{this.store.name}}</div>
<div class="col-1 bg-secondary" @click="toggleNew">+</div>
</div>
</div>
<div class="card-body" v-if="add && isActive && this.store.type === 'filestore'">
<FileUpload></FileUpload>
</div>
<div class="card-body" v-if="add && isActive">
<p> Neuen Eintrag hinzufügen</p>
<Form @submit="submitData" :validation-schema="schema" v-slot="{ errors }">
<div class="form-row">
<div class="form-group col-md-8 offset-2">
<label for="key"><strong>Schlüssel</strong></label>
<Field
as="input"
name="key"
type="text"
class="form-control"
id="key"
/>
<small class="text-danger" v-if="errors.key">{{
errors.key
}}</small>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-8 offset-2">
<label for="name"><strong>Name</strong></label>
<Field
as="input"
name="name"
type="text"
class="form-control"
id="name"
/>
<small class="text-danger" v-if="errors.name">{{
errors.name
}}</small>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-8 offset-2">
<label for="description"><strong>Beschreibung</strong></label>
<Field
as="input"
name="description"
type="textarea"
class="form-control"
id="description"
/>
<small class="text-danger" v-if="errors.description">{{
errors.description
}}</small>
</div>
</div>
<div class="form-row mt-3">
<div class="form-group col-md-8 offset-2">
<div class="d-grid">
<button class="btn bg-vue">
<span v-if="!isLoading">Speichern</span>
<span v-else class="spinner-border spinner-border-sm"></span>
</button>
</div>
</div>
</div>
</Form>
</div>
</div>
</template>
<script>
import { Form, Field } from "vee-validate";
import * as yup from "yup";
import FileUpload from "@/components/camera/parts/FileUpload";
export default {
name: "StoreSelector",
components: {
FileUpload,
Form,
Field,
},
props: {
store: {
type: Object,
required: true
},
activeStore: {
type: String,
required: true
}
},
data() {
const schema = yup.object().shape({
key: yup
.string()
.required("Der Schlüssel wird benötigt")
.trim()
.matches(/^[-a-zA-Z0-9]+$/, "Erlaubte Zeichen sind a-z A-Z 0-9 und -"),
name: yup
.string()
.required("Der Name ist erforderlich"),
description: yup.string()
});
return {
schema,
add: false,
isLoading: false,
error:""
}
},
computed: {
activeClass() {
if(this.activeStore === this.store.id) return ["billi-primary"];
else return ["billi-secondary"]
},
isActive() {
return this.activeStore === this.store.id;
}
},
methods: {
toggleNew() {
this.add = !this.add;
},
submitData(values) {
console.log(values);
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,20 +1,26 @@
<template>
<div>
<header class="navbar navbar-expand-md navbar-dark billi-bg">
<TheNavbar></TheNavbar>
<main>
<div class="container-fluid" :class="containerClasses">
<div class="row" :class="rowClasses">
<div :class="leftColumnClasses">
<slot name="leftCol">
</header>
<div class="container-xxl billi-layout">
<main class="billi-main order-1" id="billi-main">
<slot name="main">
<h1>Linke Spalte</h1>
</slot>
</div>
<div :class="rightColumnClasses">
<slot name="rightCol"><h1>Rechte Spalte</h1></slot>
</div>
</div>
</div>
</main>
<aside class="billi-sidebar billi-bg overflow-scroll" id="billi-sidebar">
<slot name="aside">
<h1>Linke Spalte</h1>
</slot>
</aside>
</div>
</template>
@ -24,38 +30,37 @@ export default {
name: "TheShopLayout",
components: {
TheNavbar
},
props: {
leftColumnClass: {
type:String,
default: 'col-md-8'
},
rightColumnClass: {
type:String,
default: 'col-md-4'
},
fullsize: {
type: Boolean,
default: false,
}
},
computed: {
leftColumnClasses() {
return [this.leftColumnClass, this.fullsize ? "h-100": ""];
},
rightColumnClasses() {
return [this.rightColumnClass, this.fullsize ? "h-100": ""];
},
rowClasses(){
return [this.fullsize ? "h-100": ""];
},
containerClasses(){
return [this.fullsize ? "vh-100": ""];
}
}
}
</script>
<style scoped>
.billi-layout {
display: grid;
grid-template-columns: auto 400px ;
grid-template-areas:
"inhalt seitenleiste"
}
#billi-head {
background-color: grey;
grid-area: kopfzeile
}
#billi-sidebar {
padding-top: 1em;
padding-left:1em;
padding-right: 1em;
height: 100vh;
grid-area: seitenleiste;
}
#billi-main {
padding:1em;
grid-area: inhalt;
}
</style>

View File

@ -3,9 +3,33 @@ import App from './App.vue'
import store from './store';
import router from './router'
import VueKeyCloak from '@dsb-norge/vue-keycloak-js'
import { KeycloakInstance } from "keycloak-js";
import { VueKeycloakInstance } from "@dsb-norge/vue-keycloak-js/dist/types";
import {library} from "@fortawesome/fontawesome-svg-core";
import { faUserSecret,faTelescope,
faCamera,
faAperture,
faBoxFull,
faTag,
faToggleOff,
faToggleOn } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
library.add(faUserSecret);
//
//
library.add(faTelescope);
library.add(faCamera);
library.add(faAperture);
library.add(faBoxFull);
library.add(faTag);
library.add(faToggleOff);
library.add(faToggleOn);
const app =
createApp(App);
@ -13,6 +37,7 @@ createApp(App);
app.use(store)
app.use(router)
app.component("font-awesome-icon", FontAwesomeIcon);
app.use(VueKeyCloak, {
config: {
url: 'https://auth.toking.de/',

View File

@ -5,7 +5,7 @@
:right-column-class="'col-md-4'"
:fullsize="true"
>
<template #leftCol>
<template #main>
<div class="mt-5 text-center">
<div class="display-1 my-5">Billibox</div>
<div class="display-4 my-5">Die kleine Welt alter Fotoapparate</div>
@ -15,7 +15,7 @@
</div>
</template>
<template #rightCol>
<template #aside>
<transition
enter-active-class="animate__animated animate__bounceInRight"
leave-active-class="animate__animated animate__bounceOutRight"

View File

@ -4,7 +4,7 @@
:right-column-class="'col-md-4 bg-vue'"
:fullsize="true"
>
<template #leftCol>
<template #main>
<div v-if="activeItem && activeItem.edit">
<CameraEditDetail></CameraEditDetail>
@ -23,18 +23,12 @@
<AccessoireReadDetail :item="activeItem"></AccessoireReadDetail>
</div>
<!-- <component-->
<!-- :is="componentName"-->
<!-- :item="activeItem"-->
<!-- v-if="activeItem"-->
<!-- ></component>-->
<div v-else>
<h2>Bitte wählen sie eine Kamera aus der Liste</h2>
</div>
</template>
<template #rightCol>
<template #aside>
<InventoryList :items="inventory"></InventoryList>

View File

@ -0,0 +1,77 @@
<template>
<TheCameraLayout
:left-column-class="'col-md-8 '"
:right-column-class="'col-md-4 bg-vue'"
:fullsize="true"
>
<template #main>
<div v-if="activeStore!=''">
<NameKeyReadList
:storeType="activeStore"
:items="store(activeStore)" >
</NameKeyReadList>
</div>
<div v-else class="alert alert-secondary">Bitte einen Eintrag aus der rechten liste wählen</div>
</template>
<template #aside>
<div v-for="store in stores" :key="store.id">
<div v-if="store.type === 'nameKey' || store.type === 'filestore'" >
<StoreSelector :active-store="activeStore" :store="store" @click="setActiveStore(store.id)"></StoreSelector>
<!-- <div class="alert" :class="isActiveClass(store.id)" @click="setActiveStore(store.id)">{{store.name}}</div>-->
</div>
</div>
</template>
</TheCameraLayout>
</template>
<script>
import TheCameraLayout from "@/layouts/TheCameraLayout";
import {mapGetters} from "vuex";
import NameKeyReadList from "@/components/camera/NameKeyReadList";
import StoreSelector from "@/components/camera/parts/StoreSelector";
export default {
name: "ReadNameKeyPage",
components: {
NameKeyReadList,
TheCameraLayout,
StoreSelector
},
data() {
return {
activeStore: "brands"
}
},
computed: {
...mapGetters(["stores", "store"]),
},
methods: {
isActiveClass(key) {
if (key === this.activeStore) return ['billi-primary'];
else return ['alert-secondary'];
},
setActiveStore(key) {
this.activeStore = key
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,7 +1,6 @@
import ShopPage from "@/pages/ShopPage.vue";
import CreateCameraPage from "@/pages/CreateCameraPage.vue";
import ReadProductPage from "@/pages/ReadProductPage.vue";
import component from "*.vue";
import ReadNameKeyPage from "@/pages/ReadNameKeyPage.vue";
import ReadListPage from "@/pages/ReadListPage.vue";
const shopRoutes = [
{
@ -9,6 +8,15 @@ const shopRoutes = [
component: CreateCameraPage,
meta: {
requiresAuth: true
},
},
{
path: "/admin/namekey",
component: ReadNameKeyPage,
props: true,
meta: {
requiresAuth: false,
// enterTransition: "rubberBand"
}
},

View File

@ -1,7 +1,8 @@
import store from "@/store";
import ReadCameraPage from "@/pages/ReadCameraPage.vue";
import ReadListPage from "@/pages/ReadListPage.vue";
import AuthRedirect from '@/components/auth/AuthRedirect.vue'
import AuthRedirect from '@/components/auth/AuthRedirect.vue';
import ReadNameKeyPage from "@/pages/ReadNameKeyPage.vue";
const appRoutes = [
@ -57,6 +58,7 @@ const appRoutes = [
// enterTransition: "rubberBand"
}
},
]
export default appRoutes;

View File

@ -46,7 +46,8 @@ export const buildtypes = [
{key: "lf", name: "Lichtfeldkamera", description: ''},
{key: "balg", name: "Balgenkamera", description: ''},
{key: "sof", name: "Sofortbild", description: ''},
{key: "spz", name: "Spezialkamera", description: ''},
{key: "stu", name: "Studiokamera", description: ''},
];
export const brands = [
@ -383,13 +384,14 @@ export const lenses = [
year_manufactured_from: "1980",
year_manufactured_to: "1990",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "Bisher nur kleinere Reinigungsarbeiten",
ligthMesure: "",
light_measure_method: "",
special: "",
year_of_production: 2000,
year_of_purchase: 1980
year_of_purchase: 1980,
set_key: "pentax"
}
];
@ -401,19 +403,21 @@ export const cameras = [
brand_key: "pentax",
condition_key: "neuwertig",
name: "Pentax K30",
buildtype: "slr",
description: "Langer Text",
buildtype_key: "slr",
description: 'eine tolle Kamera mit vielen Funktionen, ' +
'Bildstabilisierung und noch viel mehr',
year_manufactured_from: "",
year_manufactured_to: "",
manufacturer_key: "",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "",
ligthMesure: "",
work_done: "kleinere Arbeiten",
light_measure_method: "Mehrfeld",
special: "",
year_of_production: 2000,
year_of_purchase: 2022,
set_key: "pentax"
},
{
id: "pentax_20",
@ -422,16 +426,16 @@ export const cameras = [
brand_key: "pentax",
condition_key: "neuwertig",
name: "Pentax K50",
buildtype: "slr",
buildtype_key: "slr",
description: "Langer Text",
year_manufactured_from: "",
year_manufactured_to: "",
manufacturer_key: "",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "",
ligthMesure: "",
light_measure_method: "",
special: "",
year_of_production: 2000,
year_of_purchase: 2022,
@ -443,16 +447,16 @@ export const cameras = [
brand_key: "pentax",
condition_key: "neuwertig",
name: "Pentax K3",
buildtype: "slr",
buildtype_key: "slr",
description: "Langer Text",
year_manufactured_from: "",
year_manufactured_to: "",
manufacturer_key: "",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "",
ligthMesure: "",
light_measure_method: "",
special: "",
year_of_production: 2000,
year_of_purchase: 2022,
@ -464,16 +468,16 @@ export const cameras = [
brand_key: "agfa",
condition_key: "neuwertig",
name: "Agfa Click",
buildtype: "slr",
buildtype_key: "slr",
description: "Langer Text",
year_manufactured_from: "",
year_manufactured_to: "",
manufacturer_key: "",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "",
ligthMesure: "",
light_measure_method: "",
special: "",
year_of_production: 2000,
year_of_purchase: 2000,
@ -485,16 +489,16 @@ export const cameras = [
brand_key: "agfa",
condition_key: "neuwertig",
name: "Agfa Box",
buildtype: "slr",
buildtype_key: "slr",
description: "Langer Text",
year_manufactured_from: "",
year_manufactured_to: "",
manufacturer_key: "",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "",
ligthMesure: "",
light_measure_method: "",
special: "",
year_of_production: 2000,
year_of_purchase: 1980,
@ -506,16 +510,16 @@ export const cameras = [
brand_key: "canon",
condition_key: "neuwertig",
name: "Canon EOS",
buildtype: "slr",
buildtype_key: "slr",
description: "Langer Text",
year_manufactured_from: "",
year_manufactured_to: "",
manufacturer_key: "",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "",
ligthMesure: "",
light_measure_method: "",
special: "",
year_of_production: 2000,
year_of_purchase: 1980,
@ -527,16 +531,16 @@ export const cameras = [
brand_key: "agfa",
condition_key: "neuwertig",
name: "Agfa Click",
buildtype: "slr",
buildtype_key: "slr",
description: "Langer Text",
year_manufactured_from: "",
year_manufactured_to: "",
manufacturer_key: "",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "",
ligthMesure: "",
light_measure_method: "",
special: "",
year_of_production: 2000,
year_of_purchase: 2000,
@ -548,16 +552,16 @@ export const cameras = [
brand_key: "agfa",
condition_key: "neuwertig",
name: "Agfa Box",
buildtype: "slr",
buildtype_key: "slr",
description: "Langer Text",
year_manufactured_from: "",
year_manufactured_to: "",
manufacturer_key: "",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "",
ligthMesure: "",
light_measure_method: "",
special: "",
year_of_production: 2000,
year_of_purchase: 1980,
@ -569,16 +573,16 @@ export const cameras = [
brand_key: "canon",
condition_key: "neuwertig",
name: "Canon EOS",
buildtype: "slr",
buildtype_key: "slr",
description: "Langer Text",
year_manufactured_from: "",
year_manufactured_to: "",
manufacturer_key: "",
price_purchased: 0.0,
price_hisotric: 0.0,
price_historic: 0.0,
price_current: 0.0,
work_done: "",
ligthMesure: "",
light_measure_method: "",
special: "",
year_of_production: 2000,
year_of_purchase: 1980,
@ -592,7 +596,15 @@ export const sets = [
inventory_number: '300',
item_type: "set",
name: "minox Set"
},
{
id: "pentax-set",
inventory_number: 'B-1300',
item_type: "set",
name: "Pentax K30 Set",
items: ["pentax-zoom", "pentax_19"]
}
]

View File

@ -24,4 +24,29 @@ export default class Camera extends InventoryItem {
}
}
}
get light_measure_method(): string {
return this._light_measure_method;
}
get mount_key(): string {
return this._mount_key;
}
get buildtype_key(): string {
return this._buildtype_key;
}
get media_key(): string {
return this._media_key;
}
get year_manufactured_from(): string {
return this._year_manufactured_from;
}
get year_manufactured_to(): string {
return this._year_manufactured_to;
}
}

View File

@ -32,6 +32,8 @@ export default class InventoryItem {
private _year_of_production: number;
private _year_of_purchase: number;
private _set_key: string
constructor( id:string, name:string, values?:Object ) {
@ -168,5 +170,14 @@ export default class InventoryItem {
set year_of_purchase(value: number) {
this._year_of_purchase = value;
}
get set_key(): string {
return this._set_key;
}
set set_key(value: string) {
this._set_key = value;
}
}

View File

@ -22,6 +22,15 @@ export default class NameKey {
return this._key;
}
set key(value: string) {
this._key = value;
}
set description(value: string) {
this._description = value;
}
get name(): string {
return this._name;
}
@ -45,4 +54,9 @@ export default class NameKey {
set active(value: boolean) {
this._active = value;
}
get description(): string {
return this._description;
}
}

View File

@ -12,27 +12,31 @@ const db = new BilliDB("billibox", 3);
const stores = [
"inventory",
"brands",
"conditions",
"buildtypes",
"manufacturers",
"medias",
"mounts",
"sets"
{id: "inventory", name: 'Inventar', type: 'object'},
{id: "brands", name: "Marken", type: "nameKey"},
{id: "conditions", name: "Zustände", type: "nameKey"},
{id: "buildtypes", name: "Bauformen", type: "nameKey"},
{id: "manufacturers", name: "Hersteller", type: "nameKey"},
{id: "medias", name: "Medien", type: "nameKey"},
{id: "mounts", name: "Anschlüsse", type: "nameKey"},
{id: "filtermounts", name: "Filteranschlüsse", type: "nameKey"},
{id: "assets", name: "Dateien", type: "filestore"},
];
const state = {
isInitialized: false,
inventory: [],
assets:[],
brands: [],
manufacturers: [],
conditions: [],
medias: [],
mounts: [],
sets: [],
buildtypes: []
buildtypes: [],
filtermounts: []
};
@ -41,6 +45,12 @@ const getters = {
return state.inventory
},
stores: (state?:any) => stores,
store: (state:any) => (id: any) => {
console.log("Query store:", id);
return state[id]
},
cameras: (state: any) => {
return state.inventory.find((item:InventoryItem) => item.item_type ==="camera");
},
@ -66,6 +76,9 @@ const getters = {
mounts: (state: any) => state.mounts,
mount: (state: any) => (id: any) => state.mounts.find((item: any) => item.key === id),
filtermounts: (state: any) => state.filtermounts,
filtermount: (state: any) => (id: any) => state.filtermounts.find((item: any) => item.key === id),
conditions: (state: any) => state.conditions,
condition: (state: any) => (id: any) => state.conditions.find((condition: any) => condition.key === id),
@ -78,6 +91,20 @@ const getters = {
isInitialized: (state:any ) => state.isInitialized,
getIconName: (state:any) => (item_type) => {
switch (item_type) {
case "camera":
return "camera";
case "set":
return "box-open-full";
case "accessoire":
return "telescope";
case "lens":
return "aperture";
}
return "telescope"
}
};
const mutations = {
setInventory(state:any, payload:any) {
@ -104,6 +131,13 @@ const mutations = {
return payload.id;
},
storeNameKey(state:any, payload:any) {
const objNK:NameKey = state[payload.storeType].find( (item) => item.key === payload.itemOld.key);
if(objNK.key) objNK.key = payload.item.key;
objNK.name = payload.item.name;
objNK.description = payload.item.description;
},
setItemActiveState(state:any, payload:any) {
console.log("mutation.setItemActiveState");
state.inventory.map((item:InventoryItem) => item.active= false);
@ -129,6 +163,17 @@ const mutations = {
};
const actions = {
storeNameKeyObject(context:ActionContext<any, any>, payload) {
console.log("actions.storeNameKeyObject");
db.saveItem(payload.item, payload.storeType).then((res) => {
console.log("Item stored in Database")
})
context.commit("storeNameKey", payload);
},
fetchNameKeys(context:ActionContext<any, any>, payload) {
const field =payload.namekey_type;
db.getItems(field).then((items) => {
@ -178,11 +223,11 @@ const actions = {
console.log("action.loadDataFromLocalDB")
const storeActions = [];
for (const store of stores) {
if(store ==="inventory") {
if(store.type !="nameKey") {
storeActions.push(context.dispatch("fetchInventory"));
}
else {
storeActions.push(context.dispatch("fetchNameKeys", {namekey_type:store}));
storeActions.push(context.dispatch("fetchNameKeys", {namekey_type:store.id}));
}
}
Promise.all(storeActions).then( () => {
@ -199,7 +244,7 @@ const actions = {
if(payload.seed && seedVersion < SEED_VERSION ) {
console.log("SEEDING database");
for (const store of stores) {
promises.push(db.saveItems(seed[store], store));
promises.push(db.saveItems(seed[store.id], store.id));
}
localStorage.setItem("SEED_VERSION", seed.SEED_VERSION.toString())
} else {

View File

@ -1,4 +1,15 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
transpileDependencies: true,
devServer: {
proxy: {
"/api" : {
ws:true,
changeOrigin: true,
target: "http://127.0.0.1:4000/"
}
}
}
})