Vue. js автоматическое обновление вычислено - PullRequest
0 голосов
/ 15 апреля 2020

Я новичок в Vue. js. У меня есть 2 компонента, один как строки, а второй сумма всех строк выше. Проблема в том, что суммы не обновляются автоматически, даже если они установлены как вычисленные. Найдите ниже мой код:

let s = []
Vue.component('subitem-row', {
                props: ['subitem', 'crt', 'si'],
                template: `
                    <tr>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>EPE</label>
                                <input v-model="subitem.eprice" @change="calculateSubitem();">
                            </div>
                            <span v-else>{{subitem.eprice}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si" @change="parseExpresion(); calculateSubitem();">
                                <label>Anzahl</label>
                                <input v-model="subitem.qtytext">
                            </div>
                            <span v-else>{{subitem.qtytext}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>Faktor</label>
                                <input v-model="subitem.factor" @change="calculateSubitem();">
                            </div>
                            <span v-else>{{subitem.factor}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>EK</label>
                                <input v-model="subitem.tp1" readonly>
                            </div>
                            <span v-else>{{subitem.tp1}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>Marge</label>
                                <input v-model="subitem.margin" readonly>
                            </div>
                            <span v-else>{{subitem.margin}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>VK</label>
                                <input v-model="subitem.tp2" readonly>
                            </div>
                            <span v-else>{{subitem.tp2}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>Rabatt</label>
                                <input v-model="subitem.discount" @change="calculateSubitem();">
                            </div>
                            <span v-else>{{subitem.discount}}</span>
                        </td>
                    </tr>
                `,
                methods: {
                    calculateSubitem: function() {
                        if(this.subitem.hasOwnProperty('eprice') && !isNaN(this.subitem.eprice)) {
                            if(!this.subitem.hasOwnProperty('factor') || isNaN(this.subitem.factor))
                                this.subitem.factor = 1
                            if(!this.subitem.hasOwnProperty('discount') || isNaN(this.subitem.discount))
                                this.subitem.discount = 0
                            if(!this.subitem.hasOwnProperty('qty') || isNaN(this.subitem.qty))
                                this.subitem.qty = 0

                            let discount = 1 - (parseFloat(this.subitem.discount.toString().split(',').join('.')) / 100),
                            margin = 0
                            this.subitem.dprice = (this.subitem.eprice.split(',').join('.') * discount)
                            this.subitem.tp1 = (this.subitem.dprice * this.subitem.qty * this.subitem.factor)
                            this.subitem.margin = (this.subitem.tp1 * (parseFloat(margin) / 100))
                            this.subitem.tp2 = (this.subitem.tp1 + this.subitem.margin)
                            this.$forceUpdate()//{TODO} - find an alternative to $forceUpdate
                        }
                    },
                    parseExpresion: function() {
                        this.subitem.qty = parseFloat(this.subitem.qtytext.split(',').join('.')) || 0
                        this.$nextTick(function () {
                            this.calculateSubitem()
                        })
                    }
                },
            })

            Vue.component('subitem-row-sum', {
                props: ['sisum'],
                template: `
                    <tr>
                        <td colspan="3">SUM</td>
                        <td>
                            <span>{{sisum.tp1}}</span>
                        </td>
                        <td>
                            <span>{{sisum.margin}}</span>
                        </td>
                        <td>
                            <span>{{sisum.tp2}}</span>
                        </td>
                        <td></td>
                    </tr>
                `,
            })

            Vue.component('html-textarea',{
                template: `<div class="html-textarea" contenteditable="true" @input="updateHTML" rows="3"></div>`,
                props: ['value'],
                mounted: function () {
                    this.$el.innerHTML = this.value;
                },
                methods: {
                    updateHTML: function(e) {
                        this.$emit('input', e.target.innerHTML)
                    }
                }
            })

            const app = new Vue({
                el: '#app',
                data: {
                    obj: s, // main object for loading
                    ii: 0, // items index
                    si: 0, // subitems index
                },
                computed: {
                    items: function() {
                        return this.obj
                    },
                    row: function() {
                        if(!this.items.length)
                            this.items.push({})
                        return this.items[this.ii]
                    },
                    subitems: function() {
                        if(!this.row.hasOwnProperty('subitems'))
                            this.row.subitems = [{}]
                        return this.row.subitems
                    },
                    srow: function() {
                        if(!this.subitems.length)
                            this.subitems.push({})
                        return this.subitems[this.si]
                    },
                    sisum: function() {
                        let sisum = {
                            tp1: 0,
                            tp2: 0,
                            margin: 0
                        }
                        this.subitems.forEach(si => {
                            sisum.tp1 += si.tp1 || 0
                            sisum.tp2 += si.tp2 || 0
                            sisum.margin += si.margin || 0
                        })
                        return sisum
                    }
                },
                methods: {
                    setIi: function(i) {
                        this.ii = i
                    },
                    setSi: function(i) {
                        this.si = i
                    },
                    addItemRow: function() {
                        this.items.push({})
                        this.setIi(this.items.length - 1)
                        this.$nextTick(function () {
                            document.getElementById('items').scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"})
                        })
                    },
                    addSubitemRow: function() {
                        this.subitems.push({})
                        this.setSi(this.subitems.length - 1)
                        this.$nextTick(function () {
                            document.getElementById('subitems').scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"})
                        })
                    },
                },
            })
body {
                padding: 0;
                margin: 0;
                font-size: 14px;
                font-family: 'Courier New', monotype;
            }
            .tables-container {
                display: flex;
                flex-direction: column;
                height: 100vh;
            }
            .table-container {
                height: calc(50% - 2px);
                overflow: auto;
                margin: 0 0 30px 0;
                background: #eee
            }
            .table-container td {
                padding: 3px 6px;
                width: 60px;
                height: 30px;
                border-bottom: 1px solid black;
                vertical-align: middle;
                font-size: 1.1rem;
            }
            .html-textarea-container {
                position: relative;
            }
            .html-textarea {
                background: white;
                border: 1px solid black;
            }
            .btn-group-edit {
                position: absolute;
                right: 2px;
                bottom: 2px;
                /*display: none;*/
                z-index: 1;
            }
            /*.html-textarea:hover + .btn-group-edit,
            .html-textarea:focus + .btn-group-edit {
                display: block;
            }*/
            .table-container .form-group > input,
            .table-container .form-group > textarea,
            .table-container .form-group > select,
            .html-textarea {
                width: calc(200px - 12px);
                height: calc(30px - 6px);
                font-size: 1.1rem;
                font-family: 'Courier New', monotype;
                word-wrap: break-word;
            }
            .table-container .form-group > textarea,
            .html-textarea {
                width: calc(600px - 12px);
            }
            /*.table-container .form-group > textarea:focus,
            .html-textarea {
                height: calc(150px - 6px);
            }*/
            .btn-add {
                width: 30px;
                height: 30px;
                padding: 4px 0;
            }
<div id="app">
            <div class="tables-container">
                <div class="table-container table-subitems">
                    <table id="subitems">
                        <tr is="subitem-row" v-for="(subitem, i) in subitems" v-bind:subitem="subitem" v-bind:key="i" v-bind:crt="i" v-bind:si="si" v-on:click.native="setSi(i)"></tr>
                        <tr is="subitem-row-sum" v-bind:sisum="sisum"></tr>
                    </table>
                </div>
                <button class="btn-add" v-on:click="addSubitemRow">+</button>
            </div>
        </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

При добавлении чисел в поля "EPE" и "Anzahl" остальные поля заполняются автоматически, а суммы - нет. В чем моя ошибка здесь? Будучи вычисляемым свойством, я думал, что оно будет реагирующим и само обновляется, основываясь на текущем наборе подпунктов.

Спасибо.

1 Ответ

0 голосов
/ 15 апреля 2020

Добро пожаловать в переполнение стека. Я попытался исправить ваш код, но мне было очень трудно это понять, и я думаю, что вы злоупотребляете вычисленными свойствами, например

items: function() {
    return this.obj
},

Вы могли бы просто сослаться на this.obj вместо this.items, так как вы слишком усложняете свой код.

Ваша основная проблема - инициализация данных, Since Vue doesn’t allow dynamically adding root-level reactive properties, you have to initialize Vue instances by declaring all root-level reactive data properties upfront, even with an empty value Объявление реактивных свойств

Итак, я инициализировал s как это:

let s = [{
  subitems: [{
    eprice: "0",
    factor: 0,
    discount: 0,
    qty: 0,
    dprice: 0,
    tp1: 0,
    margin: 0,
    tp2: 0
  }]
}];

let s = [{
  subitems: [{
    eprice: "0",
    factor: 0,
    discount: 0,
    qty: 0,
    dprice: 0,
    tp1: 0,
    margin: 0,
    tp2: 0
  }]
}];
Vue.component('subitem-row', {
  props: ['subitem', 'crt', 'si'],
  template: `
                    <tr>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>EPE</label>
                                <input v-model="subitem.eprice" @change="calculateSubitem();">
                            </div>
                            <span v-else>{{subitem.eprice}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si" @change="parseExpresion(); calculateSubitem();">
                                <label>Anzahl</label>
                                <input v-model="subitem.qtytext">
                            </div>
                            <span v-else>{{subitem.qtytext}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>Faktor</label>
                                <input v-model="subitem.factor" @change="calculateSubitem();">
                            </div>
                            <span v-else>{{subitem.factor}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>EK</label>
                                <input v-model="subitem.tp1" readonly>
                            </div>
                            <span v-else>{{subitem.tp1}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>Marge</label>
                                <input v-model="subitem.margin" readonly>
                            </div>
                            <span v-else>{{subitem.margin}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>VK</label>
                                <input v-model="subitem.tp2" readonly>
                            </div>
                            <span v-else>{{subitem.tp2}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>Rabatt</label>
                                <input v-model="subitem.discount" @change="calculateSubitem();">
                            </div>
                            <span v-else>{{subitem.discount}}</span>
                        </td>
                    </tr>
                `,
  methods: {
    calculateSubitem: function() {
      if (this.subitem.hasOwnProperty('eprice') && !isNaN(this.subitem.eprice)) {
        if (!this.subitem.hasOwnProperty('factor') || isNaN(this.subitem.factor))
          this.subitem.factor = 1
        if (!this.subitem.hasOwnProperty('discount') || isNaN(this.subitem.discount))
          this.subitem.discount = 0
        if (!this.subitem.hasOwnProperty('qty') || isNaN(this.subitem.qty))
          this.subitem.qty = 0

        let discount = 1 - (parseFloat(this.subitem.discount.toString().split(',').join('.')) / 100),
          margin = 0
        this.subitem.dprice = (this.subitem.eprice.split(',').join('.') * discount)
        this.subitem.tp1 = (this.subitem.dprice * this.subitem.qty * this.subitem.factor)
        this.subitem.margin = (this.subitem.tp1 * (parseFloat(margin) / 100))
        this.subitem.tp2 = (this.subitem.tp1 + this.subitem.margin)
        this.$forceUpdate() //{TODO} - find an alternative to $forceUpdate
      }
    },
    parseExpresion: function() {
      this.subitem.qty = parseFloat(this.subitem.qtytext.split(',').join('.')) || 0
      this.$nextTick(function() {
        this.calculateSubitem()
      })
    }
  },
})

Vue.component('subitem-row-sum', {
  props: ['sisum'],
  template: `
                    <tr>
                        <td colspan="3">SUM</td>
                        <td>
                            <span>{{sisum.tp1}}</span>
                        </td>
                        <td>
                            <span>{{sisum.margin}}</span>
                        </td>
                        <td>
                            <span>{{sisum.tp2}}</span>
                        </td>
                        <td></td>
                    </tr>
                `,
})

Vue.component('html-textarea', {
  template: `<div class="html-textarea" contenteditable="true" @input="updateHTML" rows="3"></div>`,
  props: ['value'],
  mounted: function() {
    this.$el.innerHTML = this.value;
  },
  methods: {
    updateHTML: function(e) {
      this.$emit('input', e.target.innerHTML)
    }
  }
})

const app = new Vue({
  el: '#app',
  data: {
    obj: s, // main object for loading
    ii: 0, // items index
    si: 0, // subitems index
  },
  computed: {
    items: function() {
      return this.obj
    },
    row: function() {
      if (!this.items.length)
        this.items.push({})
      return this.items[this.ii]
    },
    subitems: function() {
      return this.row.subitems
    },
    srow: function() {
      if (!this.subitems.length)
        this.subitems.push({})
      return this.subitems[this.si]
    },
    sisum: function() {
      debugger;
      let sisum = {
        tp1: 0,
        tp2: 0,
        margin: 0
      }


      this.subitems.forEach(si => {
        sisum.tp1 += si.tp1 || 0
        sisum.tp2 += si.tp2 || 0
        sisum.margin += si.margin || 0
      })
      return sisum;
    }
  },
  methods: {
    setIi: function(i) {
      this.ii = i
    },
    setSi: function(i) {
      this.si = i
    },
    addItemRow: function() {
      this.items.push({})
      this.setIi(this.items.length - 1)
      this.$nextTick(function() {
        document.getElementById('items').scrollIntoView({
          behavior: "smooth",
          block: "end",
          inline: "nearest"
        })
      })
    },
    addSubitemRow: function() {
      this.subitems.push({})
      this.setSi(this.subitems.length - 1)
      this.$nextTick(function() {
        document.getElementById('subitems').scrollIntoView({
          behavior: "smooth",
          block: "end",
          inline: "nearest"
        })
      })
    },
  },
})
body {
  padding: 0;
  margin: 0;
  font-size: 14px;
  font-family: 'Courier New', monotype;
}

.tables-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.table-container {
  height: calc(50% - 2px);
  overflow: auto;
  margin: 0 0 30px 0;
  background: #eee
}

.table-container td {
  padding: 3px 6px;
  width: 60px;
  height: 30px;
  border-bottom: 1px solid black;
  vertical-align: middle;
  font-size: 1.1rem;
}

.html-textarea-container {
  position: relative;
}

.html-textarea {
  background: white;
  border: 1px solid black;
}

.btn-group-edit {
  position: absolute;
  right: 2px;
  bottom: 2px;
  /*display: none;*/
  z-index: 1;
}


/*.html-textarea:hover + .btn-group-edit,
            .html-textarea:focus + .btn-group-edit {
                display: block;
            }*/

.table-container .form-group>input,
.table-container .form-group>textarea,
.table-container .form-group>select,
.html-textarea {
  width: calc(200px - 12px);
  height: calc(30px - 6px);
  font-size: 1.1rem;
  font-family: 'Courier New', monotype;
  word-wrap: break-word;
}

.table-container .form-group>textarea,
.html-textarea {
  width: calc(600px - 12px);
}


/*.table-container .form-group > textarea:focus,
            .html-textarea {
                height: calc(150px - 6px);
            }*/

.btn-add {
  width: 30px;
  height: 30px;
  padding: 4px 0;
}
<div id="app">
  <div class="tables-container">
    <div class="table-container table-subitems">
      <table id="subitems">
        <tr is="subitem-row" v-for="(subitem, i) in subitems" v-bind:subitem="subitem" v-bind:key="i" v-bind:crt="i" v-bind:si="si" v-on:click.native="setSi(i)"></tr>
        <tr is="subitem-row-sum" v-bind:sisum="sisum"></tr>
      </table>
    </div>
    <button class="btn-add" v-on:click="addSubitemRow">+</button>
  </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...