vue3.0 通用管理页面封装

bmTable使用方法

<BmTable url="/project/list"
      :columns="columns"
      :formItem="formItem"
      :formConfig="formConfig"
      :isPagination="true"
      @postData="postData"
      @preData="preData"
      ref="bmTable">
    <template #bmSuffix>
      <div>
        <el-button type="success">新增</el-button>
      </div>
    </template>
  </BmTable>
export default {
   
  mixins: [IndexMixin],
  setup() {
   
    const bmTable = ref()
    const postData = (data)=> {
    return data; }
    const preData = (data)=> {
    return data; }
    return {
   
      postData,
      preData,
      bmTable
    }
  }
}

export default {
   
  data() {
   
    return {
   
      columns: [
        {
   
          prop: 'creatTime',
          label: '创建时间',
        },
        {
   
          prop: 'datas',
          label: '操作',
          labelWidth: 180,
          render: (item)=> {
   
            return <>
              <el-button link type="success" onClick={
   ()=> {
    this.deletes(item); }}>删除</el-button>
            </>
            
          }
        }
      ],
      formItem: [
        {
   
          prop: 'created',
          label: '创建时间',
          el: 'el-input',
          placeholder: '123',
          defaultValue: 'test'
        },
        {
   
          prop: 'crea',
          label: '创建',
          el: 'el-select',
          data: [{
   label: '123', value: '1'}, {
   label: '3', value: '2'}],
          elOpt: 'el-option',
          placeholder: '123',
          defaultValue: '1'
        },
      ],
      formConfig: {
    }
    }
  },
}

参数

  1. columns 表格列参数
  2. formItem 页面搜索条件参数
  3. formConfig 页面搜索内容配置参数
  4. isPagination 是否前端分页
  5. bmSuffix 插槽 页面操作按钮位置,如新增、批量下载、批量删除等操作

事件

  1. postData 请求完成后对返回的数据处理事件
  2. preData 请求前对请求参数处理事件

实现方案
原理:当我们存在多个组件中的数据或者功能很相近时,我们就可以利用 mixins 将公共部分提取出来,通过 mixins 封装函数,组件调用他们是不会改变函数作用域外部的。

注意事项

  • 接口返回数据
    接口返回参数若后端分页时数据放入data.data.result, 前端分页时数据放入data.data
if (!isPagination) {
   
      tableData.list = onPostData ? onPostData(data.data) : data.data;
      tableData.data = tableData.list.slice(
        (tableData.pagination.page - 1) * tableData.pagination.pageSize,
        tableData.pagination.page * tableData.pagination.pageSize
      );
      tableData.pagination.total = tableData.list.length;
      return;
    }
    tableData.data = onPostData
      ? onPostData(data.data.result)
      : data.data.result;
    tableData.pagination.total = data.data.total;
  });

组件封装源码

index.vue

<template>
  <div class="bm-content">
    <BmForm
      :formConfig="formConfig"
      :initData="searchData"
      :formItem="formItem"
      :inline="true"
      @search="search"
      v-if="formItem?.length"
    ></BmForm>
    <slot name="bmSuffix"></slot>
    <el-table
      :data="tableData.data"
      style="width: 100%"
      stripe
      v-bind="$attrs"
      header-row-class-name="bm-table-header"
      row-class-name="bm-table-body"
    >
      <el-table-column
        v-for="(item, index) in columns"
        :key="item.label"
        :width="item.labelWidth"
        v-bind="item"
      >
        <template #default="scope">
          <span v-if="!item.render">
            {
  { scope.row[item.prop] }}
          </span>
          <render v-else :render="item.render" :data="scope.row" />
        </template>
      </el-table-column>
    </el-table>
    <div class="bm-pagination">
      <el-pagination
        v-model:current-page="tableData.pagination.page"
        v-model:page-size="tableData.pagination.pageSize"
        @current-change="currentChange"
        :page-sizes="[10, 20, 50]"
        small
        layout="total, sizes, prev, pager, next, jumper"
        :total="tableData.pagination.total"
        background
      />
    </div>
  </div>
</template>

<script lang="ts" setup>
import render from "./render.jsx";
interface propsType {
     
  method?: string; // 方法
  url?: string; // 地址
  columns?: {
     
    label?: string;
    props?: string;
    labelWidth?: number;
    render?: any;
  };
  isPagination?: boolean; // 是否前端分页
  onPostData?: Function; // 后置数据处理
  formConfig?: Object; // 表单配置
  formItem?: any[]; // 表单item配置
  onPreData?: Function; // 参数前置处理
}
import {
      getCurrentInstance, onMounted, reactive, useAttrs, ref } from "vue";

const Instance = getCurrentInstance();
const http = Instance?.appContext.config.globalProperties.$http;
const attrs = useAttrs();

const {
     
  method = "get",
  url,
  columns,
  isPagination = true,
  onPostData,
  formConfig,
  formItem,
  onPreData,
}: propsType = attrs;
const searchData = reactive({
     });

const tableData: any = reactive({
     
  data: [],
  list: [],
  pagination: {
     
    total: 0,
    pageSize: 10,
    page: 1,
  },
});

const getList = () => {
     
  // 请求参数 query
  const query: any = {
      ...searchData };
  if (isPagination) {
     
    query.page = tableData.pagination.page;
    query.pageSize = tableData.pagination.pageSize;
  }
  // 前置调用
  onPreData && onPreData(query);
  http({
     
    method: method,
    url: url,
    data: query,
  }).then((data: any) => {
     
    // 后置调用
    // result.data
    if (!isPagination) {
     
      tableData.list = onPostData ? onPostData(data.data) : data.data;
      tableData.data = tableData.list.slice(
        (tableData.pagination.page - 1) * tableData.pagination.pageSize,
        tableData.pagination.page * tableData.pagination.pageSize
      );
      tableData.pagination.total = tableData.list.length;
      return;
    }
    tableData.data = onPostData
      ? onPostData(data.data.result)
      : data.data.result;
    tableData.pagination.total = data.data.total;
  });
};

const currentChange = (e: any) => {
     
  if (!isPagination) {
     
    tableData.data = tableData.list.slice(
      (e - 1) * tableData.pagination.pageSize,
      e * tableData.pagination.pageSize
    );
    return;
  }
  getList();
};

const search = (data, formData) => {
     
  data.validate((valid) => {
     
    if (valid) {
     
      Object.keys(searchData).map((item) => {
     
        searchData[item] = formData[item];
      });
      getList();
    } else {
     
      return false;
    }
  });
};

onMounted(() => {
     
  formItem?.map((item) => {
     
    searchData[item.prop] = item.defaultValue ? item.defaultValue : "";
  });
  getList();
});

defineExpose({
     
  getList,
});
</script>
<style lang="less" scoped>
:deep(.bm-table-header) {
     
  height: 60px;
}
:deep(.bm-table-body) {
     
  height: 50px;
  .cell {
     
    font-size: 13px;
  }
}
.bm-pagination {
     
  display: flex;
  justify-content: flex-end;
  height: 50px;
  background-color: #ffff;
}
.bm-content {
     
  border-radius: 3px;
  overflow: hidden;
  // height: calc(100vh - 100px);
  // background-color: white;
}
</style>

render.jsx

export default {
   
  render(context) {
   
    return (
      <>
        {
   context.$attrs.render(context.$attrs.data)}
      </>
    );
  }
}

bmform.vue


<template>
  <el-form v-bind="$attrs" :model="formData" ref="formRef">
    <el-form-item v-for="(item, ind) in formItem" :key="ind" v-bind="item">
      <component :is="item.el" v-bind="item" v-model="formData[item.prop]" v-if="!item.types">
        <component :is="item.elOpt"
          v-for="(option, index) in item.data"
          :key="index"
          v-bind="option"
          v-if="item.data"></component>
      </component>
      <el-button
        type="primary"
        v-else
        v-for="(opt, key) in item.options"
        :key="key"
        v-bind="opt"
        @click="opt.click(formRef)"
        >
          {
  { opt.value }}
      </el-button>
    </el-form-item>
    <el-form-item v-if="$attrs.inline">
      <el-button type="primary" :icon="Search" @click="submitForm(formRef)" style="width: 0px;">搜索</el-button>
    </el-form-item>
  </el-form>
</template>
<script setup lang="ts">
import {
      useAttrs, reactive, ref } from "vue";
import {
      Search } from '@element-plus/icons-vue';

const emit = defineEmits(['search'])
const attr = useAttrs();
const formRef = ref()

const {
      formConfig, formItem, initData }: any = attr;
const formData = reactive(initData);

// 数据向外暴露
const submitForm = (data)=> {
     
  emit('search', data, formData)
}

</script>

相关推荐

  1. vue3.0 通用管理页面封装

    2023-12-23 00:30:03       52 阅读
  2. vue管理系统封装echarts

    2023-12-23 00:30:03       39 阅读
  3. vue3 封装一个通用echarts组件

    2023-12-23 00:30:03       63 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-23 00:30:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-23 00:30:03       106 阅读
  3. 在Django里面运行非项目文件

    2023-12-23 00:30:03       87 阅读
  4. Python语言-面向对象

    2023-12-23 00:30:03       96 阅读

热门阅读

  1. 若依前端引入外部icon

    2023-12-23 00:30:03       69 阅读
  2. 标签正则化和硬标签、软标签、单标签、多标签

    2023-12-23 00:30:03       62 阅读
  3. go语言读取Excel表格中的数据

    2023-12-23 00:30:03       56 阅读
  4. 组件式Prompt设计tips

    2023-12-23 00:30:03       58 阅读
  5. 计算相对差异的Boost.Math库的测试程序

    2023-12-23 00:30:03       53 阅读
  6. C++学习笔记(十七)

    2023-12-23 00:30:03       45 阅读
  7. Copula-Variational-Bayes 元高斯分析法的 MATLAB 仿真

    2023-12-23 00:30:03       54 阅读
  8. 深入理解 Union 和 Union All 的区别及优化技巧

    2023-12-23 00:30:03       63 阅读
  9. Unity-时间

    2023-12-23 00:30:03       61 阅读