Vant - 自定义树结构选择器设计

原创
2020/08/22 11:52
阅读数 2.3W


<template>
    <div>
        <van-field
                readonly
                clickable
                :placeholder="placeholder"
                :disabled="disabled"
                :clearable="clearable"
                :rules="rules"
                :left-icon="prefixIcon"
                input-align="left"
                :required="required"
                :value="currentValue.name"
                :icon="suffixIcon"
                :label="label"
                @click.native="selectClick">
            <template #label>
                <div>
                    <span>{{ label }}</span>
                </div>
            </template>

            <template #right-icon>
                <div @click.stop="openCamera">
                    <van-icon name="scan" class="scan-icon"/>
                </div>
            </template>
        </van-field>
        <van-popup v-model="showPicker"
                   position="bottom">
            <van-picker ref="picker"
                        show-toolbar
                        :title="title"
                        :visible-item-count="7"
                        :loading="pulling"
                        :columns="columns"
                        @cancel="handleCancel"
                        @change="onChange"
                        @confirm="handleConfirm">
                <template #columns-top>
                    <van-steps inactive-icon="arrow" active-icon="location" :active="selectedItem.length-1" @click-step="stepClick" style="padding-bottom: 30px">
                        <van-step v-if="selectedItem.length>0">重选</van-step>
                        <van-step v-for="(item,index) in selectedItem" :key="item.id">
                            {{item.item.name | textAbbreviate(4)}}
                        </van-step>
                    </van-steps>
                </template>
            </van-picker>
        </van-popup>
    </div>
</template>

<script>

    import {mapGetters} from 'vuex'
    import {paging} from "../../../api/device-info";

    export default {
        name: 'DeviceSelect',
        mounted() {
            this.loadData();
        },
        components: {},
        props: {
            value: {
                required: true,
                default() {
                    return {};
                }
            },
            loading: {
                type: Boolean,
                default: false
            },
            command: {
                default: ''
            },
            disabled: {
                type: Boolean,
                default: false
            },
            label: null,
            rules: null,
            clearable: {
                type: Boolean,
                default: false
            },
            required: {
                type: Boolean,
                default: false
            },
            prefixIcon: null,
            suffixIcon: null,
            placeholder: null,
        },
        data() {
            let value = this.value || {};
            return {
                showPicker: false,
                pulling: false,
                currentValue: {
                    ...value
                },
                columns: [],
                deep: 0,
                selectedItem: [],
                params: {
                    parentId: 0
                },
                device: this.deviceDetail
            }
        },
        computed: {
            title() {
                if (!this.selectedItem || this.selectedItem.length <= 0) {
                    if (!this.currentValue || !this.currentValue.id) {
                        return '';
                    }
                    return this.currentValue.name;
                }
                return this.selectedItem[this.selectedItem.length - 1].item.name;
            },
            ...mapGetters([
                'deviceDetail'
            ])
        },
        methods: {
            dataMapper(item) {
                return {
                    text: item.name,
                    data: item,
                }
            },
            loadData() {
                this.pulling = true;
                paging(this.params).then(res => {
                    this.pulling = false;
                    if (res.code !== 'ok') {
                        this.$toast.fail(res.msg || "设备数据获取失败");
                        return;
                    }
                    if (!res.data || res.data.length <= 0) {
                        return;
                    }
                    this.deep++;
                    this.columns = (res.data.list || []).map(this.dataMapper);
                    this.columns.unshift({
                        text: '请选择',
                        data: null
                    })
                })
            },
            /**
             * field 点击事件
             * @date 2020/4/8 14:53
             */
            selectClick() {
                if (this.disabled) {
                    return;
                }
                this.showPicker = true;
            },
            /**
             * 步骤条点击事件
             * @date 2020/4/8 14:52
             */
            stepClick(index) {
                if (index === 0) {
                    this.deep = 0;
                    this.columns = [];
                    this.selectedItem = [];
                    this.params = {
                        parentId: 0
                    };
                    this.loadData();
                    return;
                }

                let selectedItemElement = this.selectedItem[index - 1];
                this.params.parentId = selectedItemElement.item.id;
                this.selectedItem = this.selectedItem.filter((val, i) => {
                    return i < index;
                });
                this.deep = selectedItemElement.deep;
                this.loadData();
            },
            /**
             * picker 改变事件
             * @date 2020/4/8 14:53
             */
            onChange(picker, values) {
                if (!values.data || !values.data.id) {
                    return;
                }
                this.updateSelectedItem(values.data, this.deep);
                this.params.parentId = values.data.id;
                // this.loadData();
            },
            /**
             * 更新选择项
             * @date 2020/4/8 14:19
             */
            updateSelectedItem(item, deep) {
                if (!item || !item.id) {
                    return;
                }

                let index = this.selectedItem.findIndex(val => {
                    return val.deep === deep;
                });

                if (index !== -1) {
                    this.selectedItem = this.selectedItem.filter(val => {
                        return val.deep < deep;
                    });
                }

                this.selectedItem.push({
                    item,
                    deep
                });

            },
            /**
             * 取消
             * @date 2020/4/8 14:54
             */
            handleCancel() {
                this.showPicker = false;
            },
            /**
             * 确认选择
             * @date 2020/4/8 14:54
             */
            handleConfirm(value, index) {
                if (!this.selectedItem || this.selectedItem.length <= 0) {
                    if (!this.currentValue || !this.currentValue.id) {
                        this.$toast.fail('请选择');
                        return;
                    }
                    this.showPicker = false;
                    return;
                }

                this.currentValue = {
                    ...this.selectedItem[this.selectedItem.length - 1].item
                };

                this.$emit('input', this.currentValue);
                this.$emit('change', this.currentValue);
                this.showPicker = false;
            },
            openCamera() {
                this.$android.qrCodeScan((data) => {
                    if (!data || !data.id) {
                        return;
                    }
                    this.currentValue = {
                        ...data
                    };
                    this.$emit('input', this.currentValue);
                    this.$emit('change', this.currentValue)
                })
            }
        },
        watch: {
            value: function (val) {
                if (!val) {
                    this.currentValue = {};
                    return;
                }
                this.currentValue = {
                    ...val
                };
            },
            deviceDetail(val) {
                if (val) {
                    this.currentValue = {
                        ...val
                    };
                    this.$emit('input', this.currentValue);
                    this.$emit('change', this.currentValue);
                }
            }
        },
        filters: {},
        beforeDestroy() {

        }
    }
</script>

<style scoped>
    .scan-icon {
        line-height: inherit;
        font-size: 1.4rem;
        vertical-align: middle
    }
</style>

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部