美文网首页vue.js转载的~暂时看不懂
使用vue-select,从入坑到跳坑

使用vue-select,从入坑到跳坑

作者: 予早 | 来源:发表于2017-04-14 14:40 被阅读8484次

    这几天在学习vue,就想给我的毕设改善一下,使用vue改写邀请管理员界面,涉及到很多复杂的DOM操作,jquery很难执行,如图:


    结果展示图

    我从vue-multiselect用到了vue-select,从vue-tagsinput用到vue-input-tag到vue-tagger再到vue-tagsinput,从Browserify用到了Webpack,把vue组件文档看了好几遍,终于做出来了!历时3天!

    各种坑

    可以跳过,直接看下面的正确解题方法

    最开始使用Vue-multiselect,npm安装后,只有一个lib目录,这也是我怀疑此包不能使用的原因,根据官方文档,需要使用:

    import Multiselect from 'vue-multiselect'
    

    但是查了一下,chrome不支持此语法,所以借助babel转换为es5:Babel在线转换es6

    然而,将下面的js(官方文档)转换后:

    <script>
      import Multiselect from 'vue-multiselect'
    
      // register globally
      Vue.component(Multiselect)
    
      export default {
        // OR register locally
        components: { Multiselect },
        data () {
          return {
            value: null,
            options: ['list', 'of', 'options']
          }
        }
      }
    </script>
    

    并不能在浏览器中使用,因为其中含有require()语句,这是node语法,需要commonJs环境,然后找到Browserify ,可以转换js在浏览器中使用,但是转换过程中报错

    Error: Cannot find module 'vueify' from '/home/lyndon/work/FormReview/public/node_modules/vue-multiselect'
    

    没有安装vueify,查了一下:

    https://github.com/vuejs/vueify/issues/93

    If I then install vueify with --legacy-bundling it works fine.

    $ npm install --legacy-bundling vueify
    - balanced-match...
    $ ./node_modules/.bin/browserify -t vueify -o app.js main.js; echo $?
    0
    

    又报错:

    Error: Cannot find module 'babelify' from '/home/lyndon/work/FormReview/public/node_modules/vue-multiselect'
    

    接着又安装了:

    "devDependencies": {
        "babel-cli": "~6.24.1",
        "babel-core": "^6.24.1",
        "babel-plugin-transform-decorators-legacy": "^1.3.4",
        "babel-plugin-transform-runtime": "^6.23.0",
        "babel-preset-es2015": "^6.24.1",
        "babel-runtime": "^6.23.0",
        "babelify": "^7.3.0",
        "browserify": "^14.3.0",
        "vueify": "^9.4.1"
      },
    
    ./node_modules/.bin/browserify -t vueify -e assets/js/invite.js -o assets/js/build/invite.js
    

    这些乱七八糟的库,这下终于转换成功了,但是,使用此js,浏览器还是报错了。
    然后,按照vueify上面所说,写了
    demo.vue:

    <template>
        <div>
            <multiselect v-model="value" :options="options"></multiselect>
        </div>
    </template>
    
    <script>
        import Multiselect from 'vue-multiselect'
    
        export default {
            components: {
                Multiselect
            },
            data () {
                return {
                    value: '',
                    options: ['Select option', 'options', 'selected', 'mulitple', 'label', 'searchable', 'clearOnSelect', 'hideSelected', 'maxHeight', 'allowEmpty', 'showLabels', 'onChange', 'touched']
                }
            }
        }
    </script>
    

    invite.js:

    var Vue = require('vue');
    var App = require('./demo.vue');
    
    var vm = new Vue({
        el: '#vm-form',
        render: function (createElement) {
            return createElement(App)
        },
     ...
    

    继续转换编译,成功,然后刷新浏览器,这下好了,浏览器报了一大堆错误


    永不言弃

    学习vue组件和webpack:

    单文件组件
    webpack-simple
    webpack使用
    使用vue-cli脚手架

    在webpack-simple示例项目中修改,加入vue-multiselect,编译后各种错误,改成Vue-select,成功运行!哈哈哈!

    使用npm run build编译出js文件!

    在组件中使用vue-resource一直报错,最后使用axios:

    // invite.vue:
    import axios from 'axios';
    axios.get('/web/admin/getOwnForms')...
    

    'change-input': {
                    created: function () {
                        window.inp = this;
                    },
                    props: ['condition'],
                    render: function (createElement) {
                        var self = this
                        console.log(this.condition);
                        if (this.condition.field != null && ['checkbox-group', 'radio-group', 'select'].indexOf(this.condition.field.type) != -1) {
                            return createElement(
                                'v-select',
                                {
                                    multiple,
                                    attrs: {
                                        'v-model': "condition.value",
                                        ':options': "condition.field.values"
                                    },
                                    domProps: {
                                        value: self.condition.value
                                    },
                                    on: {
                                        input: function (event) {
                                            self.condition.value = event.target.value
                                        }
                                    }
                                }
                            );
                        } else {
                            //'<input-tag placeholder="回车输入下一个关键词" :tags="condition.value" validate="text"></input-tag>'
                            return createElement(
                                'input',
                                {
                                    attrs: {
                                        type: "text",
                                        placeholder:"“,”分割关键词"
                                    },
                                    domProps: {
                                        value: self.condition.value
                                    },
                                    on: {
                                        input: function (event) {
                                            self.condition.value = event.target.value
                                        }
                                    }
                                }
                            );
                        }
                    }
                }
    

    根据表单控件动态变换后面的填写域,但是实际不会跟着改变。故采用动态组件
    但是又有问了,一开始conditions为计算属性,根据add_condition改变而改变,watch这个conditions只有第一次有用,改成深度监听也不行,改变策略,将conditions改为正常属性,watch add_condition和conditions,这下点加号conditions增加一个object,页面也跟着变化了。


    正确解题方法

    1. 安装webpack环境:
    • 可以根据webpack-simple安装方法,使用vue-cli安装。
    • 也可以直接运行npm init生成package.json文件,改写该文件,加入webpack,然后运行npm install,如果根目录没有.babelrc文件,必须加上:
      .babelrc:
    {
      "presets": [
         ["latest", {
          "es2015": { "modules": false }
        }]
      ]
    }
    

    webpack.config.js:

    module.exports = {
      //输入路径
      entry: './assets/js/src/invite.js',
      //输出路径
      output: {
        path: path.resolve(__dirname, './assets/js'),
        publicPath: '/assets/js/',
        filename: 'invite.js'
      },
    ...
    

    主要文件:
    invite.js:

    import Vue from 'vue'
    import App from './invite.vue'
    
    new Vue({
        el: '#app',
        render: h => h(App)
    });
    

    invite.vue:

    <template>
        <div id="app">
            <form id="form" @submit.prevent="submit" class="form-horizontal" role="form">
                <div class="form-group">
                    <label class="col-sm-2 control-label">表单</label>
                    <div class="col-sm-10">
                        <v-select label="name" v-model="form" :options="forms"></v-select>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">权限</label>
                    <div class="col-sm-10">
                        <v-select multiple v-model="authority" :options="authorities"></v-select>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">审核阶段</label>
                    <div class="col-sm-10">
                        <v-select multiple v-model="stage" :options="stages"></v-select>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">筛选</label>
                    <div class="col-sm-10">
                        <div class="bootstrap-switch-square">
                            <input type="checkbox" v-model="add_condition" id="condition"/>
                        </div>
                    </div>
                </div>
                <template v-if="add_condition">
                    <div class="form-group conditions" v-for="(condition,index) in conditions">
                        <label class="col-sm-2 control-label">条件{{index+1}}</label>
                        <div class="col-sm-4">
                            <v-select v-model="condition.field" :options="fields"></v-select>
                        </div>
                        <div class="col-sm-5">
                            <component :is="condition.input" :condition="condition"></component>
                        </div>
                        <div class="col-sm-1">
                            <span v-if="index>0" class="glyphicon glyphicon-minus" aria-hidden="true" @click="addCondition(-1,index)"></span>
                            <span class="glyphicon glyphicon-plus" aria-hidden="true" @click="addCondition(1,index)"></span>
                        </div>
                    </div>
                </template>
                <div class="form-group">
                    <label class="col-sm-2 control-label">用户</label>
                    <div class="col-sm-10">
                        <v-select multiple
                                  v-model="user"
                                  :debounce="250"
                                  :on-search="getUsers"
                                  :options="users"
                                  placeholder="搜索用户"
                        >
                        </v-select>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">备注</label>
                    <div class="col-sm-10">
                        <textarea v-model="remark" class="form-control" rows="3"></textarea>
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button type="submit" class="btn btn-default">发送邀请</button>
                    </div>
                </div>
            </form>
        </div>
    </template>
    <script>
        import vSelect from "vue-select"
        import axios from 'axios'
        import tagsInput from 'vue-tagsinput'
    
        export default {
            components: {
                vSelect,
                tagsInput,
                'select-input': {
                    components: {
                        vSelect
                    },
                    props: ['condition'],
                    template: '<v-select multiple v-model="condition.value" :options="condition.field.values"></v-select>'
                },
                'text-input': {
                    components: {
                        tagsInput
                    },
                    props: ['condition'],
                    template: '<tags-input placeholder="Tab键确认关键词" @tags-change="handleChange" :tags="condition.value"></tags-input>',
                    methods: {
                        handleChange: function handleChange(index, text) {
                            if (text) {
                                this.condition.value.splice(index, 0, text);
                            } else {
                                this.condition.value.splice(index, 1);
                            }
                        }
                    }
                }
            },
    
            data() {
                return {
                    forms: [],
                    form: null,
                    authorities: [],
                    authority: [],
                    stage: [],
                    add_condition: false,
                    conditions: [],
                    users: [],
                    user: [],
                    remark: ''
                }
            },
            created: function () {
                this.init();
            },
            methods: {
                init: function () {
                    axios.get('/web/admin/getOwnForms').then(response => {
                        let fid = getQueryString('formId');
                        fid = fid != null ? parseInt(fid) : null;
                        this.forms = response.data;
                        this.forms.forEach(form => {
                            if (fid != null && fid == form.id) {
                                this.form = form;
                            }
                            form.value = form.id;
                            form.label = form.name;
                        });
                    }).catch(function (error) {
                        console.log(error);
                    });
                    axios.get('/web/admin/getAuthorities').then(response => {
                        let auths = response.data;
                        for (let auth in auths) {
                            let a = {};
                            a.value = auth;
                            a.label = auths[auth];
                            this.authorities.push(a);
                        }
                    }).catch(function (error) {
                        console.log(error);
                    });
                },
                addCondition: function (way,index) {
                    if(way>0) {
                        this.conditions.push({
                            field: null,
                            value: [],
                            input: 'text-input'
                        });
                    }else {
                        this.conditions.splice(index,1)
                    }
                },
                getUsers: function (search, loading) {
                    loading(true);
                    axios.get('/web/admin/getExceptUsers', {
                        params: {
                            q: search
                        }
                    }).then(resp => {
                        this.users = [];
                        let users = resp.data;
                        users.forEach((user, i) => {
                            let a = {};
                            a.value = user.id;
                            a.label = user.name + '|' + user.phone + '|' + user.email;
                            this.users.push(a);
                        });
                        loading(false)
                    })
                },
                submit: function () {
                    let conditions = [];
                    this.conditions.forEach(c => {
                        let v = [];
                        c.value.forEach(val => {
                            if (typeof val == 'object' && typeof val.value != 'undefined') {
                                v.push(val.value);
                            } else {
                                v.push(val)
                            }
                        });
                        conditions.push({
                            label: c.field.label,
                            key: c.field.name,
                            value: v.join(',')
                        });
                    });
                    let authorities = [];
                    this.authority.forEach(a => {
                        authorities.push(a.value);
                    });
                    let stages = [];
                    this.stage.forEach(s => {
                        stages.push(s.value);
                    });
                    let users = [];
                    this.user.forEach(u => {
                        users.push(u.value);
                    });
                    axios.post('/web/admin', {
                        tableId: this.form.id,
                        add_condition: this.add_condition,
                        conditions: conditions,
                        authorities: authorities,
                        stages: stages,
                        users: users,
                        remark: this.remark
                    }).then(function (response) {
                        if (response.status == 200) {
                            toastr.success('成功发送邀请');
                        }
                    }).catch(function (error) {
                        console.log(error);
                    });
                }
            },
            computed: {
                stages: function () {
                    let s = [];
                    if (this.form) {
                        let reviewTimes = this.form.reviewTimes;
                        for (let i = 0; i < reviewTimes; i++) {
                            s.push({
                                value: i,
                                label: reviewTime2zh(i)
                            });
                        }
                    }
                    return s;
                },
                fields: function () {
                    if (this.form) {
                        return JSON.parse(this.form.fields);
                    }
                    return [];
                }
            },
            watch: {
                add_condition: function (val) {
                    if (val) {
                        this.conditions = [{
                            field: null,
                            value: [],
                            input: 'text-input'
                        }];
                    }
                },
                conditions: {
                    handler: function (val) {
                        val.forEach((item, i) => {
                            if (typeof item != 'undefined' && item.field != null && ['checkbox-group', 'radio-group', 'select'].indexOf(item.field.type) != -1) {
                                item.input = 'select-input';
                            } else {
                                item.input = 'text-input';
                            }
                        });
                    },
                    deep: true
                }
            }
        };
    </script>
    

    invite.blade.php

    @extends('layouts.app')
    
    @section('content')
        <div class="container">
            <div class="row">
                <div class="col-md-10 col-md-offset-1">
                    <div class="panel panel-default">
                        <div class="panel-heading">邀请管理员</div>
                        <div class="panel-body">
                            <div id="app"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    @endsection
    
    @section('js')
        <script src="{{cdnAsset('/assets/js/invite.js')}}"></script>
    @endsection
    

    相关文章

      网友评论

      本文标题:使用vue-select,从入坑到跳坑

      本文链接:https://www.haomeiwen.com/subject/hfcwattx.html