91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Element?Plus組件Form表單Table表格二次封裝怎么實現

發布時間:2022-09-05 11:41:11 來源:億速云 閱讀:226 作者:iii 欄目:開發技術

這篇文章主要介紹了Element Plus組件Form表單Table表格二次封裝怎么實現的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Element Plus組件Form表單Table表格二次封裝怎么實現文章都會有所收獲,下面我們一起來看看吧。

    Form表單的封裝

    正常的使用

    如果我們正常使用組件庫里面的組件會是這樣的

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    代碼如下

    role.vue頁面組件

    <template>
      <div class="role">
        <el-form>
          <el-form-item label="用戶id">
            <el-input placeholder="請輸入用戶id"></el-input>
          </el-form-item>
          <el-form-item label="用戶名">
            <el-input placeholder="請輸入用戶名"></el-input>
          </el-form-item>
          <el-form-item label="真實姓名">
            <el-input placeholder="請輸入真實姓名"></el-input>
          </el-form-item>
          <el-form-item label="用戶名">
            <el-input placeholder="請輸入用戶名"></el-input>
          </el-form-item>
          <el-form-item label="電話號碼">
            <el-input placeholder="請輸入電話號碼"></el-input>
          </el-form-item>
          <el-form-item label="用戶狀態">
            <el-select placeholder="請選擇用戶狀態">
              <el-option label="禁用" value="0"></el-option>
              <el-option label="啟用" value="1"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="創建時間">
            <el-date-picker
              startPlaceholder="開始時間"
              endPlaceholder="結束時間"
              type="daterange"
            ></el-date-picker>
          </el-form-item>
        </el-form>
      </div>
    </template>
    
    <script setup lang="ts"></script>
    
    <style scoped lang="less"></style>

    這時我們可以加點樣式讓他變得好看,并且布局也變一變就可以變成這樣,當然樣式布局可以自定義

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    代碼如下

    role.vue頁面組件

    <template>
      <div class="role">
        <el-form labelWidth="120px">
          <el-row>
            <el-col :span="8">
              <el-form-item
                label="用戶id"
                :style="{
                  padding: '10px 20px'
                }"
              >
                <el-input placeholder="請輸入用戶id"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item
                label="用戶名"
                :style="{
                  padding: '10px 20px'
                }"
              >
                <el-input placeholder="請輸入用戶名"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item
                label="真實姓名"
                :style="{
                  padding: '10px 20px'
                }"
              >
                <el-input placeholder="請輸入真實姓名"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item
                label="電話號碼"
                :style="{
                  padding: '10px 20px'
                }"
              >
                <el-input placeholder="請輸入電話號碼"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item
                label="用戶狀態"
                :style="{
                  padding: '10px 20px'
                }"
              >
                <el-select placeholder="請選擇用戶狀態">
                  <el-option label="禁用" value="0"></el-option>
                  <el-option label="啟用" value="1"></el-option>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item
                label="創建時間"
                :style="{
                  padding: '10px 20px'
                }"
              >
                <el-date-picker
                  startPlaceholder="開始時間"
                  endPlaceholder="結束時間"
                  type="daterange"
                ></el-date-picker>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
    </template>
    
    <script setup lang="ts"></script>
    
    <style scoped lang="less">
    .el-form-item {
      margin-top: 18px;
    }
    .el-select {
      width: 100%;
    }
    </style>

    開始封裝①

    這時我們就可以開始封裝了,如果我們可以通過傳配置項的方法來控制樣式和form表單項的類型和個數的話,是不是變得很方便,下次直接傳配置項用就好了?話不多說直接上圖上代碼

    可以看到效果一樣,代碼卻簡潔了,模板里面不會出現大量重復的代碼了

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    role.vue頁面組件

    <template>
      <div class="role">
        <el-form :labelWidth="searchFormConfig.labelWidth">
          <el-row>
            <template v-for="item in searchFormConfig.formItems" :key="item.label">
              <el-col :span="8">
                <el-form-item
                  :label="item.label"
                  :
                >
                  <template
                    v-if="item.type === 'input' || item.type === 'password'"
                  >
                    <el-input 
                        :placeholder="item.placeholder" 
                        :show-password="item.type === 'password'"
                    ></el-input>
                  </template>
                  <template v-else-if="item.type === 'select'">
                    <el-select :placeholder="item.placeholder">
                      <el-option
                        v-for="option in item.options"
                        :key="option.value"
                        :label="option.label"
                        :value="option.value"
                      ></el-option>
                    </el-select>
                  </template>
                  <template v-else>
                    <el-date-picker v-bind="item.otherOptions"></el-date-picker>
                  </template>
                </el-form-item>
              </el-col>
            </template>
          </el-row>
        </el-form>
      </div>
    </template>
    
    <script setup lang="ts">
    // 定義表單配置項
    const searchFormConfig = {
      formItems: [
        {
          type: 'input',
          label: '用戶id',
          placeholder: '請輸入用戶id'
        },
        {
          type: 'input',
          label: '用戶名',
          placeholder: '請輸入用戶名'
        },
        {
          type: 'input',
          label: '真實姓名',
          placeholder: '請輸入真實姓名'
        },
        {
          type: 'input',
          label: '電話號碼',
          placeholder: '請輸入電話號碼'
        },
        {
          type: 'select',
          label: '用戶狀態',
          placeholder: '請選擇用戶狀態',
          options: [
            {
              label: '禁用',
              value: 0
            },
            {
              label: '啟用',
              value: 1
            }
          ]
        },
        {
          type: 'datepicker',
          label: '創建時間',
          otherOptions: {
            startPlaceholder: '開始時間',
            endPlaceholder: '結束時間',
            type: 'daterange',
            'unlink-panels': true
          }
        }
      ],
      labelWidth: '120px',
      itemStyle: {
        padding: '10px 20px'
      },
      itemColLayout: {
        span: 8
      }
    }
    </script>
    
    <style scoped lang="less">
    .el-form-item {
      margin-top: 18px;
    }
    .el-select {
      width: 100%;
    }
    </style>

    開始封裝②

    這時它復用的錐形已經有了,我們可以將配置項抽出去,并給它一些類型限制,把這部分使用表單的代碼抽出去,封裝成form組件,這樣之后我們在用的時候,直接用這個組件然后給它傳配置項就可以了

    1.配置項類型限制文件(不用ts的話就沒有,不想限制全部給any類型隨意,我這里是為了讓代碼嚴謹一丟丟哈哈)

    type IFormType = 'input' | 'password' | 'select' | 'datepicker'
    
    interface IFormOption {
      label: string
      value: string | number
    }
    
    export interface IFormItem {
      type: IFormType //輸入框類型
      label: string //輸入框標題
      placeholder?: any //輸入框默認顯示內容
      // 針對select
      options?: IFormOption[] //選擇器的可選子選項
      // 針對特殊屬性
      otherOptions?: any
    
    }
    
    export interface IForm {
      formItems: IFormItem[]
      labelWidth?: string
      itemStyle?: any
      itemColLayout?: any
    }

    2.配置項文件

    import { IForm } from '@/base-ui/form/type'
    export const searchFormConfig: IForm = {
      formItems: [
        {
          type: 'input',
          label: '用戶id',
          placeholder: '請輸入用戶id'
        },
        {
          type: 'input',
          label: '用戶名',
          placeholder: '請輸入用戶名'
        },
        {
          type: 'input',
          label: '真實姓名',
          placeholder: '請輸入真實姓名'
        },
        {
          type: 'input',
          label: '電話號碼',
          placeholder: '請輸入電話號碼'
        },
        {
          type: 'select',
          label: '用戶狀態',
          placeholder: '請選擇用戶狀態',
          options: [
            { label: '啟用', value: 1 },
            { label: '禁用', value: 0 }
          ]
        },
        {
          type: 'datepicker',
          label: '創建時間',
          otherOptions: {
            startPlaceholder: '開始時間',
            endPlaceholder: '結束時間',
            type: 'daterange'
          }
        }
      ],
      labelWidth: '120px',
      itemColLayout: {
        span: 8
      },
      itemStyle: {
        padding: '10px 20px'
      }
    }

    3.form表單文件

    注意:在這里,我將labelWidth,itemColLayout,itemStyle設置了默認值,所以我上面的那些樣式配置項可以不傳,默認就是我設置的那些值,如果需要別的樣式可以傳入修改,不要樣式可以傳個空進去,這里我還加了兩個插槽,增加可擴展性

    <template>
      <div class="header">
        <slot name="header"> </slot>
      </div>
      <el-form ref="ruleFormRef" :labelWidth="labelWidth">
        <el-row>
          <template v-for="item in formItems" :key="item.label">
            <el-col v-bind="itemColLayout">
              <el-form-item
                v-if="!item.isHidden"
                :label="item.label"
                :
                :prop="item.field"
              >
                <template v-if="item.type === 'input' || item.type === 'password'">
                  <el-input
                    :placeholder="item.placeholder"
                    :show-password="item.type === 'password'"
                  ></el-input>
                </template>
                <template v-else-if="item.type === 'select'">
                  <el-select :placeholder="item.placeholder">
                    <el-option
                      v-for="option in item.options"
                      :key="option.label"
                      :label="option.label"
                      :value="option.value"
                    ></el-option>
                  </el-select>
                </template>
                <template v-if="item.type === 'datepicker'">
                  <el-date-picker v-bind="item.otherOptions"></el-date-picker>
                </template>
              </el-form-item>
            </el-col>
          </template>
        </el-row>
      </el-form>
      <div class="footer">
        <slot name="footer"></slot>
      </div>
    </template>
    
    <script setup lang="ts">
    import { defineProps, withDefaults } from 'vue'
    import { IFormItem } from './type'
    interface Prop {
      formItems: IFormItem[] // 表單配置項
      labelWidth?: string // 每個表單標題寬度
      itemStyle?: object // 每個表單樣式
      itemColLayout?: object // 表單布局
      isHidden?: boolean // 該輸入框是否隱藏
    }
    const props = withDefaults(defineProps<Prop>(), {
      labelWidth: '120px',
      itemColLayout: () => ({
        xl: 6, // >=1920px
        lg: 8, // >=1200px
        md: 12, // >=992px
        sm: 24, // >=768px
        xs: 24 // <768px
      }),
      itemStyle: () => ({
        padding: '10px 20px'
      })
    })
    </script>
    
    <style scoped>
    .el-form-item {
      margin-top: 18px;
    }
    .el-select {
      width: 100%;
    }
    </style>

    4.role.vue頁面組件

    <template>
      <div class="role">
        <form-test v-bind="searchFormConfig"></form-test>
      </div>
    </template>
    
    <script setup lang="ts">
    import formTest from '@/base-ui/form/form-test.vue'
    import { searchFormConfig } from './config/search-config-test'
    </script>
    
    <style scoped lang="less"></style>

    這時已經初步封裝好了,我們可以使用一下看效果,我們可以看到樣式跟之前完全一樣,但是頁面的代碼量就那么點,要用的話直接用我們封裝好的form組件然后傳入配置項就出來了

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    它的可擴展性也是很強的,比如:

    這里我們把樣式配置項全部傳空值,然后配置項也傳一個,它又變成原來最丑的樣子了,證明我們是可以隨意更改它的樣式和布局,只需要通過傳入配置項更改就可以了,方便

    配置項文件

    import { IForm } from '@/base-ui/form/type'
    export const searchFormConfig: IForm = {
      formItems: [
        {
          field: 'id',
          type: 'input',
          label: '用戶id',
          placeholder: '請輸入用戶id'
        }
      ],
      labelWidth: '',
      itemColLayout: {},
      itemStyle: {}
    }

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    其實到這里還沒結束,因為這時的表單還輸入不了東西,因為我們根本就沒給它的輸入框綁定值,所以我們要在配置項傳入多一個field字段,它可以作為輸入框綁定的值

    開始封裝③

    這里僅僅是給配置項中增加field字段(注意如果用了ts的還要去type文件里面給我們定義的IFormItem接口添加一個field字段)

    配置項文件

    import { IForm } from '@/base-ui/form/type'
    export const searchFormConfig: IForm = {
      formItems: [
        {
          field: 'id',
          type: 'input',
          label: '用戶id',
          placeholder: '請輸入用戶id'
        },
        {
          field: 'name',
          type: 'input',
          label: '用戶名',
          placeholder: '請輸入用戶名'
        },
        {
          field: 'realname',
          type: 'input',
          label: '真實姓名',
          placeholder: '請輸入真實姓名'
        },
        {
          field: 'cellphone',
          type: 'input',
          label: '電話號碼',
          placeholder: '請輸入電話號碼'
        },
        {
          field: 'enable',
          type: 'select',
          label: '用戶狀態',
          placeholder: '請選擇用戶狀態',
          options: [
            { label: '啟用', value: 1 },
            { label: '禁用', value: 0 }
          ]
        },
        {
          field: 'createAt',
          type: 'datepicker',
          label: '創建時間',
          otherOptions: {
            startPlaceholder: '開始時間',
            endPlaceholder: '結束時間',
            type: 'daterange'
          }
        }
      ],
      labelWidth: '120px',
      itemColLayout: {
        span: 8
      },
      itemStyle: {
        padding: '10px 20px'
      }
    }

    因為傳入了fied字段,所以我們要收集所有的field字段,組成formData數據,傳入表單組件,formData里面的每個子項分別作為每個輸入框綁定的值

    注意:這里有兩個難點

    難點一:

    我們傳進去的數據在里面是要做修改傳出來的,而vue的原則是單項數據流傳輸,我們不能直接將數據傳進去(其實事實可以這樣做,但是違背了單向數據流傳輸原則,我們盡量不違背哈),所以我們采用v-model的方式將formData傳入form組件,這樣做的話就是雙向判定了,不算違背嘿嘿

    難點二:因為我們傳進去的formData的數據,并不是在form組件里面用的,而是要綁定到form組件里面的element puls的輸入框里面的,所以我們在form組件里面接收到formData數據,然后在把formData它的各個子項v-model綁定到輸入框里面,但是這樣會報錯,不能直接用v-model,這里就需要知道v-model是怎么實現的了,我們在這里是直接把接收到的formData數據綁定到輸入框里面的,在form組件并沒有定義formData這個變量,所以不能直接用v-model的方法,這了可能有點懵,舉個例子

    (比如你將一個值test用v-model傳入一個input的框,你輸入框輸入數據,你的test是會同步改變,也就是說,v-model會把你修改后的值傳出來賦值給你的test,而在這里,我們將formData用v-model綁定到輸入框,輸入框值改變,正常來說它會將修改后的值賦值給我們傳進去的formData,但是我們不能讓它直接賦值給我們的formData,因為我們的formData也是從別的組件傳進來的,所以我們要把修改后的值再次傳出去到傳進來formData數據的那個組件中,而不是直接就賦值,這時我們就要用到v-model的原始寫法了,其實v-model是個語法糖來的)

    form.vue組件

    <template>
      <div class="header">
        <slot name="header"> </slot>
      </div>
      <el-form ref="ruleFormRef" :labelWidth="labelWidth">
        <el-row>
          <template v-for="item in formItems" :key="item.label">
            <el-col v-bind="itemColLayout">
              <el-form-item
                v-if="!item.isHidden"
                :label="item.label"
                :
              >
                <template v-if="item.type === 'input' || item.type === 'password'">
                  <el-input
                    :placeholder="item.placeholder"
                    :show-password="item.type === 'password'"
                    :model-value="modelValue[`${item.field}`]"
                    @update:modelValue="valueChange($event, item.field)"
                  ></el-input>
                </template>
                <template v-else-if="item.type === 'select'">
                  <el-select
                    :placeholder="item.placeholder"
                    :model-value="modelValue[`${item.field}`]"
                    @update:modelValue="valueChange($event, item.field)"
                  >
                    <el-option
                      v-for="option in item.options"
                      :key="option.label"
                      :label="option.label"
                      :value="option.value"
                    ></el-option>
                  </el-select>
                </template>
                <template v-if="item.type === 'datepicker'">
                  <el-date-picker
                    v-bind="item.otherOptions"
                    :model-value="modelValue[`${item.field}`]"
                    @update:modelValue="valueChange($event, item.field)"
                  ></el-date-picker>
                </template>
              </el-form-item>
            </el-col>
          </template>
        </el-row>
      </el-form>
      <div class="footer">
        <slot name="footer"></slot>
      </div>
    </template>
    
    <script setup lang="ts">
    import { defineProps, withDefaults, defineEmits } from 'vue'
    import { IFormItem } from './type'
    interface Prop {
      formItems: IFormItem[] // 表單配置項
      labelWidth?: string // 每個表單標題寬度
      itemStyle?: object // 每個表單樣式
      itemColLayout?: object // 表單布局
      isHidden?: boolean // 該輸入框是否隱藏
      modelValue: object //綁定表單的每個數據
    }
    const props = withDefaults(defineProps<Prop>(), {
      labelWidth: '120px',
      itemColLayout: () => ({
        xl: 6, // >=1920px
        lg: 8, // >=1200px
        md: 12, // >=992px
        sm: 24, // >=768px
        xs: 24 // <768px
      }),
      itemStyle: () => ({
        padding: '10px 20px'
      })
    })
    const emit = defineEmits<{
      (e: 'update:modelValue', value: any): void
    }>()
    
    // 輸入框值改變該函數都會觸發,將改變后的值傳出去
    const valueChange = (value: any, field: string) => {
      emit('update:modelValue', { ...props.modelValue, [field]: value })
    }
    </script>
    
    <style scoped>
    .el-form-item {
      margin-top: 18px;
    }
    .el-select {
      width: 100%;
    }
    </style>

    role.vue頁面組件

    <template>
      <div class="role">
        <form-test v-bind="searchFormConfig" v-model="formData"></form-test>
      </div>
    </template>
    
    <script setup lang="ts">
    import formTest from '@/base-ui/form/form-test.vue'
    import { searchFormConfig } from './config/search-config-test'
    import { ref } from 'vue'
    // 在這里取出所有的field字段組成formData數據
    const formItems = searchFormConfig.formItems ?? []
    
    let formDataInit = {}
    formItems.map((item) => {
      formDataInit[item.field] = ''
    })
    let formData = ref(formDataInit)
    </script>
    
    <style scoped lang="less"></style>

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    這時我們發現它可以拿到數據了,很nice,其實這差不多已經算封裝好了,可以通過配置項修改里面的東西了,同時也可以拿到數據,但是我這個項目不止于此,我這其實要做表單的查詢的,所以我要改裝一下變成這樣

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    其實就是加了兩個插槽和兩個方法,我這里要實現功能就是點擊重置按鈕,它會重置表單數據,點擊搜索按鈕就可以拿到表單數據,這樣我們就可以用我們拿到的表單數據去進行我們的操作拉,所以上代碼

    role.vue組件

    該部分我們傳入了兩個template,一個是標題:高級檢索,一個是兩個按鈕

    這里要重置按鈕重置表單數據,取到表單的ref調用resetFields方法就好了,然后點擊搜索按鈕可以打印出formData數據,然后我們就可以利用該數據去做我們想要做的操作了,例如查詢列表數據等

    <template>
      <div class="role">
        <form-test v-bind="searchFormConfig" v-model="formData" ref="formTestRef">
          <template #header>
            <div class="header">
              <h2>高級檢索</h2>
            </div>
          </template>
          <template #footer>
            <div class="footer">
              <el-button type="primary" :icon="Refresh" @click="resetBtnClick"
                >重置</el-button
              >
              <el-button type="primary" :icon="Search" @click="searchBtnClick"
                >搜索</el-button
              >
            </div>
          </template>
        </form-test>
      </div>
    </template>
    
    <script setup lang="ts">
    import formTest from '@/base-ui/form/form-test.vue'
    import { searchFormConfig } from './config/search-config-test'
    import { ref } from 'vue'
    import { Search, Refresh } from '@element-plus/icons-vue'
    // 在這里取出所有的field字段組成formData數據
    const formItems = searchFormConfig.formItems ?? []
    
    let formDataInit = {}
    formItems.map((item) => {
      formDataInit[item.field] = ''
    })
    let formData = ref(formDataInit)
    
    const formTestRef = ref<InstanceType<typeof formTest>>()
    
    // 重置點擊
    const resetBtnClick = () => {
      formTestRef.value?.resetForm()
    }
    // 搜索點擊
    const searchBtnClick = () => {
      // 這里需要遍歷搜索配置項,配置項里可以傳dataType,要求數據返回什么類型的數據
      let queryInfo = { ...formData.value }
      searchFormConfig.formItems.map((item: any) => {
        if (item.dataType === 'number' && queryInfo[item.field] !== '') {
          queryInfo[item.field] = Number(queryInfo[item.field])
        }
      })
      // 清空queryInfo中沒有值的屬性
      for (const i in queryInfo) {
        if (queryInfo[i] === '') {
          delete queryInfo[i]
        }
      }
      console.log(queryInfo)
    }
    </script>
    
    <style scoped lang="less">
    .header {
      padding-top: 20px;
    }
    .footer {
      text-align: right;
      padding: 0 50px 20px 0;
    }
    </style>

    form.vue

    <template>
      <div class="header">
        <slot name="header"> </slot>
      </div>
      <el-form ref="ruleFormRef" :labelWidth="labelWidth" :model="modelValue">
        <el-row>
          <template v-for="item in formItems" :key="item.label">
            <el-col v-bind="itemColLayout">
              <el-form-item
                v-if="!item.isHidden"
                :label="item.label"
                :
                :prop="item.field"
              >
                <template v-if="item.type === 'input' || item.type === 'password'">
                  <el-input
                    :placeholder="item.placeholder"
                    :show-password="item.type === 'password'"
                    :model-value="modelValue[`${item.field}`]"
                    @update:modelValue="valueChange($event, item.field)"
                  ></el-input>
                </template>
                <template v-else-if="item.type === 'select'">
                  <el-select
                    :placeholder="item.placeholder"
                    :model-value="modelValue[`${item.field}`]"
                    @update:modelValue="valueChange($event, item.field)"
                  >
                    <el-option
                      v-for="option in item.options"
                      :key="option.label"
                      :label="option.label"
                      :value="option.value"
                    ></el-option>
                  </el-select>
                </template>
                <template v-if="item.type === 'datepicker'">
                  <el-date-picker
                    v-bind="item.otherOptions"
                    :model-value="modelValue[`${item.field}`]"
                    @update:modelValue="valueChange($event, item.field)"
                  ></el-date-picker>
                </template>
              </el-form-item>
            </el-col>
          </template>
        </el-row>
      </el-form>
      <div class="footer">
        <slot name="footer"></slot>
      </div>
    </template>
    
    <script setup lang="ts">
    import { defineProps, withDefaults, defineEmits, ref, defineExpose } from 'vue'
    import { IFormItem } from './type'
    import type { FormInstance } from 'element-plus'
    
    interface Prop {
      formItems: IFormItem[] // 表單配置項
      labelWidth?: string // 每個表單標題寬度
      itemStyle?: object // 每個表單樣式
      itemColLayout?: object // 表單布局
      isHidden?: boolean // 該輸入框是否隱藏
      modelValue: object //綁定表單的每個數據
    }
    const props = withDefaults(defineProps<Prop>(), {
      labelWidth: '120px',
      itemColLayout: () => ({
        xl: 6, // >=1920px
        lg: 8, // >=1200px
        md: 12, // >=992px
        sm: 24, // >=768px
        xs: 24 // <768px
      }),
      itemStyle: () => ({
        padding: '10px 20px'
      })
    })
    const emit = defineEmits<{
      (e: 'update:modelValue', value: any): void
    }>()
    const ruleFormRef = ref<FormInstance>()
    
    // 輸入框值改變該函數都會觸發,將改變后的值傳出去
    const valueChange = (value: any, field: string) => {
      emit('update:modelValue', { ...props.modelValue, [field]: value })
    }
    
    // 表單重置方法
    const resetForm = () => {
      ruleFormRef.value?.resetFields()
    }
    defineExpose({
      resetForm
    })
    </script>
    
    <style scoped>
    .el-form-item {
      margin-top: 18px;
    }
    .el-select {
      width: 100%;
    }
    </style>

    1.該組件要添加表單重置的方法

    2.把formData的值綁定到form表單上:model=&lsquo;formData&rsquo;

    3.給el-form-item加上prop屬性

    2,3如果不加上的話,表單內置的重置表單方法會失效

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    這時我們已經封裝完成了,但是我們可以發現,我們的role組件東西有點多了,如果我們其他組件比如,user組件等,都要用這樣類似的布局,我們這時就又要把這一堆代碼給cv一遍,所以我們有可以把role里面這堆東西再封裝一次

    開始封裝④

    page-search.vue組件

    <template>
      <Bu-form v-bind="searchFormConfig" v-model="formData" ref="BuFormRef">
        <template #header>
          <div class="header">
            <h2>高級檢索</h2>
          </div>
        </template>
        <template #footer>
          <div class="footer">
            <el-button type="primary" :icon="Refresh" @click="resetBtnClick"
              >重置</el-button
            >
            <el-button type="primary" :icon="Search" @click="searchBtnClick"
              >搜索</el-button
            >
          </div>
        </template>
      </Bu-form>
    </template>
    
    <script setup lang="ts">
    import { Search, Refresh } from '@element-plus/icons-vue'
    import BuForm from '@/base-ui/form/form-test.vue'
    import { defineProps, ref, defineEmits } from 'vue'
    import { useStore } from '@/store'
    interface Prop {
      searchFormConfig: any
    }
    const props = defineProps<Prop>()
    const emit = defineEmits<{
      (e: 'resetBtnClick'): void
      (e: 'searchBtnClick', formData: object): void
    }>()
    const store = useStore()
    const BuFormRef = ref<InstanceType<typeof BuForm>>()
    
    const formItems = props.searchFormConfig?.formItems ?? []
    
    let formDataInit = {}
    formItems.map((item: any) => {
      formDataInit[item.field] = ''
    })
    let formData = ref(formDataInit)
    
    // 重置點擊
    const resetBtnClick = () => {
      BuFormRef.value?.resetForm()
      emit('resetBtnClick')
    }
    // 搜索點擊
    const searchBtnClick = () => {
      // 這里需要遍歷搜索配置項,配置項里可以傳dataType,要求數據返回什么類型的數據
      let queryInfo = { ...formData.value }
      props.searchFormConfig.formItems.map((item: any) => {
        if (item.dataType === 'number' && queryInfo[item.field] !== '') {
          queryInfo[item.field] = Number(queryInfo[item.field])
        }
      })
      // 清空queryInfo中沒有值的屬性
      for (const i in queryInfo) {
        if (queryInfo[i] === '') {
          delete queryInfo[i]
        }
      }
      emit('searchBtnClick', queryInfo)
    }
    </script>
    
    <style scoped>
    .header {
      padding-top: 20px;
    }
    .footer {
      text-align: right;
      padding: 0 50px 20px 0;
    }
    </style>

    role.vue組件

    <template>
      <div class="role">
        <page-search-test
          :searchFormConfig="searchFormConfig"
          @resetBtnClick="handlerReset"
          @searchBtnClick="handlerSearch"
        ></page-search-test>
      </div>
    </template>
    
    <script setup lang="ts">
    import { searchFormConfig } from './config/search-config-test'
    import pageSearchTest from '@/components/page-search/page-search-test.vue'
    const handlerReset = () => {
      console.log('點擊了重置按鈕')
    }
    const handlerSearch = (queryInfo: any) => {
      console.log('點擊了搜索按鈕,值為:', queryInfo)
    }
    </script>
    
    <style scoped lang="less"></style>

    效果圖

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    這里我們就可以體會到了,一樣的效果,role里面的代碼是這么的少,只需要傳入配置項就可以搞出這個表單,而且還能拿到表單數據,而且重點是,下個頁面再用這個布局,直接用page-search組件就可以了,只需要傳入不同的配置項,如果不同布局,再封裝另一個page-search就是了,但是這是后臺耶?搞那么華麗呼哨?不就是搜索表單,展示表單么

    下面附上完整全部封裝代碼

    完整封裝代碼⑤

    配置項類型文件
    // type.ts
    
    type IFormType = 'input' | 'password' | 'select' | 'datepicker'
    
    interface IFormOption {
      label: string
      value: string | number
    }
    
    export interface IFormItem {
      field: string //字段名
      type: IFormType //輸入框類型
      dataType?: string //輸入框返回數據類型
      label: string //輸入框標題
      rules?: any[] //輸入框驗證規則
      placeholder?: any //輸入框默認顯示內容
      // 針對select
      options?: IFormOption[] //選擇器的可選子選項
      // 針對特殊屬性
      otherOptions?: any
      // 該行是否隱藏
      isHidden?: boolean
    }
    
    export interface IForm {
      formItems: IFormItem[]
      labelWidth?: string
      itemStyle?: any
      itemColLayout?: any
    }
    配置項文件
    import { IForm } from '@/base-ui/form/type'
    export const searchFormConfig: IForm = {
      formItems: [
        {
          field: 'id',
          type: 'input',
          label: '用戶id',
          placeholder: '請輸入用戶id'
        },
        {
          field: 'name',
          type: 'input',
          label: '用戶名',
          placeholder: '請輸入用戶名'
        },
        {
          field: 'realname',
          type: 'input',
          label: '真實姓名',
          placeholder: '請輸入真實姓名'
        },
        {
          field: 'cellphone',
          type: 'input',
          label: '電話號碼',
          placeholder: '請輸入電話號碼'
        },
        {
          field: 'enable',
          type: 'select',
          label: '用戶狀態',
          placeholder: '請選擇用戶狀態',
          options: [
            { label: '啟用', value: 1 },
            { label: '禁用', value: 0 }
          ]
        },
        {
          field: 'createAt',
          type: 'datepicker',
          label: '創建時間',
          otherOptions: {
            startPlaceholder: '開始時間',
            endPlaceholder: '結束時間',
            type: 'daterange'
          }
        }
      ]
    }
    form表單組件文件
    <template>
      <div class="header">
        <slot name="header"> </slot>
      </div>
      <el-form
        :label-width="labelWidth"
        ref="ruleFormRef"
        status-icon
        :model="modelValue"
      >
        <el-row>
          <template v-for="item in formItems" :key="item.label">
            <el-col v-bind="itemColLayout">
              <el-form-item
                v-if="!item.isHidden"
                :label="item.label"
                :rules="item.rules"
                :
                :prop="item.field"
              >
                <template v-if="item.type === 'input' || item.type === 'password'">
                  <el-input
                    :placeholder="item.placeholder"
                    :show-password="item.type === 'password'"
                    :model-value="modelValue[`${item.field}`]"
                    @update:modelValue="valueChange($event, item.field)"
                    clearable
                  />
                </template>
                <template v-else-if="item.type === 'select'">
                  <el-select
                    :placeholder="item.placeholder"
                    :model-value="modelValue[`${item.field}`]"
                    @update:modelValue="valueChange($event, item.field)"
                    
                    clearable
                  >
                    <el-option
                      v-for="option in item.options"
                      :key="option.value"
                      :value="option.value"
                      :label="option.label"
                    >
                    </el-option>
                  </el-select>
                </template>
                <template v-else-if="item.type === 'datepicker'">
                  <el-date-picker
                    unlink-panels
                    v-bind="item.otherOptions"
                    :model-value="modelValue[`${item.field}`]"
                    @update:modelValue="valueChange($event, item.field)"
                  ></el-date-picker>
                </template>
              </el-form-item>
            </el-col>
          </template>
        </el-row>
      </el-form>
      <div class="footer">
        <slot name="footer"></slot>
      </div>
    </template>
    
    <script setup lang="ts">
    import { IFormItem } from './type'
    import { defineProps, withDefaults, ref, defineEmits, defineExpose } from 'vue'
    import type { FormInstance } from 'element-plus'
    
    // 定義屬性
    interface Props {
      formItems: IFormItem[] // 表單配置項
      labelWidth?: string // 每個表單標題寬度
      itemStyle?: object // 每個表單樣式
      itemColLayout?: object // 表單布局
      modelValue: object //綁定表單的每個數據
      isHidden?: boolean
    }
    const props = withDefaults(defineProps<Props>(), {
      formItems: () => [],
      labelWidth: '120px',
      itemStyle: () => ({ padding: '10px 20px' }),
      itemColLayout: () => ({
        xl: 6, // >=1920px
        lg: 8, // >=1200px
        md: 12, // >=992px
        sm: 24, // >=768px
        xs: 24 // <768px
      })
    })
    const emit = defineEmits<{
      (e: 'update:modelValue', value: any): void
    }>()
    
    const ruleFormRef = ref<FormInstance>()
    
    // 定義方法
    const valueChange = (value: any, field: string) => {
      emit('update:modelValue', { ...props.modelValue, [field]: value })
    }
    
    // 表單重置方法
    const resetForm = () => {
      ruleFormRef.value?.resetFields()
    }
    defineExpose({
      resetForm
    })
    </script>
    
    <style scoped lang="less">
    .el-form-item {
      margin-top: 18px;
    }
    </style>
    page-search組件文件
    <template>
      <div class="page-search">
        <Bu-form v-bind="searchFormConfig" v-model="formData" ref="BuFormRef">
          <template #header>
            <div class="header">
              <h2>高級檢索</h2>
            </div>
          </template>
          <template #footer>
            <div class="footer">
              <el-button type="primary" :icon="Refresh" @click="resetClick"
                >重置</el-button
              >
              <el-button type="primary" :icon="Search" @click="searchClick"
                >搜索</el-button
              >
            </div>
          </template>
        </Bu-form>
      </div>
    </template>
    
    <script setup lang="ts">
    import { useStore } from '@/store'
    import BuForm from '@/base-ui/form/form.vue'
    import { Search, Refresh } from '@element-plus/icons-vue'
    import { ref, defineProps, defineEmits } from 'vue'
    import { IForm } from '@/base-ui/form/type'
    
    // 定義屬性
    interface Props {
      searchFormConfig: IForm
    }
    const props = defineProps<Props>()
    const emit = defineEmits<{
      (e: 'resetBtnClick'): void
      (e: 'searchBtnClick', formData: object): void
    }>()
    const store = useStore()
    const BuFormRef = ref<InstanceType<typeof BuForm>>()
    
    // 1.從接收到的搜索配置中取出各個field,組成表單的數據formData
    const formItems = props.searchFormConfig?.formItems ?? []
    let formDataInit = {}
    formItems.map((item) => {
      formDataInit[item.field] = ''
    })
    let formData = ref(formDataInit)
    
    // 2.重置與搜索功能
    // 重置按鈕觸發
    const resetClick = () => {
      BuFormRef.value?.resetForm()
      emit('resetBtnClick')
    }
    // 搜索按鈕觸發
    const searchClick = () => {
      // 這里需要遍歷搜索配置項,配置項里可以傳dataType,要求數據返回什么類型的數據
      let queryInfo = { ...formData.value }
      props.searchFormConfig.formItems.map((item) => {
        if (item.dataType === 'number' && queryInfo[item.field] !== '') {
          queryInfo[item.field] = Number(queryInfo[item.field])
        }
      })
      // 清空queryInfo中沒有值的屬性
      for (const i in queryInfo) {
        if (queryInfo[i] === '') {
          delete queryInfo[i]
        }
      }
      emit('searchBtnClick', queryInfo)
    }
    </script>
    
    <style scoped>
    .header {
      padding-top: 20px;
    }
    .footer {
      text-align: right;
      padding: 0 50px 20px 0;
    }
    </style>
    role頁面組件文件
    <template>
      <div class="role">
        <page-search
          :searchFormConfig="searchFormConfig"
          @resetBtnClick="handlerReset"
          @searchBtnClick="handlerSearch"
        ></page-search>
      </div>
    </template>
    
    <script setup lang="ts">
    import { searchFormConfig } from './config/search-config-test'
    import pageSearch from '@/components/page-search/page-search.vue'
    const handlerReset = () => {
      console.log('點擊了重置按鈕')
    }
    const handlerSearch = (queryInfo: any) => {
      console.log('點擊了搜索按鈕,值為:', queryInfo)
    }
    </script>
    
    <style scoped lang="less"></style>
    結語

    寫了這么多,終于寫完了,這里是屬于Form表單部分的封裝全部過程,能寫到這其實我挺有成就感的哈哈哈哈,因為我剛學會的時候思路有了,但是敲出來有點困難,不過這個過程是我又捋了一遍,然后自己敲出來的,感覺其實也不難,已經掌握了這種封裝思路與方法了哈哈,其他組件其實也可以利用這種思路,封裝出來,在頁面上的使用直接傳配置項調用就完事,開發每個相似的頁面效率簡直是牛逼

    Table表格的封裝

    簡述

    再來折磨一遍,這里是table表單的封裝,其實跟上面的差不多,有點小區別,會用到添加動態插槽,這里就不墨跡了,直接上用配置項封裝前的正常使用

    正常使用

    user.vue

    <template>
      <div class="user">
        <el-table  :data="dataList" border>
          <!-- 1.傳入showSelectColumn時展示的全選列 -->
          <template v-if="contentTableConfig.showSelectColumn">
            <el-table-column type="selection" />
          </template>
          <!-- 2.傳入showIndexColumn時展示的序號列 -->
          <template v-if="contentTableConfig.showIndexColumn">
            <el-table-column type="index" label="序號" />
          </template>
          <!-- 3.propList里面的所有列 -->
          <template v-for="item in contentTableConfig.propList" :key="item.prop">
            <el-table-column v-bind="item" show-overflow-tooltip>
              <!-- 傳有slotName時展示的插槽列 -->
              <template #default="scope" v-if="item.slotName">
                <template v-if="item.slotName === 'handler'">
                  <el-button size="small" :icon="Edit" type="text">編輯</el-button>
                  <el-button size="small" :icon="Delete" type="text"
                    >刪除</el-button
                  >
                </template>
                <template v-if="item.slotName === 'status'">
                  <el-button>{{
                    scope.row.status === 0 ? '禁用' : '啟用'
                  }}</el-button>
                </template>
              </template>
            </el-table-column>
          </template>
        </el-table>
      </div>
    </template>
    
    <script setup lang="ts">
    import { Delete, Edit } from '@element-plus/icons-vue'
    import { useStore } from '@/store'
    import { computed } from 'vue'
    const store = useStore()
    // 這里是網絡請求數據
    const getPageData = () => {
      store.dispatch(`main/getPageListAction`, {
        queryInfo: {
          offset: 0,
          size: 10
        },
        pageName: 'users'
      })
    }
    // 頁面加載時第一次調用獲取表單數據
    getPageData()
    const dataList = computed(() => store.getters[`main/pageListData`]('users'))
    
    // 表格配置項
    const contentTableConfig = {
      // 表格配置
      propList: [
        { prop: 'id', label: '用戶id', minWidth: '100', align: 'center' },
        { prop: 'name', label: '用戶名', minWidth: '100', align: 'center' },
        { prop: 'realname', label: '真實姓名', minWidth: '100', align: 'center' },
        { prop: 'cellphone', label: '手機號碼', minWidth: '100', align: 'center' },
        {
          prop: 'enable',
          label: '狀態',
          minWidth: '100',
          slotName: 'status',
          align: 'center'
        },
        {
          label: '操作',
          minWidth: '120',
          slotName: 'handler',
          align: 'center'
        }
      ],
      // 表格具有序號列
      showIndexColumn: true,
      // 表格具有可選列
      showSelectColumn: true
    }
    </script>
    
    <style scoped lang="less"></style>

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    從上面可以看出,主頁面的代碼有多冗余,看到就頭疼+裂開,所以開始一層封裝

    開始封裝①

    配置項類型文件

    export interface ITbaleOption {
      prop?: string
      label: string
      minWidth?: string
      slotName?: string
    }
    export interface ITable {
      propList: ITbaleOption[]
      showIndexColumn?: boolean
      showSelectColumn?: boolean
      childrenProps?: object
    }

    配置項文件

    import { ITable } from '@/base-ui/table/type'
    export const contentTableConfig: ITable = {
      // 表格配置
      propList: [
        { prop: 'id', label: '用戶id', minWidth: '100' },
        { prop: 'name', label: '用戶名', minWidth: '100' },
        { prop: 'realname', label: '真實姓名', minWidth: '100' },
        { prop: 'cellphone', label: '手機號碼', minWidth: '100' },
        { prop: 'enable', label: '狀態', minWidth: '100', slotName: 'status' },
        {
          label: '操作',
          minWidth: '120',
          slotName: 'handler'
        }
      ],
      // 表格具有序號列
      showIndexColumn: false,
      // 表格具有可選列
      showSelectColumn: true
    }

    table.vue文件

    <template>
      <el-table  :data="listData" border>
        <!-- 1.傳入showSelectColumn時展示的全選列 -->
        <template v-if="showSelectColumn">
          <el-table-column type="selection" />
        </template>
        <!-- 2.傳入showIndexColumn時展示的序號列 -->
        <template v-if="showIndexColumn">
          <el-table-column type="index" label="序號" />
        </template>
        <!-- 3.propList里面的所有列 -->
        <template v-for="item in propList" :key="item.prop">
          <el-table-column v-bind="item" show-overflow-tooltip>
            <!-- 傳有slotName時展示的插槽列 -->
            <template #default="scope" v-if="item.slotName">
              <template v-if="item.slotName === 'handler'">
                <el-button size="small" :icon="Edit" type="text">編輯</el-button>
                <el-button size="small" :icon="Delete" type="text">刪除</el-button>
              </template>
              <template v-if="item.slotName === 'status'">
                <el-button>{{
                  scope.row.status === 0 ? '禁用' : '啟用'
                }}</el-button>
              </template>
            </template>
          </el-table-column>
        </template>
      </el-table>
    </template>
    
    <script setup lang="ts">
    import { Delete, Edit } from '@element-plus/icons-vue'
    import { defineProps, withDefaults, defineEmits } from 'vue'
    import { ITbaleOption } from './type'
    interface Props {
      listData: any[] //表單數據
      propList: ITbaleOption[] //表單配置項
      showIndexColumn?: boolean //是否顯示索引列
      showSelectColumn?: boolean //是否顯示全選列
      childrenProps?: object // 是否有子數據,樹形數據才用得到
    }
    const props = withDefaults(defineProps<Props>(), {
      showIndexColumn: false,
      showSelectColumn: false,
      childrenProps: () => ({})
    })
    </script>
    
    <style scoped></style>

    user.vue

    <template>
      <div class="user">
        <table-test v-bind="contentTableConfig" :listData="dataList"></table-test>
      </div>
    </template>
    
    <script setup lang="ts">
    // 導入表單配置項
    import { contentTableConfig } from './config/table-config'
    import tableTest from '@/base-ui/table/table-test.vue'
    import { useStore } from '@/store'
    import { computed } from 'vue'
    const store = useStore()
    
    // 這里是網絡請求數據
    const getPageData = () => {
      store.dispatch(`main/getPageListAction`, {
        queryInfo: {
          offset: 0,
          size: 10
        },
        pageName: 'users'
      })
    }
    // 頁面加載時第一次調用獲取表單數據
    getPageData()
    const dataList = computed(() => store.getters[`main/pageListData`]('users'))
    </script>
    
    <style scoped lang="less"></style>

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    可以看到,代碼抽出去了,但是我們可以發現,里面的插槽其實不能給它寫死,應該是動態決定的,所以我們可以這樣做

    在擁有slotName部分的插槽列部分放入一個具名插槽,再將默認插槽列中的scope.row發回給具名插槽

    table.vue(為了可擴展性,我依舊在里面加了兩個插槽,一個頂部一個底部)

    <template>
      <div class="header">
        <slot name="header"> </slot>
      </div>
      <el-table  :data="listData" border>
        <!-- 1.傳入showSelectColumn時展示的全選列 -->
        <template v-if="showSelectColumn">
          <el-table-column type="selection" />
        </template>
        <!-- 2.傳入showIndexColumn時展示的序號列 -->
        <template v-if="showIndexColumn">
          <el-table-column type="index" label="序號" />
        </template>
        <!-- 3.propList里面的所有列 -->
        <template v-for="item in propList" :key="item.prop">
          <el-table-column v-bind="item" show-overflow-tooltip>
            <!-- 傳有slotName時展示的插槽列 -->
            <template #default="scope" v-if="item.slotName">
              <slot :name="item.slotName" :row="scope.row"></slot>
            </template>
          </el-table-column>
        </template>
      </el-table>
      <div class="footer">
        <slot name="footer"> </slot>
      </div>
    </template>
    
    <script setup lang="ts">
    import { defineProps, withDefaults, defineEmits } from 'vue'
    import { ITbaleOption } from './type'
    interface Props {
      listData: any[] //表單數據
      propList: ITbaleOption[] //表單配置項
      showIndexColumn?: boolean //是否顯示索引列
      showSelectColumn?: boolean //是否顯示全選列
      childrenProps?: object // 是否有子數據,樹形數據才用得到
    }
    const props = withDefaults(defineProps<Props>(), {
      showIndexColumn: false,
      showSelectColumn: false,
      childrenProps: () => ({})
    })
    </script>
    
    <style scoped></style>

    然后在user.vue中放入具名插槽,傳進去table組件里,同時傳入一些內容到可擴展插槽里面

    user.vue

    <template>
      <div class="user">
        <div class="content">
          <table-test v-bind="contentTableConfig" :listData="dataList">
            <!-- 1.header中的插槽 -->
            <template #header>
              <div class="header-default">
                <!-- 傳入標題(位于左側) -->
                <div class="title">用戶列表</div>
                <!-- 傳入處理內容(位于右側) -->
                <div class="handler">
                  <el-button type="primary" @click="addUserBtnClick"
                    >新建用戶</el-button
                  >
                </div>
              </div>
            </template>
            <!-- 2.該user頁面獨有部分 -->
            <template #status="scope">
              <el-button>{{ scope.row.status === 0 ? '禁用' : '啟用' }}</el-button>
            </template>
            <!-- 3.每個頁面都會有的部分 -->
            <template #handler="scope">
              <el-button
                size="small"
                :icon="Edit"
                type="text"
                @click="handleEditClick(scope.row)"
                >編輯</el-button
              >
              <el-button
                size="small"
                :icon="Delete"
                type="text"
                @click="deleteBtnClick(scope.row)"
                >刪除</el-button
              >
            </template>
            <!-- 4.footer插槽 -->
            <template #footer>
              <!-- 只有總條數>0才會有分頁器 -->
              <div class="footer-default">
                <el-pagination
                  :page-sizes="[100, 200, 300, 400]"
                  layout="total, sizes, prev, pager, next, jumper"
                  :total="400"
                />
              </div>
            </template>
          </table-test>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    // 導入表單配置項
    import { contentTableConfig } from './config/table-config'
    import { Delete, Edit } from '@element-plus/icons-vue'
    import tableTest from '@/base-ui/table/table-test.vue'
    import { useStore } from '@/store'
    import { computed } from 'vue'
    const store = useStore()
    
    // 這里是網絡請求數據
    const getPageData = () => {
      store.dispatch(`main/getPageListAction`, {
        queryInfo: {
          offset: 0,
          size: 10
        },
        pageName: 'users'
      })
    }
    // 頁面加載時第一次調用獲取表單數據
    getPageData()
    const dataList = computed(() => store.getters[`main/pageListData`]('users'))
    
    // 點擊編輯按鈕觸發事件
    const handleEditClick = (row: any) => {
      console.log('點擊了編輯按鈕,數據為:', row)
    }
    // 點擊刪除按鈕觸發事件
    const deleteBtnClick = (row: any) => {
      console.log('點擊了刪除按鈕,數據為:', row)
    }
    // 點擊新建用戶觸發事件
    const addUserBtnClick = () => {
      console.log('點擊了新建用戶')
    }
    </script>
    
    <style scoped lang="less">
    .content {
      border-top: 20px solid #dedee1;
      padding: 40px;
    }
    .header-default {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
      .title {
        font-size: 22px;
        font-weight: 700;
      }
    }
    .footer-default {
      display: flex;
      justify-content: flex-end;
      margin-top: 20px;
    }
    </style>

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    顯然此時封裝已經滿足需求了,但是我們發現主頁面的代碼頁還是比較冗余,如果不用到插槽的話還好,要用到插槽的話,就要在主頁面寫入很多插槽,多個頁面都這樣的話頁裂開,所以我們要像之前一樣把這坨代碼再封一層

    開始封裝②

    page-table.vue

    <template>
      <table-test v-bind="contentTableConfig" :listData="dataList">
        <!-- 1.header中的插槽 -->
        <template #header>
          <div class="header-default">
            <!-- 傳入標題(位于左側) -->
            <div class="title">{{ pageNameInChinese }}列表</div>
            <!-- 傳入處理內容(位于右側) -->
            <div class="handler">
              <el-button type="primary" @click="addUserBtnClick"
                >新建{{ pageNameInChinese }}</el-button
              >
            </div>
          </div>
        </template>
        <!-- 2.該user頁面獨有部分 -->
        <template #status="scope">
          <el-button>{{ scope.row.status === 0 ? '禁用' : '啟用' }}</el-button>
        </template>
        <!-- 3.每個頁面都會有的部分 -->
        <template #handler="scope">
          <el-button
            size="small"
            :icon="Edit"
            type="text"
            @click="handleEditClick(scope.row)"
            >編輯</el-button
          >
          <el-button
            size="small"
            :icon="Delete"
            type="text"
            @click="deleteBtnClick(scope.row)"
            >刪除</el-button
          >
        </template>
        <!-- 4.footer插槽 -->
        <template #footer>
          <!-- 只有總條數>0才會有分頁器 -->
          <div class="footer-default">
            <el-pagination
              :page-sizes="[100, 200, 300, 400]"
              layout="total, sizes, prev, pager, next, jumper"
              :total="400"
            />
          </div>
        </template>
      </table-test>
    </template>
    
    <script setup lang="ts">
    import { Delete, Edit } from '@element-plus/icons-vue'
    import tableTest from '@/base-ui/table/table-test.vue'
    import type { ITable } from '@/base-ui/table/type'
    
    import { useStore } from '@/store'
    import { defineProps, computed } from 'vue'
    
    // 定義屬性
    interface Props {
      contentTableConfig: ITable //表單配置接收
      pageName: string //表單名字接收
    }
    const props = defineProps<Props>()
    
    const store = useStore()
    
    // 這里是網絡請求數據
    const getPageData = () => {
      store.dispatch(`main/getPageListAction`, {
        queryInfo: {
          offset: 0,
          size: 10
        },
        pageName: props.pageName
      })
    }
    // 頁面加載時第一次調用獲取表單數據
    getPageData()
    const dataList = computed(() =>
      store.getters[`main/pageListData`](props.pageName)
    )
    
    // 1.獲取頁面中文名稱
    const pageNameTransform = (pageName: string): string => {
      switch (pageName) {
        case 'users':
          return '用戶'
        default:
          return ''
      }
    }
    const pageNameInChinese = pageNameTransform(props.pageName)
    
    // 點擊編輯按鈕觸發事件
    const handleEditClick = (row: any) => {
      console.log('點擊了編輯按鈕,數據為:', row)
    }
    // 點擊刪除按鈕觸發事件
    const deleteBtnClick = (row: any) => {
      console.log('點擊了刪除按鈕,數據為:', row)
    }
    // 點擊新建用戶觸發事件
    const addUserBtnClick = () => {
      console.log('點擊了新建用戶')
    }
    </script>
    
    <style scoped lang="less">
    .header-default {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
      .title {
        font-size: 22px;
        font-weight: 700;
      }
    }
    .footer-default {
      display: flex;
      justify-content: flex-end;
      margin-top: 20px;
    }
    </style>

    user.vue

    <template>
      <div class="user">
        <div class="content">
          <page-table
            :contentTableConfig="contentTableConfig"
            pageName="users"
          ></page-table>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    // 導入表單配置項
    import { contentTableConfig } from './config/table-config'
    import pageTable from '@/components/page-table/page-table-test.vue'
    </script>
    
    <style scoped lang="less">
    .content {
      border-top: 20px solid #dedee1;
      padding: 40px;
    }
    </style>

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    圖中可以看出效果其實是一樣的,主頁面的代碼少了,只需要傳入配置項和pageName(控制網絡請求數據)就可以生成一個表格,但是我們可以發現,如果多個頁面復用的話,其實操作列的插槽是公有的,但是狀態列卻是私有的,別的頁面不一定有狀態頁,所以狀態列內容插入的位置應該在主頁面user里面而不該在封裝的組件里面,而且編輯新建邏輯也是頁面獨有,應該在主頁面執行

    開始封裝③

    user.vue

    <template>
      <div class="user">
        <div class="content">
          <page-table
            :contentTableConfig="contentTableConfig"
            pageName="users"
            @editBtnClick="handleEditClick"
            @createBtnClick="handleCreateClick"
          >
            <template #status="scope">
              <el-button>{{ scope.row.status === 0 ? '禁用' : '啟用' }}</el-button>
            </template>
          </page-table>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    // 導入表單配置項
    import { contentTableConfig } from './config/table-config'
    import pageTable from '@/components/page-table/page-table-test.vue'
    
    const handleEditClick = (row: any, pageNameInChinese: any) => {
      console.log('點擊了編輯按鈕,數據為:', row, pageNameInChinese)
    }
    
    const handleCreateClick = (pageNameInChinese: any) => {
      console.log('點擊了新建用戶,數據為:', pageNameInChinese)
    }
    </script>
    
    <style scoped lang="less">
    .content {
      border-top: 20px solid #dedee1;
      padding: 40px;
    }
    </style>

    page-table.vue

    <template>
      <table-test v-bind="contentTableConfig" :listData="dataList">
        <!-- 1.header中的插槽 -->
        <template #header>
          <div class="header-default">
            <!-- 傳入標題(位于左側) -->
            <div class="title">{{ pageNameInChinese }}列表</div>
            <!-- 傳入處理內容(位于右側) -->
            <div class="handler">
              <el-button type="primary" @click="addUserBtnClick"
                >新建{{ pageNameInChinese }}</el-button
              >
            </div>
          </div>
        </template>
        <!-- 2.該user頁面獨有部分 -->
        <template
          v-for="item in otherPropSlots"
          :key="item.prop"
          #[item.slotName]="scope"
        >
          <template v-if="item.slotName">
            <slot :name="item.slotName" :row="scope.row"></slot>
          </template>
        </template>
        <!-- 3.每個頁面都會有的部分 -->
        <template #handler="scope">
          <el-button
            size="small"
            :icon="Edit"
            type="text"
            @click="handleEditClick(scope.row)"
            >編輯</el-button
          >
          <el-button
            size="small"
            :icon="Delete"
            type="text"
            @click="deleteBtnClick(scope.row)"
            >刪除</el-button
          >
        </template>
        <!-- 4.footer插槽 -->
        <template #footer>
          <!-- 只有總條數>0才會有分頁器 -->
          <div class="footer-default">
            <el-pagination
              :page-sizes="[100, 200, 300, 400]"
              layout="total, sizes, prev, pager, next, jumper"
              :total="400"
            />
          </div>
        </template>
      </table-test>
    </template>
    
    <script setup lang="ts">
    import { Delete, Edit } from '@element-plus/icons-vue'
    import tableTest from '@/base-ui/table/table-test.vue'
    import type { ITable } from '@/base-ui/table/type'
    
    import { useStore } from '@/store'
    import { defineProps, computed, defineEmits } from 'vue'
    
    // 定義屬性
    interface Props {
      contentTableConfig: ITable //表單配置接收
      pageName: string //表單名字接收
    }
    const props = defineProps<Props>()
    const emit = defineEmits<{
      (e: 'createBtnClick', pageNameInChinese: string): void
      (e: 'editBtnClick', rowData: any, pageNameInChinese: string): void
    }>()
    
    const store = useStore()
    
    // 這里是網絡請求數據
    const getPageData = () => {
      store.dispatch(`main/getPageListAction`, {
        queryInfo: {
          offset: 0,
          size: 10
        },
        pageName: props.pageName
      })
    }
    // 頁面加載時第一次調用獲取表單數據
    getPageData()
    const dataList = computed(() =>
      store.getters[`main/pageListData`](props.pageName)
    )
    
    // 1.獲取頁面中文名稱
    const pageNameTransform = (pageName: string): string => {
      switch (pageName) {
        case 'users':
          return '用戶'
        default:
          return ''
      }
    }
    const pageNameInChinese = pageNameTransform(props.pageName)
    
    // 2.屬于動態插槽的配置項篩選
    const otherPropSlots: any = props.contentTableConfig?.propList.filter(
      (item: any) => {
        if (item.slotName === 'handler') return false
        return item.slotName
      }
    )
    
    // 點擊編輯按鈕觸發事件
    const handleEditClick = (row: any) => {
      emit('editBtnClick', row, pageNameInChinese)
    }
    // 點擊刪除按鈕觸發事件
    const deleteBtnClick = (row: any) => {
      console.log('點擊了刪除按鈕,數據為:', row)
    }
    // 點擊新建用戶觸發事件
    const addUserBtnClick = () => {
      emit('createBtnClick', pageNameInChinese)
    }
    </script>
    
    <style scoped lang="less">
    .header-default {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
      .title {
        font-size: 22px;
        font-weight: 700;
      }
    }
    .footer-default {
      display: flex;
      justify-content: flex-end;
      margin-top: 20px;
    }
    </style>

    Element?Plus組件Form表單Table表格二次封裝怎么實現

    可以看到,這時我們已經把獨有私有的插槽區分開了,而且編輯和新建的邏輯也在主頁面中執行,封裝完畢,下面附上完整代碼

    完整封裝代碼④

    配置項類型文件
    export interface ITbaleOption {
      prop?: string
      label: string
      minWidth?: string
      slotName?: string
      align?: string
    }
    export interface ITable {
      propList: ITbaleOption[]
      showIndexColumn?: boolean
      showSelectColumn?: boolean
      childrenProps?: object
    }
    配置項文件
    import { ITable } from '@/base-ui/table/type'
    export const contentTableConfig: ITable = {
      // 表格配置
      propList: [
        { prop: 'id', label: '用戶id', minWidth: '100', align: 'center' },
        { prop: 'name', label: '用戶名', minWidth: '100', align: 'center' },
        { prop: 'realname', label: '真實姓名', minWidth: '100', align: 'center' },
        { prop: 'cellphone', label: '手機號碼', minWidth: '100', align: 'center' },
        {
          prop: 'enable',
          label: '狀態',
          minWidth: '100',
          slotName: 'status',
          align: 'center'
        },
        {
          label: '操作',
          minWidth: '120',
          slotName: 'handler',
          align: 'center'
        }
      ],
      // 表格具有序號列
      showIndexColumn: false,
      // 表格具有可選列
      showSelectColumn: true
    }
    table表單組件文件
    <template>
      <div class="header">
        <slot name="header"> </slot>
      </div>
      <el-table  :data="listData" border>
        <!-- 1.傳入showSelectColumn時展示的全選列 -->
        <template v-if="showSelectColumn">
          <el-table-column type="selection" />
        </template>
        <!-- 2.傳入showIndexColumn時展示的序號列 -->
        <template v-if="showIndexColumn">
          <el-table-column type="index" label="序號" />
        </template>
        <!-- 3.propList里面的所有列 -->
        <template v-for="item in propList" :key="item.prop">
          <el-table-column v-bind="item" show-overflow-tooltip>
            <!-- 傳有slotName時展示的插槽列 -->
            <template #default="scope" v-if="item.slotName">
              <slot :name="item.slotName" :row="scope.row"></slot>
            </template>
          </el-table-column>
        </template>
      </el-table>
      <div class="footer">
        <slot name="footer"> </slot>
      </div>
    </template>
    
    <script setup lang="ts">
    import { defineProps, withDefaults, defineEmits } from 'vue'
    import { ITbaleOption } from './type'
    interface Props {
      listData: any[] //表單數據
      propList: ITbaleOption[] //表單配置項
      showIndexColumn?: boolean //是否顯示索引列
      showSelectColumn?: boolean //是否顯示全選列
      childrenProps?: object // 是否有子數據,樹形數據才用得到
    }
    const props = withDefaults(defineProps<Props>(), {
      showIndexColumn: false,
      showSelectColumn: false,
      childrenProps: () => ({})
    })
    </script>
    
    <style scoped></style>
    page-table組件文件
    user頁面組件文件
    <template>
      <div class="user">
        <div class="content">
          <page-table
            :contentTableConfig="contentTableConfig"
            pageName="users"
            @editBtnClick="handleEditClick"
            @createBtnClick="handleCreateClick"
          >
            <template #status="scope">
              <el-button>{{ scope.row.status === 0 ? '禁用' : '啟用' }}</el-button>
            </template>
          </page-table>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    // 導入表單配置項
    import { contentTableConfig } from './config/table-config'
    import pageTable from '@/components/page-table/page-table-test.vue'
    
    const handleEditClick = (row: any, pageNameInChinese: any) => {
      console.log('點擊了編輯按鈕,數據為:', row, pageNameInChinese)
    }
    
    const handleCreateClick = (pageNameInChinese: any) => {
      console.log('點擊了新建用戶,數據為:', pageNameInChinese)
    }
    </script>
    
    <style scoped lang="less">
    .content {
      border-top: 20px solid #dedee1;
      padding: 40px;
    }
    </style>

    關于“Element Plus組件Form表單Table表格二次封裝怎么實現”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Element Plus組件Form表單Table表格二次封裝怎么實現”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    湾仔区| 山东省| 汝州市| 四会市| 信丰县| 巴里| 健康| 益阳市| 伊宁县| 苍南县| 新和县| 普安县| 三都| 大田县| 云梦县| 清涧县| 布尔津县| 苏尼特右旗| 上虞市| 崇仁县| 阳西县| 资源县| 玉山县| 湖口县| 康保县| 鲜城| 陕西省| 罗江县| 封开县| 扬州市| 保定市| 青河县| 定南县| 察哈| 怀化市| 阿鲁科尔沁旗| 连云港市| 边坝县| 顺平县| 汝城县| 南江县|