[TASK] Inital State
This commit is contained in:
+304
@@ -0,0 +1,304 @@
|
||||
<template>
|
||||
<div>
|
||||
<NavigationBar
|
||||
:primary-color="primaryColor"
|
||||
@changed-tab="changeView"
|
||||
/>
|
||||
<div class="container">
|
||||
<div
|
||||
v-if="!ready"
|
||||
class="valign-wrapper"
|
||||
style="height: 90vh"
|
||||
>
|
||||
<div class="progress">
|
||||
<div class="indeterminate" />
|
||||
</div>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-if="ready && view == 'inventory'"
|
||||
id="inventory"
|
||||
class="col s12"
|
||||
>
|
||||
<InventoryTable
|
||||
ref="inventory"
|
||||
:articles="articles"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="ready && view == 'calc'"
|
||||
id="calc"
|
||||
class="col s12"
|
||||
>
|
||||
<Calculator :articles="articles" />
|
||||
</div>
|
||||
<div
|
||||
v-if="ready && view == 'article'"
|
||||
id="article"
|
||||
class="col s12"
|
||||
>
|
||||
<div
|
||||
v-for="(a, index) in articles"
|
||||
:key="'article_'+index"
|
||||
class="card"
|
||||
>
|
||||
<div class="card-content row">
|
||||
<span class="card-title">{{ a.name }}</span>
|
||||
<div class="input-field inline col s8">
|
||||
<input
|
||||
:id="'a_name_'+index"
|
||||
v-model="a.name"
|
||||
placeholder="Artikelname"
|
||||
class="validate"
|
||||
>
|
||||
<label
|
||||
:for="'a_name_'+index"
|
||||
class="active"
|
||||
>Name</label>
|
||||
</div>
|
||||
<div class="input-field inline col s4">
|
||||
<input
|
||||
:id="'a_short_'+index"
|
||||
v-model="a.short"
|
||||
placeholder="Kurzname"
|
||||
class="validate"
|
||||
>
|
||||
<label
|
||||
:for="'a_short_'+index"
|
||||
class="active"
|
||||
>Kürzel</label>
|
||||
</div>
|
||||
<div class="input-field col s8">
|
||||
<input
|
||||
:id="'a_csize_'+index"
|
||||
v-model.number="a.content.size"
|
||||
placeholder="Gesamtinhalt"
|
||||
type="number"
|
||||
class="validate"
|
||||
step="0.01"
|
||||
>
|
||||
<label
|
||||
:for="'a_csize_'+index"
|
||||
class="active"
|
||||
>Gesamtinhalt</label>
|
||||
</div>
|
||||
<div class="input-field col s4">
|
||||
<input
|
||||
:id="'a_dim_'+index"
|
||||
v-model="a.dimension"
|
||||
placeholder="Dimension"
|
||||
class="validate"
|
||||
max="5"
|
||||
>
|
||||
<label
|
||||
:for="'a_dim_'+index"
|
||||
class="active"
|
||||
>Dimension</label>
|
||||
<span class="helper-text">z.B. Liter(l), Stück(Stk.)</span>
|
||||
</div>
|
||||
|
||||
<div class="input-field col s4">
|
||||
<input
|
||||
:id="'a_psize_'+index"
|
||||
v-model.number="a.portion.size"
|
||||
placeholder="Gesamtinhalt"
|
||||
type="number"
|
||||
class="validate"
|
||||
step="0.01"
|
||||
max="5"
|
||||
>
|
||||
<label
|
||||
:for="'a_psize_'+index"
|
||||
class="active"
|
||||
>Portionsinhalt</label>
|
||||
</div>
|
||||
<div class="input-field col s4">
|
||||
<input
|
||||
:id="'a_ptype_'+index"
|
||||
v-model="a.portion.type"
|
||||
placeholder="Art"
|
||||
class="validate"
|
||||
max="5"
|
||||
>
|
||||
<label
|
||||
:for="'a_ptype_'+index"
|
||||
class="active"
|
||||
>Portionsbezeichnung</label>
|
||||
</div>
|
||||
<div class="input-field col s4">
|
||||
<input
|
||||
:id="'a_pprice_'+index"
|
||||
v-model.number="a.portion.price"
|
||||
placeholder="Preis"
|
||||
type="number"
|
||||
step="0.01"
|
||||
class="validate"
|
||||
>
|
||||
<label
|
||||
:for="'a_pprice_'+index"
|
||||
class="active"
|
||||
>Portionspreis in €</label>
|
||||
</div>
|
||||
|
||||
<div class="col s12 right">
|
||||
<span class="right">Gesamtpreis {{ a.ContentPrice }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<a
|
||||
class
|
||||
href="#"
|
||||
>Artikel löschen</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
<div
|
||||
ref="fab"
|
||||
class="fixed-action-btn"
|
||||
>
|
||||
<a
|
||||
class="btn-floating btn-large"
|
||||
:class="primaryColor"
|
||||
>
|
||||
<i class="large material-icons">more_vert</i>
|
||||
</a>
|
||||
<ul>
|
||||
<li v-show="view == 'article'">
|
||||
<a
|
||||
href="#"
|
||||
class="btn-floating tooltipped"
|
||||
data-position="left"
|
||||
data-tooltip="Artikelliste speichern"
|
||||
@click="storeArticles"
|
||||
>
|
||||
<i class="material-icons">save</i>
|
||||
</a>
|
||||
</li>
|
||||
<li v-show="view == 'article'">
|
||||
<a
|
||||
href="#"
|
||||
class="btn-floating tooltipped"
|
||||
data-position="left"
|
||||
data-tooltip="Artikel hinzufügen"
|
||||
@click="addArticle"
|
||||
>
|
||||
<i class="material-icons">add</i>
|
||||
</a>
|
||||
</li>
|
||||
<li v-show="view == 'inventory'">
|
||||
<a
|
||||
href="#"
|
||||
class="btn-floating tooltipped"
|
||||
data-position="left"
|
||||
data-tooltip="Inventur zurücksetzen"
|
||||
@click="$refs.inventory.$emit('reset-inventur')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</a>
|
||||
</li>
|
||||
<li v-show="view == 'inventory'">
|
||||
<a
|
||||
href="#"
|
||||
class="btn-floating tooltipped"
|
||||
data-position="left"
|
||||
data-tooltip="Inventur exportieren"
|
||||
@click="$refs.inventory.$emit('export-inventur')"
|
||||
>
|
||||
<i class="material-icons">save</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Calculator from './components/Calculator';
|
||||
import InventoryTable from './components/InventoryTable';
|
||||
import NavigationBar from './components/NavigationBar';
|
||||
import {Article, thawArticle} from './model/article';
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: { InventoryTable, NavigationBar, Calculator },
|
||||
data() {
|
||||
return {
|
||||
articles: [],
|
||||
bon: [],
|
||||
ready: false,
|
||||
caretPosition: 0,
|
||||
fab: null,
|
||||
view: "inventory",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
primaryColor() {
|
||||
switch (this.view) {
|
||||
case "inventory":
|
||||
return "teal";
|
||||
case "calc":
|
||||
return "orange";
|
||||
case "article":
|
||||
return "blue";
|
||||
default:
|
||||
return "red";
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.loadArticles();
|
||||
},
|
||||
mounted() {
|
||||
var fab = this.$refs.fab;
|
||||
this.fab = this.$M.FloatingActionButton.init(fab, {});
|
||||
},
|
||||
methods: {
|
||||
changeView(tabId) {
|
||||
this.view = tabId;
|
||||
},
|
||||
addArticle: function() {
|
||||
this.articles.push(new Article());
|
||||
},
|
||||
storeArticles: function() {
|
||||
this.$http
|
||||
.post(
|
||||
"/api/artikel",
|
||||
JSON.stringify(this.articles)
|
||||
)
|
||||
.then(response => {
|
||||
this.$M.toast({ html: response.body });
|
||||
});
|
||||
},
|
||||
loadArticles: function() {
|
||||
var app = this;
|
||||
this.$http
|
||||
.get("api/artikel/theater")
|
||||
.then(response => {
|
||||
return response.data;
|
||||
})
|
||||
.then(json => {
|
||||
console.log(json);
|
||||
json.forEach(element => {
|
||||
app.articles.push(thawArticle(element));
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
this.$M.toast({ html: "Artikel wurden geladen." });
|
||||
})
|
||||
.then(() => {
|
||||
this.$M.updateTextFields();
|
||||
app.ready = true;
|
||||
});
|
||||
},
|
||||
exportInventur() {
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="row pin-top">
|
||||
<div class="col s12 m4 card darken-4 grey grey-text text-lighten-2">
|
||||
<table class="card-content">
|
||||
<tbody style="max-height:300px">
|
||||
<tr
|
||||
v-for="item in items"
|
||||
v-show="item.count > 0"
|
||||
:key="'bon_'+item.short"
|
||||
>
|
||||
<td class="right-align">
|
||||
{{ item.count }} ×
|
||||
</td>
|
||||
<td>{{ item.name }}</td>
|
||||
<td class="right-align">
|
||||
<TweenedNumber
|
||||
:wert="item.sum"
|
||||
:einheit="'€'"
|
||||
:precision="2"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="white-text">
|
||||
<th class="right-align">
|
||||
<TweenedNumber
|
||||
:wert="totalCount"
|
||||
/>
|
||||
</th>
|
||||
<th>
|
||||
Artikel
|
||||
</th>
|
||||
<th class="right-align">
|
||||
<TweenedNumber
|
||||
:wert="totalSum"
|
||||
:einheit="'€'"
|
||||
:precision="2"
|
||||
/>
|
||||
</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col s12 m8">
|
||||
<div class="row">
|
||||
<div
|
||||
v-for="a in items"
|
||||
:key="'bon_btn_'+a.short"
|
||||
class="col s3"
|
||||
>
|
||||
<button
|
||||
class="waves-effect waves-light btn-large btn-flat col s12"
|
||||
@click="a.count++;a.sum+=a.price;"
|
||||
>
|
||||
{{ a.short }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col s3">
|
||||
<button
|
||||
class="waves-effect waves-light btn-large orange col s12"
|
||||
@click="resetBon"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TweenedNumber from './TweenedNumber';
|
||||
|
||||
export default {
|
||||
name: "Calculator",
|
||||
components: { TweenedNumber },
|
||||
props: {
|
||||
articles: {
|
||||
type: Array,
|
||||
default() { return []; }
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalSum() {
|
||||
return this.items.reduce((total, item) => {
|
||||
return total + item.sum;
|
||||
}, 0);
|
||||
},
|
||||
totalCount() {
|
||||
return this.items.reduce((total, item) => {
|
||||
return total + item.count;
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.resetBon();
|
||||
},
|
||||
methods: {
|
||||
resetBon() {
|
||||
this.items = this.articles.map(a => {
|
||||
return {
|
||||
count: 0,
|
||||
name: a.name,
|
||||
short: a.short,
|
||||
price: a.portion.price,
|
||||
sum: 0,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
.inventory-item {
|
||||
transition: all .2s;
|
||||
}
|
||||
|
||||
.inventory-item>* {
|
||||
transition: all .2s .2s;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<tr
|
||||
style="border: 0;"
|
||||
class="inventory-item"
|
||||
:class="{
|
||||
'teal lighten-5' : item.Sale > 0,
|
||||
'orange darken-2 white-text' : item.Sold %1 !=0,
|
||||
'red darken-2 white-text' : item.Sale < 0,
|
||||
}"
|
||||
>
|
||||
<th
|
||||
class="right-align"
|
||||
>
|
||||
{{ item.article.name }}
|
||||
</th>
|
||||
|
||||
<td
|
||||
v-for="prop in ['start','fetched','end','lost']"
|
||||
:key="item.article.short+'_'+prop"
|
||||
class="border-bottom center-align"
|
||||
:class="{active:classObject.active == prop}"
|
||||
contenteditable
|
||||
@keypress="restrictInput(prop, $event)"
|
||||
@focus="onFocus(prop, $event)"
|
||||
@blur="onBlur(prop, $event)"
|
||||
>
|
||||
{{ item[prop] }}
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="right-align border-bottom border-diagram"
|
||||
>
|
||||
<TweenedNumber
|
||||
:style="{
|
||||
'border-image': 'linear-gradient(to right , rgba(0,150,136,.01) 0%, rgba(0,150,136,.3) '
|
||||
+ item.Sold*100/total
|
||||
+ '%, transparent '
|
||||
+ item.Sold*100/total
|
||||
+'%,transparent 100%) 1'
|
||||
}"
|
||||
:wert="item.Sold"
|
||||
:einheit="item.article.PortionType"
|
||||
:precision="item.PortionPrecision"
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
class="right-align border-bottom"
|
||||
>
|
||||
<TweenedNumber
|
||||
:wert="item.Sale"
|
||||
:einheit="'€'"
|
||||
:precision="2"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TweenedNumber from './TweenedNumber';
|
||||
import './InventoryItem.css';
|
||||
|
||||
export default {
|
||||
name:"InventoryItem",
|
||||
components: {
|
||||
TweenedNumber,
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tweenedSold: 0,
|
||||
tweenedSale: 0,
|
||||
classObject: {active : null}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
SoldAnimated() {
|
||||
return this.tweenedSold.toFixed(this.item.PortionPrecision);
|
||||
},
|
||||
SaleAnimated() {
|
||||
return this.tweenedSale.toFixed(2);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
setCaretPosition(el){
|
||||
if (el != null) {
|
||||
var range = document.createRange();
|
||||
var sel = window.getSelection();
|
||||
range.setStart(el.firstChild, el.innerText.length);
|
||||
range.collapse(true);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
el.focus();
|
||||
}
|
||||
},
|
||||
restrictInput(prop, event){
|
||||
var x = event.key;
|
||||
console.log(prop + " inserting " + x)
|
||||
if (x === "Enter" ) {
|
||||
event.target.blur();
|
||||
}
|
||||
if (isNaN(x) && x != ',') {
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
onFocus(property, event){
|
||||
this.classObject.active = property;
|
||||
var range = document.createRange();
|
||||
var sel = window.getSelection();
|
||||
range.selectNodeContents(event.target);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
},
|
||||
onBlur(property, event) {
|
||||
this.classObject.active = null;
|
||||
var value = event.target.innerText;
|
||||
value = value.replace(',', '.')
|
||||
console.log(property + " left value: " + value);
|
||||
if (!isNaN(value)) {
|
||||
this.item[property] = Number(value);
|
||||
}
|
||||
},
|
||||
onInput(property, event){
|
||||
if (event.inputType == "insertText") {
|
||||
var regex = /\d+\.?\d{0,2}/;
|
||||
if (event.data.match(regex)){
|
||||
var value = event.target.innerText;
|
||||
this.item[property] = Number(value);
|
||||
}
|
||||
}
|
||||
this.setCaretPosition(event.target);
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
table.condensed td,
|
||||
table.condensed th {
|
||||
padding: 5px 5px;
|
||||
}
|
||||
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.border-right {
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.border-diagram {
|
||||
padding: 0;
|
||||
}
|
||||
.border-diagram>div {
|
||||
margin-bottom: -3px;
|
||||
border-radius: 0;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
|
||||
.active {
|
||||
border-color: rgb(38, 166, 154);
|
||||
border-width: 2px;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<table class="table condensed highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="right-align">
|
||||
Artikel
|
||||
</th>
|
||||
<th class="center-align">
|
||||
Beginn
|
||||
</th>
|
||||
<th class="center-align">
|
||||
Zugang
|
||||
</th>
|
||||
<th class="center-align">
|
||||
Ende
|
||||
</th>
|
||||
<th class="center-align">
|
||||
Verlust
|
||||
</th>
|
||||
<th class="right-align">
|
||||
Verkauft
|
||||
</th>
|
||||
<th class="right-align">
|
||||
Summe
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<InventoryItem
|
||||
v-for="(item,index) in inventory"
|
||||
:key="'item-'+index"
|
||||
:item="item"
|
||||
:total="sold"
|
||||
/>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th
|
||||
colspan="6"
|
||||
class="right-align"
|
||||
>
|
||||
Gesamtsumme:
|
||||
</th>
|
||||
<td class="right-align">
|
||||
<TweenedNumber
|
||||
:wert="sales"
|
||||
:precision="2"
|
||||
:einheit="'€'"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import InventoryItem from "./InventoryItem";
|
||||
import TweenedNumber from "./TweenedNumber";
|
||||
import { InventoryArticle } from '../model/inventory_article';
|
||||
import './InventoryTable.css';
|
||||
|
||||
export default {
|
||||
name: "InventoryTable",
|
||||
components: { InventoryItem, TweenedNumber },
|
||||
props: {
|
||||
articles: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inventory: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
meta() {
|
||||
return {
|
||||
datum: new Date().toString(),
|
||||
}
|
||||
},
|
||||
sales() {
|
||||
var total_sales = this.inventory.reduce(function(total, item) {
|
||||
return total + item.Sale;
|
||||
}, 0);
|
||||
return total_sales;
|
||||
},
|
||||
sold() {
|
||||
var total_sold = this.inventory.reduce((total, item) => {
|
||||
return total + item.Sold;
|
||||
},0);
|
||||
return total_sold;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$on('reset-inventur', this.resetInventory);
|
||||
this.$on('export-inventur', this.exportInventory);
|
||||
|
||||
this.resetInventory();
|
||||
},
|
||||
methods: {
|
||||
resetInventory() {
|
||||
this.inventory = this.articles.map(item => {
|
||||
return new InventoryArticle(item);
|
||||
});
|
||||
},
|
||||
getInventurBlob() {
|
||||
let inventur = this.inventory.map(T => {
|
||||
return {
|
||||
name: T.article.name,
|
||||
preis: T.article.portion.price,
|
||||
beginn: T.start,
|
||||
zugang: T.fetched,
|
||||
ende: T.end,
|
||||
verlust: T.lost,
|
||||
verkauft: T.Sold,
|
||||
umsatz: T.Sale
|
||||
};
|
||||
});
|
||||
const data = JSON.stringify({
|
||||
meta: this.meta,
|
||||
data: inventur
|
||||
});
|
||||
const blob = new Blob([data],{type: 'text/json' });
|
||||
return blob;
|
||||
},
|
||||
exportInventory() {
|
||||
var formData = new FormData();
|
||||
formData.append('file', this.getInventurBlob(), 'inventur.json');
|
||||
this.$http
|
||||
.post(
|
||||
"/api/inventur",
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
this.$M.toast({ html: response.body });
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<nav
|
||||
class="nav-extended"
|
||||
:class="primaryColor"
|
||||
>
|
||||
<div class="nav-wrapper container" />
|
||||
<div class="nav-content container">
|
||||
<ul class="tabs tabs-transparent">
|
||||
<li class="tab">
|
||||
<a
|
||||
class="active"
|
||||
@click.stop.prevent="changeTab('inventory', $event)"
|
||||
>Inventur</a>
|
||||
</li>
|
||||
<li class="tab">
|
||||
<a
|
||||
@click.stop.prevent="changeTab('article', $event)"
|
||||
>Artikelliste</a>
|
||||
</li>
|
||||
<li class="tab">
|
||||
<a
|
||||
@click.stop.prevent="changeTab('calc', $event)"
|
||||
>Rechner</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import M from 'materialize-css';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
primaryColor: {
|
||||
type: String,
|
||||
default: "red"
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabs: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
var tabs = document.getElementsByClassName("tabs")[0];
|
||||
M.Tabs.init(tabs, {});
|
||||
this.tabs = M.Tabs.getInstance(tabs);
|
||||
},
|
||||
methods: {
|
||||
changeTab(tabId, event) {
|
||||
this.view = tabId;
|
||||
this.tabs._handleTabClick(event);
|
||||
this.tabs.updateTabIndicator();
|
||||
this.$emit("changed-tab", this.view);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,15 @@
|
||||
.tweened {
|
||||
display: flex;
|
||||
align-content: end;
|
||||
justify-content: end;
|
||||
}
|
||||
.tweened>div:first-child {
|
||||
text-align: right;
|
||||
min-width: 1.5rem;
|
||||
}
|
||||
.tweened>div:nth-child(0n+2) {
|
||||
min-width: 1.5rem;
|
||||
}
|
||||
.tweened>div:nth-child(0n+3) {
|
||||
min-width: 2rem;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="tweened">
|
||||
<div>{{ intAnimated }}</div>
|
||||
<div v-if="precision>0">
|
||||
,{{ deciAnimated }}
|
||||
</div>
|
||||
<div v-else />
|
||||
<div>{{ einheit }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gsap from 'gsap';
|
||||
import './TweenedNumber.css';
|
||||
|
||||
export default {
|
||||
name: "TweenedNumber",
|
||||
props: {
|
||||
wert: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
einheit: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
precision: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
tweenedValue: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
valueAnimated() {
|
||||
return this.tweenedValue.toLocaleString(
|
||||
"de-DE",
|
||||
{
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: this.precision
|
||||
}
|
||||
);
|
||||
},
|
||||
intAnimated() {
|
||||
return this.valueAnimated.split(',')[0];
|
||||
},
|
||||
deciAnimated() {
|
||||
return this.valueAnimated.split(',')[1];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
wert: {
|
||||
handler(newValue) {
|
||||
gsap.to(this.$data, 0.5, {tweenedValue: newValue });
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,16 @@
|
||||
import Vue from "vue";
|
||||
import App from "./App";
|
||||
import 'materialize-css/dist/css/materialize.min.css';
|
||||
import Axios from 'axios';
|
||||
import M from 'materialize-css';
|
||||
|
||||
Vue.prototype.$http = Axios;
|
||||
Vue.prototype.$M = M;
|
||||
|
||||
new Vue({
|
||||
delimiters: ["${", "}"],
|
||||
components: {
|
||||
App
|
||||
},
|
||||
template: "<App/>"
|
||||
}).$mount("#app");
|
||||
@@ -0,0 +1,80 @@
|
||||
export class Article{
|
||||
constructor() {
|
||||
this.name = "";
|
||||
this.short = "";
|
||||
this.dimension = "";
|
||||
this.content = {
|
||||
size : 0,
|
||||
price: 0
|
||||
};
|
||||
this.portion = {
|
||||
size : 0,
|
||||
price : 0,
|
||||
type : ""
|
||||
};
|
||||
}
|
||||
get Name() {
|
||||
return this.name;
|
||||
}
|
||||
set Name(value) {
|
||||
this.name = value;
|
||||
}
|
||||
get Short() {
|
||||
return this.short;
|
||||
}
|
||||
set Short(value) {
|
||||
this.short = value;
|
||||
}
|
||||
get ContentSize() {
|
||||
return this.content.size;
|
||||
}
|
||||
set ContentSize(value) {
|
||||
this.content.size = value;
|
||||
}
|
||||
get Dimension() {
|
||||
return this.dimension;
|
||||
}
|
||||
set Dimension(value) {
|
||||
this.dimension = value;
|
||||
}
|
||||
get PortionSize() {
|
||||
return this.portion.size;
|
||||
}
|
||||
set PortionSize(value) {
|
||||
this.portion.size = value;
|
||||
}
|
||||
get PortionType() {
|
||||
return this.portion.type;
|
||||
}
|
||||
set PortionType(value) {
|
||||
this.portion.type = value;
|
||||
}
|
||||
get PortionPrice() {
|
||||
return this.portion.price;
|
||||
}
|
||||
set PortionPrice(value) {
|
||||
this.portion.price = value;
|
||||
}
|
||||
get ContentPrice() {
|
||||
return this.Portions * this.portion.price;
|
||||
}
|
||||
get Portions() {
|
||||
return this.content.size / (this.portion.size || 1);
|
||||
}
|
||||
}
|
||||
|
||||
Article.thaw = function (json) {
|
||||
var article = new Article();
|
||||
article.id = json.id;
|
||||
article.Name = json.name;
|
||||
article.Short = json.short;
|
||||
article.Dimension = json.dimension;
|
||||
article.ContentSize = json.content.size;
|
||||
article.PortionPrice = json.portion.price;
|
||||
article.PortionSize = json.portion.size;
|
||||
article.PortionType = json.portion.type;
|
||||
|
||||
return article;
|
||||
};
|
||||
|
||||
export const thawArticle = Article.thaw;
|
||||
@@ -0,0 +1,72 @@
|
||||
export class InventoryArticle {
|
||||
constructor(article) {
|
||||
this.article = article;
|
||||
this.end = 0;
|
||||
this.start = 0;
|
||||
this.fetched = 0;
|
||||
this.lost = 0;
|
||||
}
|
||||
get StartPortions() {
|
||||
var countFull = Math.floor(this.start);
|
||||
var fullPortions = countFull * this.article.Portions;
|
||||
var rest = this.start - countFull;
|
||||
var restPortions = rest / this.article.PortionSize;
|
||||
|
||||
return fullPortions + restPortions;
|
||||
}
|
||||
get FetchedPortions() {
|
||||
var countFull = Math.floor(this.fetched);
|
||||
var fullPortions = countFull * this.article.Portions;
|
||||
var rest = this.fetched - countFull;
|
||||
var restPortions = rest / this.article.PortionSize;
|
||||
|
||||
return fullPortions + restPortions;
|
||||
}
|
||||
get EndPortions() {
|
||||
var countFull = Math.floor(this.end);
|
||||
var fullPortions = countFull * this.article.Portions;
|
||||
var rest = this.end - countFull;
|
||||
var restPortions = rest / this.article.PortionSize;
|
||||
|
||||
return fullPortions + restPortions;
|
||||
}
|
||||
get LostPortions() {
|
||||
var countFull = Math.floor(this.lost);
|
||||
var fullPortions = countFull * this.article.Portions;
|
||||
var rest = this.lost - countFull;
|
||||
var restPortions = rest / this.article.PortionSize;
|
||||
|
||||
return fullPortions + restPortions;
|
||||
}
|
||||
get Sold() {
|
||||
var adds = this.StartPortions + this.FetchedPortions;
|
||||
var subs = this.EndPortions + this.LostPortions;
|
||||
return adds - subs;
|
||||
}
|
||||
get Sale() {
|
||||
return this.Sold * this.article.PortionPrice;
|
||||
}
|
||||
get StepSize() {
|
||||
var singlePack = this.article.Portions == 1;
|
||||
return singlePack ? 1 : 0.05;
|
||||
}
|
||||
get PortionPrecision() {
|
||||
var singlePack = this.article.Portions == 1;
|
||||
return singlePack ? 0 : 2;
|
||||
}
|
||||
reset() {
|
||||
this.start = 0;
|
||||
this.fetched = 0;
|
||||
this.end = 0;
|
||||
this.lost = 0;
|
||||
}
|
||||
}
|
||||
|
||||
InventoryArticle.thaw = function (json) {
|
||||
this.name = json.name;
|
||||
this.start = json.start;
|
||||
this.fetched = json.fetched;
|
||||
this.end = json.end;
|
||||
this.lost = json.lost;
|
||||
};
|
||||
export const thawInventoryArticle = InventoryArticle.thaw;
|
||||
Reference in New Issue
Block a user