JSFiddle - React, Tailwind, and code Playground
by tidy
HTML
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="https://unpkg.com/[email protected]/min/moment.min.js"></script>
<script src="https://unpkg.com/[email protected]/lodash.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/flatpickr.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/flatpickr.min.css">
<div id="offer">
<div id="info">
<button @click="toggleJson" style="float: right;">JSON</button>
<h2>Tuotteet</h2>
<p>Demo tuotteen reaaliaikaisesta hintalaskennasta. Ohje:</p>
<ol>
<li>Aloita valitsemalla tuotteen "Esimerkki". Eri esimerkit viittaavat tilattuihin palveluihin.</li>
<li>Valitse vuokrauksen alku ja päätös päivä</li>
<li>Täytä hintakentät</li>
</ol>
<button @click="addProduct">Lisää tuote</button>
</div>
<product-row v-for="(product, idx) in products"
class="product"
:idx="idx"
:prod="product"
v-on:update="updateProduct"
v-on:remove="removeProduct"
></product-row>
<pre id="json" v-text="productJson" :class="{ 'hidden': jsonHidden }"></pre>
</div>
<script type="text/x-template" id="product-row-template">
<div class="row">
<div class="desc">
<b v-text="'Tuote ' + (idx + 1)"></b><br>
<select v-model="prod.category">
<option :value="null" disabled>Valitse esimerkki</option>
<option v-for="cat in categories" :value="cat.id" v-text="cat.name"></option>
</select>
</div>
<div class="prices">
<div>
<label>Vuokrauksen alku</label>
<input type="text" :class="'datepicker-' + prod.id" v-model="prod.dateStart" placeholder="2017-05-01" @change="setPriceUnit">
</div>
<div>
<label>Vuokrauksen päätös</label>
<input type="text" :class="'datepicker-' + prod.id" v-model="prod.dateEnd" placeholder="2017-05-30" @change="setPriceUnit">
...
CSS
body {
font-family: sans-serif;
}
#info {
margin-bottom: 20px;
}
#json {
width: 100%;
border: 2px solid red;
padding: 10px;
box-sizing: border-box;
}
#json.hidden {
display: none;
}
.row {
display: flex;
width: 100%;
flex-direction: row;
border-top: 1px solid black;
margin-bottom: 10px;
padding-top: 10px;
}
.row:first-child {
border-top: 0;
}
.row > div {
margin-left: 5px;
padding-left: 10px;
padding-right: 10px;
border-right: 1px solid black;
}
.row > div:last-child {
border: 0;
}
.row .prices {
display: flex;
flex-direction: column;
}
.row .prices > div {
display: flex;
flex-direction: column;
padding-bottom: 10px;
}
.row .row-btn {
flex-grow: 1;
text-align: right;
}
JavaScript
// core packages
// https://unpkg.com/[email protected]/dist/vue.min.js
// https://unpkg.com/[email protected]/min/moment.min.js
// https://unpkg.com/[email protected]/lodash.min.js
// extras
// https://unpkg.com/[email protected]/dist/flatpickr.min.css
// https://unpkg.com/[email protected]/dist/flatpickr.min.js
var productDefaults = {
id: null,
category: null,
dateStart: null,
dateEnd: null,
priceUnit: null,
pricePerUnit: null,
deliveryPrice: null,
returnPrice: null,
installPrice: null,
uninstallPrice: null,
}
Vue.component('product-row', {
template: '#product-row-template',
data: function() {
return {
categories: [
{ id: 'cat1', name: 'Nouto/palautus', delivery: false, install: false },
{ id: 'cat2', name: 'Toimitus', delivery: true, install: false },
{ id: 'cat3', name: 'Toimitus + Asennus', delivery: true, install: true },
],
pricing: [
{ id: 'day', name: 'Päivähinta', range: 'päivä' },
{ id: 'week', name: 'Viikkohinta', range: 'viikko' },
{ id: 'month', name: 'Kuukausihinta', range: 'kuukausi' },
],
}
},
props: {
idx: Number,
prod: Object,
},
watch: {
prod: {
handler: function(prod){
this.$emit('update', this.idx, prod)
},
deep: true,
},
},
computed: {
productCategory: function(){
if (!this.prod.category) {
return null
}
return _.find(this.categories, ['id', this.prod.category])
},
rentDays: function(){
var me = this
if (!me.prod.dateStart || !me.prod.dateEnd) {
return null
}
var days = moment(me.prod.dateEnd, 'YYYY-MM-DD')
.diff(moment(me.prod.dateStart, 'YYYY-MM-DD'), 'days')
if (days < 1) {
return null
}
return days
},
priceUnitByDays: function() {
var me = this
if (!me.rentDays) {
return null
}
if (me.rentDays < 7) {
return 'day'
} else if (me.rentDays...