前段時間有朋友私信我 Vue + TypeScript 的問題,然後就打算寫一篇 Vue + TypeScript 封裝組件的文章 正好公司項目中需要封裝一個表頭查詢組件,就拿出來分享一下~ 組件的整體思路是通過一個 config 數組生成列表的頭部表單: PS:配合《Vue 爬坑之路(九)—— 用 ...
前段時間有朋友私信我 Vue + TypeScript 的問題,然後就打算寫一篇 Vue + TypeScript 封裝組件的文章
正好公司項目中需要封裝一個表頭查詢組件,就拿出來分享一下~
組件的整體思路是通過一個 config 數組生成列表的頭部表單:
PS:配合《Vue 爬坑之路(九)—— 用正確的姿勢封裝組件》食用更佳
一、組件設計
這個組件由兩部分組成:輸入組件和按鈕
其中輸入組件可以通過 v-for 迴圈渲染,並通過 v-if 來切換輸入框 input 和下拉框 select
每個輸入組件都有各自的 v-model,可以在 config 傳入對應的 code 來綁定對應的參數
基於這些想法,組件的基本結構就出來了:
由此可以設計出 config 的數據結構 data.ts:
/* * data.ts * * 數據類型 - table-header 組件 */ export class SelectOptionItem { public value: String | Number; public label: String | Number; } export class HeaderConfigItem { public title: String; public code: String; public type?: 'select' | 'input'; public options?: SelectOptionItem[] }
二、內部邏輯
整個組件需要傳入兩個必選參數:config 和 data
data 是整個表頭的數據對象,config 就是整個組件的配置項,由此渲染出頭部結構
然後還有“查詢”和“清空”兩個按鈕
這類公共組件不建議直接處理事件,所以通過 emit 將事件拋給父組件處理
這裡的 this._copy 是 data 的拷貝對象,在 mounted 的時候將 data 拷貝出來作為初始值,清空的時候再將這個初始值傳回去
這裡會涉及到在子組件中對父組件傳入的參數直接修改,所以需要用 sync 修飾符
三、完整代碼
除了這些基本邏輯之外,我還添加了一個 size 用於控制整體的尺寸,然後基於自身的項目微調了樣式,所以這部分僅做參考
<template> <div class="table-header"> <el-form :inline="true" :model="data" class="form--label-left" label-width="180px"> <el-row :gutter="20"> <el-col :span="8" v-for="item in config" :key="item.code"> <el-form-item :label="item.title" class="table-header-item"> <el-select v-if="item.type === 'select'" v-model="data[item.code]" :placeholder="`請輸入${item.title}`" :size="size" clearable> <el-option v-for="option in item.options" :key="option.value" :value="option.value" :label="option.label"></el-option> </el-select> <el-input v-else v-model="data[item.code]" :placeholder="`請輸入${item.title}`" :size="size"></el-input> </el-form-item> </el-col> <el-col :span="8" class="table-header_button"> <el-button :size="size" type="text" @click="reset">清空</el-button> <el-button :size="size" type="primary" icon="el-icon-search" @click="search">查詢</el-button> </el-col> </el-row> </el-form> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; import { HeaderConfigItem } from "./data.ts"; @Component({}) export default class TableHeader extends Vue { public _copy: Object = {} @Prop({ default: function () { return 'small' }}) size: 'small' | 'mini' | 'medium' @Prop({}) data: Object @Prop({default: []}) config: HeaderConfigItem[] mounted() { this._copy = Object.assign({}, this.data) } // 查詢 search() { this.$emit('search', this.data) } // 清空 reset() { this.$emit('update:data', Object.assign({}, this._copy)) this.search() } } </script> <style lang="scss"> .table-header { padding-top: 10px; .table-header_button { text-align: right; float: right; margin-bottom: 12px; line-height: 40px; } .table-header-item.el-form-item { width: 100%; display: flex; flex: auto; margin-bottom: 12px; .el-form-item__content, .el-select { width: 100%; } } } </style>
父組件調用: