vue3列表页常用hooks

 UseList组件

import { onMounted, ref, unref, watch } from "vue";
import fileDownload from "js-file-download";

const DEFAULT_MESSAGE = {
  GET_DATA_IF_FAILED: "获取列表数据失败",
  EXPORT_DATA_IF_FAILED: "导出数据失败",
};

export default function useList(listRequestFn, options = {}) {
  const {
    immediate = true,
    preRequest,
    message = DEFAULT_MESSAGE,
    filterOption = ref(),
    exportRequestFn = undefined,
    exportFileName = '导出.xls',
    transformFn = undefined,
  } = options;
  const { GET_DATA_IF_FAILED, EXPORT_DATA_IF_FAILED } = message;
  // 加载态
  const loading = ref(false);
  // 当前页
  const curPage = ref(1);
  // 分页大小
  const pageSize = ref(10);
  // 总
  const total = ref(0);
  // 数据
  const list = ref([]);
//   重置
  const reset = () => {
    if (!filterOption.value) return;
    const keys = Reflect.ownKeys(filterOption.value);
    keys.forEach((key) => {
      Reflect.set(filterOption.value, key, undefined);
    });
    options?.setRefresh?.();
    loadData();
  };
//   筛选
  const filter = () => {
    loadData(curPage.value);
  }

  const loadData = (pageNum = curPage.value) => {
    // 兼容page可能是event
    if (typeof pageNum === 'object') {
        pageNum = unref(curPage);
    }
    return new Promise(async (resolve, reject) => {
      loading.value = true;
      try {
        preRequest?.();
        const result = await listRequestFn({pageSize: pageSize.value, pageNum, ...filterOption.value});
        const transformResult = transformFn ? transformFn(result) : result;
        let data = Array.isArray(transformResult.data) 
          ? transformResult.data
          : "records" in transformResult.data
          ? transformResult.data.records
          : "list" in transformResult.data
          ? transformResult.data.list
          : [];
        let count =
          "total" in transformResult
          ? transformResult.total
          : "total" in transformResult.data
          ? transformResult.data.total
          : 0;
        list.value = data;
        total.value = count;
        options?.requestSuccess?.();
        resolve({
          list: data,
          total: count,
        });
      } catch (error) {
        GET_DATA_IF_FAILED && console.log(GET_DATA_IF_FAILED);
        options?.requestError?.();
      } finally {
        loading.value = false;
      }
    });
  };
//   导出
  const exportFile = async () => {
    if (!exportRequestFn && typeof exportRequestFn !== "function") {
      throw new Error("当前没有提供exportRequest函数");
    }
    try {
      const res = await exportRequestFn(filterOption.value);
      fileDownload(res, exportFileName);
      // window.open(link);
      options?.exportSuccess?.();
    } catch (error) {
      EXPORT_DATA_IF_FAILED && console.log(EXPORT_DATA_IF_FAILED);
      options?.exportError?.();
    }
  };

  // 监听分页数据改变
  watch([curPage, pageSize], () => {
    loadData(curPage.value);
  });

  onMounted(() => {
    if (immediate) {
      loadData(curPage.value);
    }
  });

  return {
    loading,
    curPage,
    pageSize,
    total,
    list,
    filterOption,
    filter,
    reset,
    exportFile,
    loadData,
  };
}

引入后使用

const {
        list,    //请求到的数据列表
        filter,    //用于绑定模糊查询的按钮
        reset,    //用于绑定重置的按钮
        curPage,
        pageSize,
        total,    //这三个值为分页所需,下方会细说
        exportFile,    //用于绑定导出按钮
        loadData    //用于前端改动数据后更新页面
    } = useList(此处为请求数据的list接口, {
        filterOption: searchForm,    //searchForm为绑定模糊查询的数据
        exportRequestFn: 此处为导出的接口,
        exportFileName: '文件.xls',    //该名称可自行定义
        exportParams: () => {        //此处可以设置导出时需要的参数
            const params = ref({})
            params.value = searchForm.value
            return params.value
        }
        preRequest: () => {searchForm.value.State = null},    //该方法内可改变请求接口的参数,如将State值改成null
        setRefresh: () => {
            searchForm.value.xxx = xxx    //重置按钮带参
    }
    });

Table组件

<template>
	<el-table
		ref="myTableRef"
		:data="tableData"
		class="mytable"
		:border="isBorder"
		:stripe="isStripe"
		:max-height="heightVal"
		v-loading="loading"
		v-bind="$attrs"
	>
		<template #empty>
			<div>暂无数据</div>
		</template>
		<slot></slot>
		<template v-for="(item, index) in tableColumns" :key="index">
			<!-- 复选框 -->
			<el-table-column
				v-if="item.type === 'selection'"
				type="selection"
				reserve-selection
				:width="item.width || 60"
				:fixed="item.isFixed"
				v-bind="item.data"
			></el-table-column>
			<!-- 序号 -->
			<el-table-column
				v-else-if="item.type === 'index'"
				type="index"
				:label="item.title || '序号'"
				:min-width="item.width || 60"
				:fixed="item.isFixed"
				:align="item.align || 'center'"
				v-bind="item.data"
			></el-table-column>
			<!-- 自定义slot-->
			<slot v-else-if="item.slot" :name="item.slot" :item="item" :index="index"></slot>

			<el-table-column
				v-else
				:align="item.align || 'left'"
				:fixed="item.isFixed"
				:sortable="item?.isSortable"
				:min-width="item.width"
				:label="item.title"
				:prop="item.prop"
				v-bind="item.data"
			>
				<template #header v-if="item.header">
					<Texpand :column="item" :render="item.header" :colIndex="index" />
				</template>
				<template #default="scope">
					<template v-if="item.render">
						<Texpand :column="item" :row="scope.row" :render="item.render" :index="scope.$index" :colIndex="index" />
					</template>
					<template v-else>
						{{ scope.row[item.prop] }}
					</template>
				</template>
			</el-table-column>
		</template>
	</el-table>
</template>
<script>
export default {
	name: 'MyTable',
};
</script>
<script setup>
import { ref, getCurrentInstance } from 'vue';
import Texpand from './expand.vue';

const props = defineProps({
    tableData: {
        type: Array,
        default: () => {
            return [];
        }
    },
    tableColumns: {
        type: Array,
        default: () => {
            return [];
        }
    },
    objectSpanMethod: {
        type: Object,
        default: () => {
            return {};
        }
    },
    isBorder: {
        type: Boolean,
        default: false
    },
    isStripe: {
        type: Boolean,
        default: true
    },
    heightVal: {
        type: String
    },
    loading: {
        type: Boolean,
        default: false
    },
});

const { proxy } = getCurrentInstance();
const myTableRef = ref();

defineExpose({ myTableRef });
</script>

<style lang="scss" scoped>
</style>

Texpand组件

<script >
import { h, getCurrentInstance, defineComponent } from 'vue';
export default defineComponent({
	name: 'Expand',
	props: {
		row: Object,
		render: Function,
		index: Number,
		colIndex: Number,
		column: {
			type: Object,
			default: null
		}
	},
	// proxy: getCurrentInstance(),
	render() {
		return this.render(h, { row: this.row , index: this.index, colIndex: this.colIndex, column: this.column })
	}
})
</script>

分页组件

<script setup>
const props = defineProps({
  layout: {
    type: String,
    default: "prev, pager, next,sizes,jumper",
  },
  sizes: {
    type: Array,
    default() {
      return [10, 20, 30, 50];
    },
  },
  total: {
    required: true,
    type: Number,
  }
})

const emit = defineEmits(['pagination'])
const currentPage = ref(1)
const pageSize = ref(10)

const handleSizeChange = (val) => {
  emit("pagination", { page: currentPage.value, size: val });
}
const handleCurrentChange = (val) => {
  emit("pagination", { page: val, size: pageSize.value });
}

</script>

<template>
  <el-pagination class="pagination" :layout="layout" v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="sizes"
    :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange">
    <template #default></template>
  </el-pagination>
</template>

<style lang='scss' scoped>
.pagination {
  position: fixed;
  bottom: 30px;
  right: 50px;
  --el-pagination-button-width: 23px !important;
  --el-pagination-button-height: 23px !important;


  :deep(.btn-prev) {
    width: 23px;
    height: 23px;
    background: #FFFFFF;
    border-radius: 4px;
    border: 1px solid #AAAAAA;

  }

  .el-pager {
    margin: 0px 5px;
  }

  :deep(.el-input) {
    width: 26px;
    height: 23px;

    .el-input__inner {
      font-size: 12px;
    }

    .el-input__wrapper {
      padding: 0px 5px;
    }
  }

  :deep(.el-select .el-input) {
    width: 80px;
  }

  :deep(.el-pagination__jump),
  :deep(.el-pagination__classifier) {
    margin-left: 5px !important;
  }

  :deep(.el-pagination__goto) {
    margin-right: 5px !important;
  }
}
</style>

分页组件(Pagination.vue)和useList.js需同时引入文件才能使用分页功能,引入后使用:

<MyTable :tableData="list" :tableColumns="tableColumns" />
<Pagination v-show="total > 0" :total="total" v-model:page="curPage" v-model:size="pageSize" />

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值