网站首页 » 前端开发 » Vue » Vue2 实现时空穿梭框功能模块
上一篇:
下一篇:

Vue2 实现时空穿梭框功能模块

前言

这篇文章主要是分享一个时空穿梭框功能,也就是我们平时用的选择功能。勾选了的项就会进入到另一个框中。

时空穿梭框之旅

示例演示:

Vue 实现时空穿梭框功能模块

这个时空穿梭框实现了:

  • 1、可以全选、反选
  • 2、没有选中时,不可以点穿梭按钮
  • 3、自动计数(共有多少个,选中了多少个)
  • 4、没有数据时,全选不可点击

这里主要是想通过这个示例来抛砖引玉,更多的功能,你可以根据自己的实践需要来实现。下面我们就来看看这示例的相关文件及代码。

文件结构
├── index.html
├── main.js
├── router
│ └── index.js      # 路由配置文件
└── components      # 组件目录
  ├── App.vue       # 根组件
  ├── Home.vue      # 大的框架结构组件
  ├── ChangeBox.vue 
  └── ChangeBoxArea.vue

文件也不多,只要有两个(ChangeBox.vue 和 ChangeBoxArea.vue)下面我们就来看看实现这个示例的代码:

index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>changebox</title>
  <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
  <div id="app"></div>
  <!-- built files will be auto injected -->
</body>
</html>

本示例主要用到了 bootstrap ,所以我们就在 index.html 中引入了 bootstrap 的 cdn。然后我们就可以直接在示例中使用 bootstrap 给我们提供的 UI 了。

router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'

Vue.use(Router)

export default new Router({
  routes: [{
    path: '/',
    name: 'Home',
    component: Home
  }]
})

在这里我们直接把 / 路径的配置到 Home 组件。

App.vue
<template>
  <div id="app">
    <Home></Home>
  </div>
</template>

<script>
import Home from "@/components/Home";
export default {
  name: "App",
  components: { Home }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

在根组件中,我们只是做了一件很简单的事,就是引入Home 组件。

Home.vue
<template>
  <div>
    <change-box></change-box>
  </div>
</template>
<script>
import ChangeBox from "@/components/ChangeBox";
export default {
  name: "Home",
  components: {
    ChangeBox
  }
};
</script>

Home.vue 组件代码非常地简单,这里其实也可以直接写到根组件上的,但为了养成良好的习惯,我们还是有必要把示例的整体结构往写成更接近实战一些。

好了,上面的基本功做好了之后,我就可以开始这个时空穿梭框的主要代码了的展示了。

ChangeBox.vue
<template>
 <div class="container">
   <div class="row">
        <div class="col-md-5">
          <change-box-area :title="sourceTitle" :data="sourceList"></change-box-area>
        </div>
        <div class="col-md-2 text-center">
            <p><button :disabled="sourceList.length === 0 || sourceRefNum === 0" class="btn btn-primary" @click="toTarget()">》</button></p>
            <p><button :disabled="targetList.length === 0 || targetRefNum === 0" class="btn btn-primary" @click="toSource()">《</button></p>
        </div>
        <div class="col-md-5">
          <change-box-area :title="targetTitle" :data="targetList"></change-box-area>
        </div>
   </div>
 </div>
</template>

<script>
import ChangeBoxArea from "./ChangeBoxArea";
// 这里的 isSeleted 属性可以不用添加,可以在 JS 中进行处理,一般情况下后端返回的数据也不会带有类似这种静态状态的属性
let dataList = [
  { id: 1, name: "HTML5", isSelected: false },
  { id: 2, name: "CSS3", isSelected: false },
  { id: 3, name: "Angular", isSelected: false },
  { id: 4, name: "Vue", isSelected: false },
  { id: 5, name: "Linux", isSelected: false },
  { id: 6, name: "JavaScript", isSelected: false }
];
export default {
  components: {
    ChangeBoxArea
  },
  name: "ChangeBox",
  data() {
    return {
      sourceTitle: "请选择",
      targetTitle: "已选择",
      sourceList: dataList,
      targetList: []
    };
  },
  methods: {
    exchange(fd, td) {
      let selectedItem = fd.filter(item => item.isSelected).map(item => {
        return {
          ...item,
          isSelected: false
        };
      });
      td.push(...selectedItem);
      return fd.filter(item => !item.isSelected);
    },
    // 把选择数据转移到目标(右框)
    toTarget() {
      this.sourceList = this.exchange(this.sourceList, this.targetList);
    },
    // 把选择数据转回到源(左框)
    toSource() {
      this.targetList = this.exchange(this.targetList, this.sourceList);
    }
  },
  computed: {
    // 源数据中选中的数量
    sourceRefNum() {
      return this.sourceList.filter(item => item.isSelected).length;
    },
    // 目标数据中选中的数量
    targetRefNum() {
      return this.targetList.filter(item => item.isSelected).length;
    }
  }
};
</script>

接下来我们再来看看最后一个

ChangeBoxArea.vue
<template>
    <div class="panel panel-default">
      <div class="panel-heading clearfix">
        <div class="pull-left">
          <div class="checkbox">
              <label>
                <input :disabled="data.length === 0" type="checkbox" @click="toggleAll()" :checked="selectedAllStatus"><span>{{title}}</span>
              </label>
          </div>
        </div>
        <span class="pull-right">{{selectItemNumber}}/{{data.length}}</span>
      </div>
      <div class="panel-body">
        <ul>
          <li v-for="item in data" :key="item.id">
             <div class="checkbox">
              <label>
                <input type="checkbox" v-model="item.isSelected"> {{item.name}}
              </label>
            </div>
          </li>
        </ul>
      </div>
    </div>
</template>
<script>
export default {
  name: "ChangeBox",
  props: ["title", "data"],
  computed: {
    // 选择的数量
    selectItemNumber() {
      return this.data.filter(item => item.isSelected).length;
    },
    // 全选状态
    selectedAllStatus() {
      if (
        this.selectItemNumber === this.data.length &&
        this.selectItemNumber !== 0
      ) {
        return true;
      } else {
        return false;
      }
    }
  },
  methods: {
    // 全选及反选
    toggleAll() {
      let len = this.data.length;
      let slen = this.data.filter(item => item.isSelected).length;
      if (len !== slen) {
        this.data.map(item => (item.isSelected = true));
      } else {
        this.data.map(item => (item.isSelected = false));
      }
    }
  }
};
</script>
<style scoped>
ul {
  list-style: none;
  padding: 0;
}
.checkbox {
  margin: 0;
}
</style>

在上面的代码中,有一个地图需要特别的注意下:在全选的input 中我们要使用 :checked 来绑定 selectedAllStatus,而不用 v-model,因为我们的 selectedAllStatus 是一个计算属性,如果把它绑定到 v-model,会报错的:

报错

[Vue warn]: Computed property “selectedAllStatus” was assigned to but it has no setter.

大概意思是说 selectedAllStatus 没有 setter 方法,不能给它赋值,当然,你可以把给这个属性添加 setter 方法,但这样做好像又有点累赘了。为此我们直接使用 :checked 来绑定 selectedAllStatus  属性。

Vue 实现时空穿梭框功能模块就分享到这里,其实这样的需求示例在真实的项目中是有可能出现的,但在项目中这个很有可能会更加复杂,比如:

  • 1、左边的框不是平铺的,而是多级,可展开收缩,勾选了的项才会出现在右边的框中
  • 2、搜索功能
  • 3、不用点中间的两个箭头,而是勾选后,就会自动穿梭到右边去。

这样的需求极为常见,所以你有必要在平时的学习中,把这些东西都自己整理出来,或者把常用的功能模块封装成通用组件,这个对于提高工作效率是非常有用的。花个两三天把它封装成一个常用的组件,以后开发起来,只要遇到这种的基本都可以搬过来用,顶多改改样式,所以也有必要给组件添加必要的属性为定制提供可能。又或者更过分点的,追加一些功能。

  • 微信扫一扫,赏我

  • 支付宝扫一扫,赏我

声明

原创文章,不经本站同意,不得以任何形式转载,如有不便,请多多包涵!

本文永久链接:http://yunkus.com/vue-functional-module-time-shuttle-box/

评论1
  1. 王志翔 2018年5月7日 下午3:43 回复

    多级的要怎么做??

发表评论

电子邮件地址不会被公开。 必填项已用*标注

评论 END