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

这几天在学习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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容