vueはEle.me UIを使用してteambitionのフィルタリング機能を模倣します

vueはEle.me UIを使用してteambitionのフィルタリング機能を模倣します

問題の説明

Teambition ソフトウェアは、エンタープライズ オフィス コラボレーション ソフトウェアです。私の友人の会社のいくつかがこのソフトウェアを使用していると思います。フィルタリング機能は非常に興味深いです。この記事ではその機能を真似てみます。最終結果を見てみましょう。

一般的な機能効果は次のとおりです。

  • 要件1: よく使用されるフィルター条件は上部に配置され、直接確認でき、あまり使用されないフィルター条件はフィルター条件の追加に配置されます。
  • 要件 2: フィルタリング方法には、入力ボックス フィルタリング、ドロップダウン ボックス フィルタリング、時間セレクタ フィルタリングなどが含まれます。
  • 要件3: よく使用されるフィルター条件が多すぎると感じた場合は、マウスを移動してクリックして削除し、あまり使用されないフィルター条件を入力できます。
  • 要件4: 一般的でないフィルター条件から対応するフィルター条件をクリックして、一般的なフィルター条件に「ジャンプ」することもできます。
  • 要件5: リセットをクリックして、最初のテストのスクリーニング条件を復元します。
  • 要件6: ユーザーがコンテンツを入力せずに確認ボタンをクリックすると、フィルター条件を入力するよう求められます。

思考分析

要件 1 と 2 については、まず 2 つのフルスクリーン ポップアップ ボックスを作成し、次にデータ内に 2 つの配列を定義します。1 つはよく使用される条件用、もう 1 つは一般的でない条件用です。一般的な条件は最初のポップアップ ボックスに v-fored され、一般的でない条件は 2 番目のポップアップ ボックスに v-fored されます。配列内の各項目は、名前、年齢などのフィルタリング フィールドの名前など、対応するコンテンツで構成する必要があります。フィルター フィールドの名前の次には、タイプもあります。HTML では、入力ボックス コンポーネント、選択コンポーネント、時間セレクター コンポーネントなど、3 種類のコンポーネントを記述する必要があります。 v-show を使用して、タイプ タイプに応じて対応するフィールドを表示します。たとえば、入力タイプは 1、選択タイプは 2、時間セレクター タイプは 3 です。表示されるコンポーネントはタイプです。

対応する 2 つの配列は次のとおりです。

topData: [ //共通フィルター項目を設定する {
     wordTitle: "名前",
     type: 1, // 1 は入力、2 は選択、3 は DatePicker
     content: "", // content は入力ボックスにバインドされた入力データです。options: [], // options はドロップダウン ボックスのすべてのコンテンツです。リクエストを送信して取得し、保存することができます。以下はシミュレーションです。optionArr: [], // optionArr は選択されたドロップダウン ボックスのコンテンツです。timeArr: [], // timeArr は日付選択間隔です。},
    {
     wordTitle: 「年齢」、
     タイプ: 1,
     コンテンツ: ""、
     オプション: [],
     オプション引数: [],
     時間配列: [],
    },
    {
     wordTitle: 「授業の指導」、
     タイプ: 2,
     コンテンツ: ""、
     options: [ // ドロップダウンボックスのオプションを取得するリクエストを送信します {
       id: 1,
       値: "クラス 1",
      },
      {
       id: 2,
       値: "クラス 2",
      },
      {
       id: 3,
       値: "3シフト"、
      },
     ]、
     オプション引数: [],
     時間配列: [],
    },
    {
     wordTitle: 「参加時間」、
     タイプ: 3, 
     コンテンツ: ""、 
     オプション: [], 
     オプション引数: [], 
     時間配列: [], 
    },
   ]、
   bottomData: [ // 一般的でないフィルター項目を構成する {
     wordTitle: "作品番号",
     タイプ: 1,
     コンテンツ: ""、
     オプション: [],
     オプション引数: [],
     時間配列: [],
    },
    {
     wordTitle: 「性別」、
     タイプ: 2,
     コンテンツ: ""、
     オプション: [
      {
       id: 1,
       値: "男性",
      },
      {
       id: 2,
       値: "女性",
      },
     ]、
     オプション引数: [],
     時間配列: [],
    },
   ]、

対応する HTML コードは次のとおりです。

        <div class="rightright">
         <el-入力
          v-model.trim="アイテムコンテンツ"
          クリア可能
          v-show="item.type == 1"
          placeholder="入力してください"
          サイズ="小"
          :popper-append-to-body="false"
         </el-input> ...
         <el-選択
          v-model="item.optionArr"
          v-show="item.type == 2"
          複数
          placeholder="選択してください"
         >
          <el-オプション
           v-for="item.options 内の whatItem"
           :key="アイテムID"
           :label="whatItem.値"
           :value="アイテムID"
           サイズ="小"
          >
          </el-option>
         </el-select>
         <el-日付ピッカー
          v-model="item.timeArr"
          v-show="item.type == 3"
          タイプ="日付範囲"
          範囲区切り文字="to"
          start-placeholder="開始日"
          end-placeholder="終了日"
          フォーマット="yyyy-MM-dd"
          値の形式="yyyy-MM-dd"
         >
         </el-date-picker>
        </div>

完全なコードは最後にあります。まずはアイデアに従ってください。

要件 3 と 4 については、上位の要件を削除して下位の要件にドロップすると言えます。下記をクリックすると先頭へ移動します。したがって、対応する操作は、上位配列の項目を下位配列に追加し、上位配列の項目を削除し、下位配列の項目を上位配列に追加し、この行を削除することです。 (インデックスもあることに注意してください) 対応するコードは次のとおりです。

/* アイテムの削除アイコンをクリックすると、そのアイテムが bottomData 配列に追加され、その後 topData 配列からそのアイテムが削除されます (インデックスに基づいてどのアイテムであるかを判断します) 
    最後に、1つを削除し、インデックスを初期インデックス - 1 に設定します */
  クリックアイコン(i) {
   this.bottomData.push(this.topData[i]);
   this.topData.splice(i, 1);
   this.whichIndex = -1;
  },
  // 一番下の項目をクリックすると、イベント オブジェクトを使用して一番下のどの項目がクリックされたかを確認し、対応する項目を topData に追加して表示し、一番下の配列の項目を削除します // clickBottomItem(event) {
   this.bottomData.forEach((item, index) => {
    if (item.wordTitle == event.target.innerText) {
     this.topData.push(アイテム);
     this.bottomData.splice(インデックス、1);
    }
   });
  },

要件 5 と 6 は単純です。対応するコードは次のとおりです。完全なコードコメントが記述されています。

完全なコード

<テンプレート>
 <div id="アプリ">
  <div class="filterBtn">
   <el-button type="primary" size="small" @click="filterMaskOne = true">
    データ フィルター<i class="el-icon-s-operation el-icon--right"></i>
   </el-button>
   <トランジション名="フェード">
    <div
     クラス="filterMaskOne"
     v-show="フィルターマスクOne"
     @click="filterMaskOne = false"
    >
     <div class="filterMaskOneContent" @click.stop>
      <div class="フィルターヘッダー">
       <span>データフィルタリング</span>
      </div>
      <div class="filterBody">
       <div class="outPrompt" v-show="topData.length == 0">
        フィルター条件がまだありません。フィルター条件を追加してください...
       </div>
       <div
        クラス="filterBodyCondition"
        v-for="(item, index) in topData"
        :key="インデックス"
       >
        <div
         クラス="leftleft"
         @mouseenter="mouseEnterItem(インデックス)"
         @mouseleave="mouseLeaveItem(インデックス)"
        >
         <span
          >{{ item.wordTitle }}:
          <i
           クラス="el-icon-error"
           v-show="whichIndex == index"
           @click="クリックアイコン(インデックス)"
          </i>
         </span>
        </div>
        <div class="rightright">
         <el-入力
          v-model.trim="アイテムコンテンツ"
          クリア可能
          v-show="item.type == 1"
          placeholder="入力してください"
          サイズ="小"
          :popper-append-to-body="false"
         </el-input> ...
         <el-選択
          v-model="item.optionArr"
          v-show="item.type == 2"
          複数
          placeholder="選択してください"
         >
          <el-オプション
           v-for="item.options 内の whatItem"
           :key="アイテムID"
           :label="whatItem.値"
           :value="アイテムID"
           サイズ="小"
          >
          </el-option>
         </el-select>
         <el-日付ピッカー
          v-model="item.timeArr"
          v-show="item.type == 3"
          タイプ="日付範囲"
          範囲区切り文字="to"
          start-placeholder="開始日"
          end-placeholder="終了日"
          フォーマット="yyyy-MM-dd"
          値の形式="yyyy-MM-dd"
         >
         </el-date-picker>
        </div>
       </div>
      </div>
      <div class="filterFooter">
       <div class="filterBtn">
        <el-ボタン
         タイプ="テキスト"
         アイコン="el-icon-circle-plus-outline"
         @click="filterMaskTwo = true"
         >フィルター条件を追加</el-button
        >
        <トランジション名="フェード">
         <div
          クラス="filterMaskTwo"
          v-show="フィルターマスク2"
          @click="filterMaskTwo = false"
         >
          <div class="filterMaskContentTwo" @click.stop>
           <div class="innerPrompt" v-show="bottomData.length == 0">
            まだコンテンツがありません...
           </div>
           <div
            クラス="contentTwoItem"
            @click="クリック下部項目"
            v-for="(item, index) in bottomData"
            :key="インデックス"
           >
            <div class="mingzi">
             {{ item.wordTitle }}
            </div>
           </div>
          </div>
         </div>
        </トランジション>
       </div>
       <div class="resetAndConfirmBtns">
        <el-button size="small" @click="resetFilter">リセット</el-button>
        <el-button type="primary" size="small" @click="フィルターの確認"
         >確認</el-button
        >
       </div>
      </div>
     </div>
    </div>
   </トランジション>
  </div>
 </div>
</テンプレート>

<スクリプト>
エクスポートデフォルト{
 名前:「アプリ」、
 データ() {
  戻る {
   filterMaskOne: false, // 2つのポップアップボックスの表示と非表示をそれぞれ制御するために使用されます filterMaskTwo: false,
   whichIndex: -1, // クリックを記録するために使用されるインデックス apiFilterArr:[], // ユーザーが入力したフィルターコンテンツを保存します topData: [ // 共通フィルター項目を構成します {
     wordTitle: "名前",
     type: 1, // 1 は入力、2 は選択、3 は DatePicker
     content: "", // content は入力ボックスにバインドされた入力データです。 options: [], // options はドロップダウン ボックスのすべての内容です。 optionArr: [], // optionArr は選択されたドロップダウン ボックスの内容です。 timeArr: [], // timeArr は日付の選択間隔です。 },
    {
     wordTitle: 「年齢」、
     タイプ: 1,
     コンテンツ: ""、
     オプション: [],
     オプション引数: [],
     時間配列: [],
    },
    {
     wordTitle: 「授業の指導」
     タイプ: 2,
     コンテンツ: ""、
     options: [ // ドロップダウンボックスのオプションを取得するリクエストを送信します {
       id: 1,
       値: "クラス 1",
      },
      {
       id: 2,
       値: "クラス 2",
      },
      {
       id: 3,
       値: "3シフト"、
      },
     ]、
     オプション引数: [],
     時間配列: [],
    },
    {
     wordTitle: 「参加時間」、
     タイプ: 3, 
     コンテンツ: ""、 
     オプション: [], 
     オプション引数: [], 
     時間配列: [], 
    },
   ]、
   bottomData: [ // 一般的でないフィルター項目を構成する {
     wordTitle: "作品番号",
     タイプ: 1,
     コンテンツ: ""、
     オプション: [],
     オプション引数: [],
     時間配列: [],
    },
    {
     wordTitle: 「性別」、
     タイプ: 2,
     コンテンツ: ""、
     オプション: [
      {
       id: 1,
       値: "男性",
      },
      {
       id: 2,
       値: "女性",
      },
     ]、
     オプション引数: [],
     時間配列: [],
    },
   ]、
  };
 },
 マウント() {
  // ロードを初期化するときに、構成したよく使用されるフィルター項目とあまり使用されないフィルター項目のコピーを保存します。// ユーザーがリセット ボタンをクリックすると、それを取得して元のフィルター条件の状態に復元します。sessionStorage.setItem("topData",JSON.stringify(this.topData))
  sessionStorage.setItem("bottomData",JSON.stringify(this.bottomData))
 },
 メソッド: {
  //マウスを動かすと削除アイコンが表示されます mouseEnterItem(index) {
   this.whichIndex = インデックス;
  },
  // マウスが離れると、インデックスはデフォルト値 -1 に戻ります
  マウスアイテムを離れる() {
   this.whichIndex = -1;
  },
  /* アイテムの削除アイコンをクリックすると、そのアイテムが bottomData 配列に追加され、その後 topData 配列から削除されます (インデックスに基づいてどのアイテムであるかを判断します) 
    最後に、1つを削除し、インデックスを初期インデックス - 1 に設定します */
  クリックアイコン(i) {
   this.bottomData.push(this.topData[i]);
   this.topData.splice(i, 1);
   this.whichIndex = -1;
  },
  // 一番下の項目をクリックすると、イベント オブジェクトを使用して一番下のどの項目がクリックされたかを確認し、対応する項目を topData に追加して表示し、一番下の配列の項目を削除します // clickBottomItem(event) {
   this.bottomData.forEach((item, index) => {
    if (item.wordTitle == event.target.innerText) {
     this.topData.push(アイテム);
     this.bottomData.splice(インデックス、1);
    }
   });
  },
  // クリックしてフィルターを確認します async confirmFilter() {
   // すべての入力ボックスの内容が空で、選択されたドロップダウンボックスの配列が空で、時間セレクターによって選択された配列が空の場合、ユーザーが内容を入力していないことを意味するため、フィルタリングする前にユーザーに内容を入力するよう促します。let isEmpty = this.topData.every((item)=>{
    戻り値 (item.content == "") && (item.optionArr.length == 0) && (item.timeArr.length == 0)
   })
   if(isEmpty == true){
     this.$alert('フィルタリングする前にコンテンツを入力してください', 'フィルターのヒント', {
     confirmButtonText: '確認'
    });
   }それ以外{
    // パラメータを収集し、フィルタリング要求を送信します。ここでは、パラメータをタイプ別に分類し、空でないユーザー入力コンテンツをデータ フィルタリング用の配列に保存してから、バックエンドに要求を送信する必要があります。
    this.topData.forEach((item)=>{
     if(item.type == 1){
      if(item.content != ""){
       フィルターアイテム = {
        フィールド:item.wordTitle、
        値:アイテムの内容
       }
       this.apiFilterArr.push(フィルター項目)
      }
     }そうでない場合(item.type == 2){
      if(item.optionArr.length > 0){
       フィルターアイテム = {
        フィールド:item.wordTitle、
        値:item.optionArr
       }
       this.apiFilterArr.push(フィルター項目)
      }
     }そうでない場合(item.type == 3){
      if(item.timeArr.length > 0){
       フィルターアイテム = {
        フィールド:item.wordTitle、
        値:item.timeArr
       }
       this.apiFilterArr.push(フィルター項目)
      }
     } 
    })
    // フィルタリングされたコンテンツを配列に入れてバックエンドに渡します (もちろん、パラメータを必ずしも配列に入れる必要はありません)
    // バックエンドに渡す具体的な形式については、後で詳しく説明します。console.log("Send request with filtered content", this.apiFilterArr);
   }
  },
  // リセット時に、初期設定のフィルタ項目を取り出し、対応する2つの配列に割り当てます resetFilter() {
   this.topData = JSON.parse(sessionStorage.getItem("topData"))
   this.bottomData = JSON.parse(sessionStorage.getItem("bottomData"))
  },
 },
};
</スクリプト>
<style lang="less" スコープ>
.filterBtn {
 幅: 114ピクセル;
 高さ: 40px;
 .filterMaskOne{
  上: 0;
  左: 0;
  位置: 固定;
  幅: 100%;
  高さ: 100%;
  zインデックス: 999;
  背景色: rgba(0, 0, 0, 0.3);
  .filterMaskOneContent {
   位置: 絶対;
   上: 152px;
   右: 38px;
   幅: 344ピクセル;
   高さ: 371px;
   背景色: #fff;
   ボックスの影: 0px 0px 4px 3px rgba(194, 194, 194, 0.25);
   境界線の半径: 4px;
   .filterHeader {
    幅: 344ピクセル;
    高さ: 48px;
    下境界線: 1px 実線 #e9e9e9;
    スパン {
     表示: インラインブロック;
     フォントの太さ: 600;
     フォントサイズ: 16px;
     左マージン: 24px;
     上マージン: 16px;
    }
   }
   .filterBody {
    幅: 344ピクセル;
    高さ: 275px;
    オーバーフロー-y: 自動;
    オーバーフロー-x:非表示;
    ボックスのサイズ: 境界線ボックス;
    パディング: 12px 24px 0 24px;
    .outPrompt{
     色: #666;
    }
    .filterBodyCondition {
     幅: 100%;
     最小高さ: 40px;
     ディスプレイ: フレックス;
     下部マージン: 14px;
     .leftleft{
      幅: 88ピクセル;
      高さ: 40px;
      ディスプレイ: フレックス;
      アイテムの位置を中央揃えにします。
      右マージン: 20px;
      スパン {
       位置: 相対的;
       フォントサイズ: 14px;
       色: #333;
       私 {
        色: #666;
        右: -8px;
        上: -8px;
        位置: 絶対;
        フォントサイズ: 15px;
        カーソル: ポインタ;
       }
       ホバー{
        色: #5f95f7;
       }
      }
     }
     .rightright {
      幅: calc(100% - 70px);
      高さ: 100%;
      /deep/ 入力::プレースホルダー {
       色: rgba(0, 0, 0, 0.25);
       フォントサイズ: 13px;
      }
      /deep/ .el-input__inner {
       高さ: 40px;
       行の高さ: 40px;
      }
      /deep/ .el-select {
       .el-input--サフィックス {
        /deep/ 入力::プレースホルダー {
         色: rgba(0, 0, 0, 0.25);
         フォントサイズ: 13px;
        }
        .el-input__inner {
         境界線: なし;
        }
        .el-input__inner:ホバー{
         背景: rgba(95, 149, 247, 0.05);
        }
       }
      }
      .el-date-editor {
       幅: 100%;
       フォントサイズ: 12px;
      }
      .el-range-editor.el-input__inner {
       左パディング: 2px;
       右パディング: 0;
      }
      /deep/.el-range-input {
       フォントサイズ: 13px !重要;
      }
      /deep/ .el-range-separator {
       パディング: 0 !重要;
       フォントサイズ: 12px !重要;
       幅: 8% !重要;
       マージン: 0;
      }
      /deep/ .el-range__close-icon {
       幅: 16px;
      }
     }
    }
   }
   .filterFooter {
    幅: 344ピクセル;
    高さ: 48px;
    ディスプレイ: フレックス;
    コンテンツの両端揃え: スペースの間;
    アイテムの位置を中央揃えにします。
    ボックスのサイズ: 境界線ボックス;
    左パディング: 24px;
    右パディング: 12px;
    上境界線: 1px 実線 #e9e9e9;
    .filterBtn {
     .filterMaskTwo {
      位置: 固定;
      上: 0;
      左: 0;
      幅: 100%;
      高さ: 100%;
      背景色: rgba(0, 0, 0, 0.3);
      zインデックス: 1000;
      .filterMaskContentTwo {
       幅: 240ピクセル;
       高さ: 320px;
       背景: #ffffff;
       ボックスの影: 0px 0px 4px 3px rgba(194, 194, 194, 0.25);
       境界線の半径: 4px;
       位置: 絶対;
       上: 360ピクセル;
       右: 180px;
       オーバーフロー-y: 自動;
       ボックスのサイズ: 境界線ボックス;
       パディング: 12px 0 18px 0;
       オーバーフロー-x:非表示;
       .innerPrompt {
        色: #666;
        幅: 100%;
        左パディング: 20px;
        上マージン: 12px;
       }
       .contentTwoItem{
        幅: 100%;
        高さ: 36px;
        行の高さ: 36px;
        フォントサイズ: 14px;
        色: #333333;
        カーソル: ポインタ;
        .ミンジ {
         幅: 100%;
         高さ: 36px;
         ボックスのサイズ: 境界線ボックス;
         左パディング: 18px;
        }
       }
       .contentTwoItem:ホバー{
        背景: rgba(95, 149, 247, 0.05);
       }
      }
     }
    }
   }
  }
 }
}
// フェードインとフェードアウト効果を制御します。fade-enter-active、
.フェードアウトアクティブ{
 遷移: 不透明度 0.3 秒;
}
.フェードイン、
.フェードアウト{
 不透明度: 0;
}
</スタイル>

要約する

ここで注意する必要があるのは、マウスを動かすと、対応する小さな削除アイコンが表示されることです。大体こんな感じです。コードを書くのは簡単ではないので、一緒に頑張りましょう。

以上が、vue が Ele.me UI を利用して teambition のフィルタリング機能を模倣する詳細です。vue による teambition のフィルタリング機能の模倣の詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • Vue プロジェクトを作成し、Ele.me elementUI コンポーネントを導入する手順
  • vue.js テンプレートの使用 (Ele.me のレイアウトを模倣)
  • Vue2.0 で Ele.me Web アプリのシングルページ アプリケーションを模倣する詳細な手順
  • VUE の Ele.me ツリー コントロールに追加、削除、変更機能を追加するためのサンプル コード
  • Vueはページングと入力ボックスのキーワードフィルタリング機能を実装します
  • VUEはモバイルリストフィルタリング機能を実装
  • Vue ベースの多条件フィルタリング機能の詳細説明 (JD.com や Taobao の機能に類似)
  • vue-router beforEachを使用して、ユーザーログインジャンプルートフィルタリングを判断する機能を実装する

<<:  異なるドメイン名への PC または携帯電話のアクセスを区別するように Nginx を構成する方法

>>:  現在使用されている設定ファイル my.cnf を表示する mysql メソッド (推奨)

推薦する

MySQL UPDATE ステートメントの非標準実装コード

今日は、MySQL データベースと SQL 標準 (および他のデータベース) の UPDATE ステ...

MySQL マスターとスレーブの不整合とその解決策の詳細な説明

1. MySQL マスタースレーブ非同期1.1 ネットワーク遅延MySQLのマスタースレーブレプリケ...

CSS クロスフェード() を使用して半透明の背景画像効果を実現するサンプルコード

1. 要件の説明特定の要素については、背景background-imageを半透明にしたいが、テキス...

HTML の水平および垂直中央揃えの問題の概要

最近、センタリングの問題に数多く遭遇したので、後で簡単に見つけられるように、時間をかけてそれらを要約...

Mysql 5.6.37 winx64 インストール デュアル バージョン mysql ノート

マシンに MySQL バージョン 5.0 がすでに存在する場合は、最新バージョンの MySQL のイ...

MySQLトランザクションの特徴と分離レベルについてお話ししましょう

インターネットにはすでにこの種の記事が溢れていますが、私がこれをまだ書いている理由は単純です。それは...

MySQLマスタースレーブ遅延現象と原理の詳細な分析

1. 現象早朝、オンライン テーブルにインデックスが追加されました。テーブル内のデータ量が大きすぎた...

Vueはズームイン、ズームアウト、ドラッグ機能を実装しています

この記事では、参考までに、ズームインとズームアウトのドラッグ機能を実現するためのVueの具体的なコー...

Firefox ブラウザでバックグラウンド ミュージックを再生するための究極のソリューション (Chrome マルチブラウザ対応)

FirefoxでBGMを再生するための推奨コードがテストに合格しました空のコントロールパネルを開いて...

LinuxでPythonの組み込みバージョンを削除する手順の詳細な説明

大きな落とし穴、Linuxシステムに付属するPythonのバージョンを簡単に削除しないでください1....

Raspberry PiにDockerをインストールする方法

Raspberry Pi は ARM アーキテクチャをベースとしているため、Docker のインスト...

Vue がコンポーネント通信を実装する 8 つの例

目次1. Props 親コンポーネント ---> 子コンポーネント通信2. $emit 子コン...

MySQL トリガーの紹介、トリガーの作成、使用制限の分析

この記事では、例を使用して、MySQL トリガーの概要、トリガーの作成方法、およびトリガーの使用上の...

JavaScript の遅延読み込み属性パターンを理解する

従来、開発者はインスタンスで必要になる可能性のあるデータに対して JavaScript クラス内にプ...

Ubuntu 16.04 で PostgreSQL の起動を設定する方法

PostgreSQL はコンパイルされインストールされるため、起動時に起動するように設定する必要があ...