1. 程式人生 > >Vue2 實現時空穿梭框功能模組

Vue2 實現時空穿梭框功能模組

前言

這篇文章主要是分享一個時空穿梭框功能,也就是我們平時用的選擇功能。勾選了的項就會進入到另一個框中。

時空穿梭框之旅

示例演示:

Vue 實現時空穿梭框功能模組

這個時空穿梭框實現了:

  • 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、不用點中間的兩個箭頭,而是勾選後,就會自動穿梭到右邊去。

這樣的需求極為常見,所以你有必要在平時的學習中,把這些東西都自己整理出來,或者把常用的功能模組封裝成通用元件,這個對於提高工作效率是非常有用的。花個兩三天把它封裝成一個常用的元件,以後開發起來,只要遇到這種的基本都可以搬過來用,頂多改改樣式,所以也有必要給元件新增必要的屬性為定製提供可能。又或者更過分點的,追加一些功能。