123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- <!-- 📚📚📚 Pro-Table 文档: https://juejin.cn/post/7166068828202336263 -->
- <template>
- <!-- 表格主体 -->
- <div class="card table-main">
- <!-- 表格头部 操作按钮 -->
- <div class="table-header">
- <div class="header-button-lf">
- <slot name="tableHeader" :selected-list="selectedList" :selected-list-ids="selectedListIds" :is-selected="isSelected" />
- </div>
- <div v-if="toolButton" class="header-button-ri">
- <slot name="toolButton">
- <el-button v-if="showToolButton('refresh')" circle @click="onRefresh" title="刷新表格">
- <SvgIcon name="ele-Refresh" />
- </el-button>
- <el-button v-if="showToolButton('setting') && columns.length" circle @click="openColSetting" title="列设置">
- <SvgIcon name="ele-Setting" />
- </el-button>
- <el-button v-if="showToolButton('exportCurrent') && columns.length" circle @click="exportCurrent" title="导出当前页">
- <SvgIcon name="iconfont icon-daochu" />
- </el-button>
- <el-button v-if="showToolButton('exportAll') && columns.length" circle @click="exportAll" title="导出全部">
- <SvgIcon name="iconfont icon-export" />
- </el-button>
- </slot>
- </div>
- </div>
- <!-- 表格主体 -->
- <!-- :scrollbar-always-on="true" 滚动条一直展示-->
- <el-table
- ref="tableRef"
- v-bind="$attrs"
- :data="processTableData"
- :border="border"
- :row-key="rowKey"
- @selection-change="selectionChange"
- v-loading="loading"
- :scrollbar-always-on="true"
- >
- <!-- 默认插槽 -->
- <slot />
- <template v-for="item in tableColumns" :key="item">
- <!-- selection || radio || index || expand || sort -->
- <el-table-column
- v-if="item.type && columnTypes.includes(item.type)"
- v-bind="item"
- :align="item.align ?? 'left'"
- :reserve-selection="item.type == 'selection'"
- >
- <template #default="scope">
- <!-- expand -->
- <template v-if="item.type == 'expand'">
- <component :is="item.render" v-bind="scope" v-if="item.render" />
- <slot v-else :name="item.type" v-bind="scope" />
- </template>
- <!-- radio -->
- <el-radio v-if="item.type == 'radio'" v-model="radio" :label="scope.row[rowKey]">
- <i></i>
- </el-radio>
- </template>
- </el-table-column>
- <!-- other -->
- <TableColumn v-if="!item.type && item.prop && item.isShow" :column="item" col-setting="">
- <template v-for="slot in Object.keys($slots)" #[slot]="scope">
- <slot :name="slot" v-bind="scope" />
- </template>
- </TableColumn>
- </template>
- <!-- 插入表格最后一行之后的插槽 -->
- <template #append>
- <slot name="append" />
- </template>
- <!-- 无数据 -->
- <template #empty>
- <Empty />
- </template>
- </el-table>
- <!-- 分页组件 -->
- <slot name="pagination">
- <PaginationEl v-if="pagination" @pagination="onRefresh" :total="total" v-model:current-page="pageIndex" v-model:page-size="pageSize" />
- </slot>
- </div>
- <!-- 列设置 -->
- <ColSetting v-if="toolButton" ref="colRef" v-model:col-setting="colSetting" @update:colSetting="updateColSetting" />
- </template>
- <script setup lang="ts" name="ProTable">
- import { ref, provide, onMounted, unref, computed, reactive, PropType, watch, shallowRef } from "vue";
- import { ElTable } from 'element-plus';
- import { useSelection } from '@/hooks/useSelection';
- import { ColumnProps, TypeProps } from '@/components/ProTable/interface';
- import PaginationEl from './components/Pagination.vue';
- import ColSetting from './components/ColSetting.vue';
- import TableColumn from './components/TableColumn.vue';
- import { handleProp } from '@/utils/tools';
- // 接受父组件参数,配置默认值
- const props = defineProps({
- columns: {
- // 列配置项 ==> 必传
- type: Array as PropType<ColumnProps[]>,
- required: true,
- },
- data: {
- // 静态 table data 数据
- type: Array,
- default: () => [],
- required: true,
- },
- pagination: {
- // 是否需要分页组件 ==> 非必传(默认为true)
- type: Boolean,
- default: true,
- },
- total: {
- type: Number,
- default: 0,
- },
- pageIndex: {
- type: Number,
- default: 1,
- },
- pageSize: {
- type: Number,
- default: 10,
- },
- border: {
- // 是否带有纵向边框 ==> 非必传(默认为false)
- type: Boolean,
- default: false,
- },
- toolButton: {
- // 是否显示表格功能按钮 ==> 非必传(默认为true)
- type: [Array, Boolean],
- // default: true,
- default: ['refresh', 'setting'],
- },
- rowKey: {
- // 行数据的 Key,用来优化 Table 的渲染,当表格数据多选时,所指定的 id ==> 非必传(默认为 id)
- type: String,
- default: 'id',
- },
- loading: {
- // 是否显示加载中
- type: Boolean,
- default: false,
- },
- radio: {
- type: String,
- default: '',
- },
- });
- const emit = defineEmits([
- 'dargSort',
- 'updateColSetting',
- 'selectChange',
- 'updateTable',
- 'update:pagination',
- 'exportCurrent',
- 'exportAll',
- 'update:pageSize',
- 'update:pageIndex',
- 'update:radio',
- ]);
- const pageSize = computed({
- get() {
- return props.pageSize;
- },
- set(val) {
- emit('update:pageSize', val);
- },
- });
- const pageIndex = computed({
- get() {
- return props.pageIndex;
- },
- set(val) {
- emit('update:pageIndex', val);
- },
- });
- const radio = computed({
- get() {
- return props.radio;
- },
- set(val) {
- emit('update:radio', val);
- },
- });
- // table 实例
- const tableRef = ref<InstanceType<typeof ElTable>>();
- // column 列类型
- const columnTypes: TypeProps[] = ['selection', 'radio', 'index', 'expand', 'sort'];
- // 控制 ToolButton 显示
- const showToolButton = (key: 'refresh' | 'setting' | 'exportCurrent' | 'exportAll') => {
- return Array.isArray(props.toolButton) ? props.toolButton.includes(key) : props.toolButton;
- };
- // 表格多选 Hooks
- const { selectionChange, selectedList, selectedListIds, isSelected } = useSelection(props.rowKey);
- // 清空选中数据列表
- const clearSelection = () => tableRef.value!.clearSelection();
- // 初始化表格数据 && 拖拽排序
- onMounted(() => {
- getSelectColumns();
- });
- // 处理表格数据
- const processTableData = computed(() => {
- if (!props.pagination) return props.data;
- return props.data;
- });
- // 接收 columns 并设置为响应式
- const tableColumns = reactive<ColumnProps[]>(props.columns);
- // 扁平化 columns
- const flatColumns = computed(() => flatColumnsFunc(tableColumns));
- // 定义 enumMap 存储 enum 值(避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择)
- const enumMap = ref(new Map<string, { [key: string]: any }[]>());
- const setEnumMap = async ({ prop, enum: enumValue }: ColumnProps) => {
- if (!enumValue) return;
- // 如果当前 enumMap 存在相同的值 return
- if (enumMap.value.has(prop!) && (typeof enumValue === 'function' || enumMap.value.get(prop!) === enumValue)) return;
- // 当前 enum 为静态数据,则直接存储到 enumMap
- if (typeof enumValue !== 'function') return enumMap.value.set(prop!, unref(enumValue!));
- // 为了防止接口执行慢,而存储慢,导致重复请求,所以预先存储为[],接口返回后再二次存储
- enumMap.value.set(prop!, []);
- // 当前 enum 为后台数据需要请求数据,则调用该请求接口,并存储到 enumMap
- const { data } = await enumValue();
- enumMap.value.set(prop!, data);
- };
- // 注入 enumMap
- provide('enumMap', enumMap);
- // 扁平化 columns 的方法
- const flatColumnsFunc = (columns: ColumnProps[], flatArr: ColumnProps[] = []) => {
- columns.forEach(async (col) => {
- if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children));
- flatArr.push(col);
- // column 添加默认 isShow
- col.isShow = col.isShow ?? true;
- // 设置 enumMap
- await setEnumMap(col);
- });
- return flatArr.filter((item) => !item._children?.length);
- };
- // 过滤需要搜索的配置项 && 排序
- const searchColumns = computed(() => {
- return flatColumns.value?.filter((item) => item.search?.el || item.search?.render).sort((a, b) => a.search!.order! - b.search!.order!);
- });
- // 设置 搜索表单默认排序 && 搜索表单项的默认值
- searchColumns.value?.forEach((column, index) => {
- column.search!.order = column.search?.order ?? index + 2;
- const key = column.search?.key ?? handleProp(column.prop!);
- const defaultValue = column.search?.defaultValue;
- });
- // 列设置 ==> 需要过滤掉不需要设置的列
- const colRef = ref();
- const colSetting = tableColumns!.filter((item) => {
- const { type, prop, isShow } = item;
- return !columnTypes.includes(type!) && prop !== 'operation' && isShow;
- });
- const openColSetting = () => colRef.value.openColSetting();
- // 刷新事件
- const onRefresh = () => {
- clearSelection();
- emit('updateTable');
- };
- const filterColumns = ref([]);
- const filterColumnsProp = ref([]);
- // 获取需要显示的列
- const getSelectColumns = () => {
- const flatColumns = flatColumnsFunc(tableColumns);
- filterColumns.value = flatColumns.filter((item) => item.isShow && item.prop !== 'operation' && item.prop);
- filterColumnsProp.value = filterColumns.value.map((item) => item.prop);
- };
- // 更新列设置
- const exportNewColumns = ref([]);
- const exportNewColumnsProp = ref([]);
- const updateColSetting = (test: ColumnProps[]) => {
- // 平铺数组后获取isShow为true的对象
- const flatColumns = flatColumnsFunc(test);
- exportNewColumns.value = flatColumns.filter((item) => item.isShow);
- // 获取prop
- const newColumnsProp = exportNewColumns.value.map((item) => item.prop);
- emit('updateColSetting', exportNewColumns.value, newColumnsProp);
- };
- watch(
- () => exportNewColumns.value,
- (val: any) => {
- console.log(val,'2121')
- if(val.length){
- exportNewColumnsProp.value = val.map((item: any) => item.prop);
- }else{
- exportNewColumns.value = flatColumnsFunc(colSetting);
- exportNewColumnsProp.value = exportNewColumns.value.map((item: any) => item.prop);
- }
- console.log(exportNewColumns.value, 'test');
- },
- { immediate: true,deep:true }
- );
- // 导出当前页
- const exportCurrent = () => {
- emit('exportCurrent');
- };
- // 导出全部
- const exportAll = () => {
- emit('exportAll');
- };
- watch(
- // 监听table数据改变后重置选择
- () => props.data,
- () => {
- clearSelection();
- }
- );
- // 暴露给父组件的参数和方法 (外部需要什么,都可以从这里暴露出去)
- defineExpose({
- element: tableRef,
- tableData: processTableData,
- radio,
- clearSelection,
- enumMap,
- isSelected,
- selectedList,
- selectedListIds,
- filterColumns,
- filterColumnsProp,
- exportNewColumns:shallowRef(exportNewColumns.value)
- });
- </script>
- <style scoped lang="scss">
- .table-header {
- margin-bottom: 15px;
- display: flex;
- justify-content: space-between;
- }
- .header-button-lf {
- flex: 1;
- }
- .header-button-ri {
- }
- </style>
|