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: { }
}
},
}
参数
- columns 表格列参数
- formItem 页面搜索条件参数
- formConfig 页面搜索内容配置参数
- isPagination 是否前端分页
- bmSuffix 插槽 页面操作按钮位置,如新增、批量下载、批量删除等操作
事件
- postData 请求完成后对返回的数据处理事件
- 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">
<!-- 此处优化时,应该用render函数进行渲染,支持用户自定义组件,目前支持的能力还是有权限,对于用户想要的方式还是有限制,适用场景不是很高-->
<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">
<!--此处功能可以放入bmtable处理, 组件应该是一个干净的代码,不应该放在组件封装中-->
<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>