前言

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

时空穿梭框之旅

示例演示:

这个时空穿梭框实现了:

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

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

文件结构
  1. ├── index.html
  2. ├── main.js
  3. ├── router
  4. │ └── index.js # 路由配置文件
  5. └── components # 组件目录
  6. ├── App.vue # 根组件
  7. ├── Home.vue # 大的框架结构组件
  8. ├── ChangeBox.vue
  9. └── ChangeBoxArea.vue

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

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

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

router/index.js
  1. import Vue from 'vue'
  2. import Router from 'vue-router'
  3. import Home from '@/components/Home'
  4. Vue.use(Router)
  5. export default new Router({
  6. routes: [{
  7. path: '/',
  8. name: 'Home',
  9. component: Home
  10. }]
  11. })

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

App.vue
  1. <template>
  2. <div id="app">
  3. <Home></Home>
  4. </div>
  5. </template>
  6. <script>
  7. import Home from "@/components/Home";
  8. export default {
  9. name: "App",
  10. components: { Home }
  11. };
  12. </script>
  13. <style>
  14. #app {
  15. font-family: "Avenir", Helvetica, Arial, sans-serif;
  16. -webkit-font-smoothing: antialiased;
  17. -moz-osx-font-smoothing: grayscale;
  18. color: #2c3e50;
  19. margin-top: 60px;
  20. }
  21. </style>

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

Home.vue
  1. <template>
  2. <div>
  3. <change-box></change-box>
  4. </div>
  5. </template>
  6. <script>
  7. import ChangeBox from "@/components/ChangeBox";
  8. export default {
  9. name: "Home",
  10. components: {
  11. ChangeBox
  12. }
  13. };
  14. </script>

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

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

ChangeBox.vue
  1. <template>
  2. <div class="container">
  3. <div class="row">
  4. <div class="col-md-5">
  5. <change-box-area :title="sourceTitle" :data="sourceList"></change-box-area>
  6. </div>
  7. <div class="col-md-2 text-center">
  8. <p><button :disabled="sourceList.length === 0 || sourceRefNum === 0" class="btn btn-primary" @click="toTarget()">》</button></p>
  9. <p><button :disabled="targetList.length === 0 || targetRefNum === 0" class="btn btn-primary" @click="toSource()">《</button></p>
  10. </div>
  11. <div class="col-md-5">
  12. <change-box-area :title="targetTitle" :data="targetList"></change-box-area>
  13. </div>
  14. </div>
  15. </div>
  16. </template>
  17. <script>
  18. import ChangeBoxArea from "./ChangeBoxArea";
  19. // 这里的 isSeleted 属性可以不用添加,可以在 JS 中进行处理,一般情况下后端返回的数据也不会带有类似这种静态状态的属性
  20. let dataList = [
  21. { id: 1, name: "HTML5", isSelected: false },
  22. { id: 2, name: "CSS3", isSelected: false },
  23. { id: 3, name: "Angular", isSelected: false },
  24. { id: 4, name: "Vue", isSelected: false },
  25. { id: 5, name: "Linux", isSelected: false },
  26. { id: 6, name: "JavaScript", isSelected: false }
  27. ];
  28. export default {
  29. components: {
  30. ChangeBoxArea
  31. },
  32. name: "ChangeBox",
  33. data() {
  34. return {
  35. sourceTitle: "请选择",
  36. targetTitle: "已选择",
  37. sourceList: dataList,
  38. targetList: []
  39. };
  40. },
  41. methods: {
  42. exchange(fd, td) {
  43. let selectedItem = fd.filter(item => item.isSelected).map(item => {
  44. return {
  45. ...item,
  46. isSelected: false
  47. };
  48. });
  49. td.push(...selectedItem);
  50. return fd.filter(item => !item.isSelected);
  51. },
  52. // 把选择数据转移到目标(右框)
  53. toTarget() {
  54. this.sourceList = this.exchange(this.sourceList, this.targetList);
  55. },
  56. // 把选择数据转回到源(左框)
  57. toSource() {
  58. this.targetList = this.exchange(this.targetList, this.sourceList);
  59. }
  60. },
  61. computed: {
  62. // 源数据中选中的数量
  63. sourceRefNum() {
  64. return this.sourceList.filter(item => item.isSelected).length;
  65. },
  66. // 目标数据中选中的数量
  67. targetRefNum() {
  68. return this.targetList.filter(item => item.isSelected).length;
  69. }
  70. }
  71. };
  72. </script>

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

ChangeBoxArea.vue
  1. <template>
  2. <div class="panel panel-default">
  3. <div class="panel-heading clearfix">
  4. <div class="pull-left">
  5. <div class="checkbox">
  6. <label>
  7. <input :disabled="data.length === 0" type="checkbox" @click="toggleAll()" :checked="selectedAllStatus"><span>{{title}}</span>
  8. </label>
  9. </div>
  10. </div>
  11. <span class="pull-right">{{selectItemNumber}}/{{data.length}}</span>
  12. </div>
  13. <div class="panel-body">
  14. <ul>
  15. <li v-for="item in data" :key="item.id">
  16. <div class="checkbox">
  17. <label>
  18. <input type="checkbox" v-model="item.isSelected"> {{item.name}}
  19. </label>
  20. </div>
  21. </li>
  22. </ul>
  23. </div>
  24. </div>
  25. </template>
  26. <script>
  27. export default {
  28. name: "ChangeBox",
  29. props: ["title", "data"],
  30. computed: {
  31. // 选择的数量
  32. selectItemNumber() {
  33. return this.data.filter(item => item.isSelected).length;
  34. },
  35. // 全选状态
  36. selectedAllStatus() {
  37. if (
  38. this.selectItemNumber === this.data.length &&
  39. this.selectItemNumber !== 0
  40. ) {
  41. return true;
  42. } else {
  43. return false;
  44. }
  45. }
  46. },
  47. methods: {
  48. // 全选及反选
  49. toggleAll() {
  50. let len = this.data.length;
  51. let slen = this.data.filter(item => item.isSelected).length;
  52. if (len !== slen) {
  53. this.data.map(item => (item.isSelected = true));
  54. } else {
  55. this.data.map(item => (item.isSelected = false));
  56. }
  57. }
  58. }
  59. };
  60. </script>
  61. <style scoped>
  62. ul {
  63. list-style: none;
  64. padding: 0;
  65. }
  66. .checkbox {
  67. margin: 0;
  68. }
  69. </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、不用点中间的两个箭头,而是勾选后,就会自动穿梭到右边去。

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

Vue2 实现时空穿梭框功能模块的更多相关文章

  1. Vue实现拖拽穿梭框功能四种方式

    一.使用原生js实现拖拽 点击打开视频讲解更加详细 <html lang="en"> <head> <meta charset="UTF-8 ...

  2. vue2.0 + element ui 实现表格穿梭框

    element ui 官网里介绍了穿梭框(Transfer),但在实际使用过程中,会出现一些问题: 1.穿梭框里能放置的内容太少,不能满足复杂的业务需求. 2.当选项过多时,穿梭框很难实现分页,左右两 ...

  3. Vue2 实现树形菜单(多级菜单)功能模块

    结构示意图 ├── index.html ├── main.js ├── router │ └── index.js # 路由配置文件 ├── components # 组件目录 │ ├── App. ...

  4. layui实现类似于bootstrap的模态框功能

    以前习惯了bootstrap的模态框,突然换了layui,想的用layui实现类似于bootstrap的模态框功能. 用到了layui的layer模块,例如: <!DOCTYPE html> ...

  5. 自定义SWT控件五之自定义穿梭框

    5.自定义穿梭框 package com.view.control.shuttlebox; import java.util.ArrayList; import java.util.HashMap; ...

  6. krry-transfer ⏤ 基于 element 的升级版穿梭框组件发布到 npm 啦

    博客地址:https://ainyi.com/81 基于 element ui 的==升级版穿梭框组件==发布到 npm 啦 看过我之前博客的同学或许知道我之前写过关于 element 穿梭框组件重构 ...

  7. VUE+ElementUI实现左侧为树形结构、右侧无层级结构的穿梭框

    工作中遇到一个需求,需要将一个数据选择做成穿梭框,但是要求穿梭框左侧为树形结构.右侧为无层级结构的数据展示,ElementUI自身无法在穿梭框中添加树形结构,网上搜到了大佬封装的插件但是对于右侧的无树 ...

  8. struts-hibernate-ajax完成区县和街道级联下拉框功能(二补充使用json解析list结果集,ajax循环json层级处理)

    针对<struts-hibernate-ajax完成区县和街道级联下拉框功能>进行补充,上一篇中,要在action中拼接JSON格式字符串,很容易手抖.直接用json处理一下转成json格 ...

  9. 用jsonp实现搜索框功能

    用jsonp实现搜索框功能 前面的话: 在上周本来想发一篇模仿必应搜索的界面.但是在准备写文章之前突然想到前面学习了ajax技术,在这里我也让我的页面有一种不需要手动刷新就能获取到数据.但是发现用前面 ...

随机推荐

  1. chrome插件研发手册

    chrome插件研发手册 一:需求前景 对于研发的小伙伴来说,总会遇到这样的需求,想要通过代码操作已有网站的行为动作,如:自动填充表格内容(表单内容太多,想一键将表单内容填充):自动登录网站(网站登录 ...

  2. jenkins 更改端口

    方法一 在Jenkins目录下,运行一下命令: java -jar jenkins.war --ajp13Port=-1 --httpPort=8081 出现了错误: C:\Program Files ...

  3. ci常量

    1. ENVIRONMENT产品的环境,有3种环境,分别是: development开发环境 testing测试环境 production生产环境 2. SELFCI的主入口文件名称 例如我的是: i ...

  4. ECS运维:操作系统有异常?诊断日志来帮忙!

    ​云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新.阿里云使用严格的IDC标准.服务器准入标准 ...

  5. linux0.11源码内核——系统调用,int80的实现细节

    linux0.11添加系统调用的步骤 假设添加一个系统调用foo() 1.修改include/linux/sys.h 添加声明 extern int foo(); 同时在sys_call_table数 ...

  6. MIS(管理信息系统)

    MIS 管理信息系统(Management Information System,简称MIS) 是一个以人为主导,利用计算机硬件.软件.网络通信设备以及其他办公设备,进行信息的收集.传输.加工.储存. ...

  7. python数据分析中常用的库

    Python是数据处理常用工具,可以处理数量级从几K至几T不等的数据,具有较高的开发效率和可维护性,还具有较强的通用性和跨平台性,这里就为大家分享几个不错的数据分析工具,需要的朋友可以参考下 Pyth ...

  8. python练习题之随机生成验证码

    #引用random模块下的randint项目#定义验证码函数.定义一个空字符串变量,分三种情况,随机产生的大写字母,随机产生的小写字母,随机产生的数字.然后#每一次执行哪一种情况,条件也是随机的,就是 ...

  9. AST7D08 心率计

    接线: 1.GND 2.+3.3V 3.RST 4. 5.CS 6.READY 7.DI 8.DO 9.CLK

  10. Python 进阶_OOP 面向对象编程_self 的实例绑定

    目录 目录 self 和绑定 调用非绑定的方法 self 和绑定 在 Python 中 self 变量是特殊的, 其用于在实例方法中引用该方法所绑定的实例, 换句话说就是 Python 在实例化对象时 ...