Vuex双向可编辑绑定

时间:2018-12-14 12:06:50

标签: vue.js vue-component vuex

我有一份订单位置清单。每个职位都有产品名称,数量,价格,总和。此外,还有一些输入字段,其中包含以百分比和绝对值(例如美元)为单位的预付款项。我使用吸气剂进行绝对预付款。因此,为了编辑输入的绝对值并使百分数为最新,我必须通过百分数值在存储中对其进行更新,即我必须计算百分数值,其中绝对值等于新的百分数(只需在输入字段中输入)。但是,这是一个问题:当我在绝对值的输入字段中输入数字时,百分比的计算会导致对绝对吸气剂的重新计算,从而使回车符返回到开头(好的,这里可以使用反跳功能),但是我也觉得这是错误的,并且发生了一些违反逻辑的事情。

对不起,一团糟。代码:

store.js:

Vue.use(Vuex);

let product = {
    order: 1,
    name: '',
    size: '',
    content: '',
    price: 0,
    number: 1,
    discount: '',
    guarantee: 0,
    promotion: 0,
    location: '',
    sum: 0,
};
export default new Vuex.Store({
    strict: true,
    state: {
        products: [
            Object.assign({}, product),
        ],
        form: {
            prepaymentInPercent: 100,
        },

    },
    getters: {
        total(state) {
            return +(state.products.length > 0 && state.products.reduce(function (acc, cur) {
                return acc + cur.sum;
            }, 0));
        },
        rest(state, getters) {
            return Math.round(getters.total - getters.prepaymentInRub);
        },
        prepaymentInRub(state, getters) {
            return Math.round(getters.total * state.form.prepaymentInPercent / 100);
        },
    },
    mutations: {
        [ADD_PRODUCT](state, product) {
            state.products.push(product);
        },
        [UPDATE_PRODUCT](state, {updatedProduct, index}) {
            state.products.splice(index, 1, updatedProduct);
        },
        [DELETE_PRODUCT](state, index) {
            state.products.splice(index, 1);
        },
        [UPDATE_FORM](state, updatedFormFields) {
            state.form = Object.assign({}, state.form, updatedFormFields);
        },
    },
    actions: {
        addProduct({commit}) {
            let newProduct = Object.assign({}, product, {'order': product.order++});
            commit(ADD_PRODUCT, newProduct);
        },
        updateProduct: _.debounce(function ({commit}, {product, index}) {
            let updatedProduct = Object.assign({}, product);
            commit(UPDATE_PRODUCT, {updatedProduct, index});
        }, 200),
        deleteProduct({commit, state}, index) {
            state.products.length > 1 && commit(DELETE_PRODUCT, index);
        },
        updatePrepaymentInRub({commit, getters}, rubles) {
            let prepaymentInPercent = Math.round(rubles / getters.total * 100);
            commit(UPDATE_FORM, {prepaymentInPercent});
        },
    },
});

OrderForm.vue

<template>
    <form>
        <label>Prepayment, %
            <input id="prepaymentInPercent" type="number" min="0" max="100" v-model="prepaymentInPercent">
        </label>
        <label>Prepayment, rubles
            <input id="prepaymentInRub" min="0" type="number" :max="total" v-model="prepaymentInRubles">
        </label>
    </form>
</template>

<script>
    import {UPDATE_FORM} from "../mutation-types";
    import {mapGetters} from 'vuex';

    export default {
        name: "OrderForm",
        computed: {
            'prepaymentInRubles': {
                get() {
                    return this.$store.getters.prepaymentInRub;
                },
                set(value) {
                    this.$store.dispatch('updatePrepaymentInRub', value);
                }
            },
            ...mapGetters(['total']),
            'prepaymentInPercent': {
                get() {
                    return this.$store.state.form.prepaymentInPercent;
                },
                set(value) {
                    return this.$store.commit(UPDATE_FORM, {
                        'prepaymentInPercent': value
                    });
                }
            }
        },
    }
</script>

ProductRow.vue

<template>
    <tr>
        <td colspan="2" class="id">{{indexFrom1}}</td>
        <Editable v-model="product.name"/>
        <Editable v-model="product.size"/>
        <Editable v-model="product.content"/>
        <Editable v-model.number="product.price"/>
        <Editable v-model.number="product.number"/>
        <Editable v-model="product.discount"/>
        <td>
            <select v-model.number="product.promotion">
                <option selected value="0">No</option>
                <optgroup label="forNewsettlers">
                    <option value="5">-5%</option>
                    <option value="10">-10%</option>
                    <option value="15">-15%</option>
                </optgroup>
            </select>
        </td>
        <td>{{sum}}</td>
        <Editable v-model.number="product.guarantee"/>
        <td>
            <select v-model="product.location">
                <option selected value="SERVICE">Service</option>
                <option value="STOREHOUSE">Storehouse</option>
                <option value="SO">Special Order</option>
                <option value="from 1st shop">exhibition sample from 1st shop</option>
            </select>
        </td>
        <td>
            <span class="table-remove" @click="removeProduct(index)">Удалить</span>
        </td>
    </tr>
</template>

<script>
    import Editable from './EditableCell';

    export default {
        components: {
            Editable,
        },
        name: 'ProductRow',
        props: {
            initialProduct: Object,
            index: Number,
        },
        data() {
            return {
                product: Object.assign({}, this.initialProduct),
            };

        },
        computed: {
            sum() {
                let discountedPrice = this.isDiscountInPercent(this.product.discount) ?
                    this.product.price * this.getCoeffFromPercent(this.product.discount) :
                    this.product.price - this.product.discount;
                let priceWithPromotion = discountedPrice * this.getCoeffFromPercent(this.product.promotion);
                let sum = Math.round(priceWithPromotion * this.product.number);
                return sum > 0 ? sum : 0;
            },
            indexFrom1() {
                return this.index + 1;
            },
        },
        methods: {
            getCoeffFromPercent(percent) {
                return 1 - parseInt(percent) / 100;
            },
            isDiscountInPercent(discount) {
                return ~discount.indexOf('%') ? true : false;
            },
            removeProduct(index) {
                this.$store.dispatch('deleteProduct', index);
            }
        },
        watch: {
            sum() {
                this.product.sum = this.sum;
            },
            product: {
                handler(product) {
                    this.$store.dispatch('updateProduct', {
                        product: product,
                        index: this.index,
                    });
                },
                deep: true,
            },
        },
    };
</script>

1 个答案:

答案 0 :(得分:1)

我认为您的逻辑很好。状态中的一个值具有两个不同的表示形式和可能的突变。

为避免出现光标问题,我将更改您正在输入中监听的事件。请记住,v-model="prepaymentInPercent"等效于@input="prepaymentInPercent = $event.target.value" :value="prepaymentInPercent"input事件会在每次字符更改时触发,但是您可以将其替换为change事件,它将在模糊或输入时触发。例如@change="prepaymentInPercent = $event.target.value" :value="prepaymentInPercent"

事实证明,有一个lazy修饰符会自动执行此操作,因此您只需将.lazy添加到v模型中即可。