基于AVue的二次封装:快速构建后台管理系统的CRUD方案
基于AVue的二次封装:快速构建后台管理系统的CRUD方案
在开发后台管理系统时,表格是常见的组件之一。然而,使用原生的Element Plus实现CRUD(增删改查)功能往往需要编写大量重复代码,过程繁琐。即使借助类似AVue这样的数据驱动视图框架,虽然能简化开发,但配置和使用上仍不够简洁。因此,我对AVue进行了二次封装,打造了一个更高效的CRUD组件——f-crud
,以提升开发效率。
本文将详细介绍封装思路、实现代码以及使用方法,并附上效果图和建议。
一、封装目标
- 简化配置:通过统一的接口和配置项,减少开发者重复编写CRUD逻辑的负担。
- 功能完整:支持表格展示、搜索、分页、增删改查等核心功能。
- 灵活扩展:支持插槽自定义列渲染,适配不同业务场景。
- 快速上手:只需简单配置即可实现完整的CRUD功能。
二、实现方案
1. 使用层代码 (f-crud
使用示例)
以下是基于f-crud
组件的使用示例,直接实现一个日志管理页面:
<template>
<div class="app-log-page flex pt-[10px]">
<f-crud
ref="fcrud"
:option="option"
:operate="operate"
:formProps="formProps"
v-model="form"
class="p-[12px] w-full"
>
<!-- 自定义列渲染:显示应用Logo -->
<template #appLogoUrl="{ row }">
<img v-if="row.appLogoUrl" :src="row.appLogoUrl" alt="" width="60px" height="60px">
</template>
<!-- 可选插槽:自定义操作按钮 -->
<template #menuLeft="{ row }"></template>
<template #menu="{ row }"></template>
<template #search-menu="{ row }"></template>
</f-crud>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from "vue";
// 搜索表单数据
const form = ref({ status: "", userName: "", includedSub: true });
// CRUD 接口配置
const operate = ref({
getList: "/getItalentSyncLogList",
postUrl: "/admin/data/area",
putUrl: "/admin/data/area",
delUrl: "/admin/data/area/",
});
const fcrud = ref(null);
// 分页等基础属性
const formProps = ref({ pageSize: 10 });
// 表格及表单配置
const option = () => ({
border: true,
searchSpan: 4,
searchLabelWidth: 1,
searchIcon: true,
searchGutter: 20,
searchMenuSpan: 7,
searchIndex: 5,
indexWidth: 60,
emptyBtnText: "重置",
columnBtn: false,
index: true,
align: "center",
column: [
{ label: "北森修改人", prop: "operatorName" },
{ label: "动作", prop: "actionTypeName" },
{ label: "类型", prop: "typeName" },
{ label: "同步前组织名称", prop: "name" },
{ label: "同步前信息", prop: "beforeUpdate", showOverflowTooltip: true, overHidden: true, width: 250 },
{ label: "同步后信息", prop: "afterUpdate", showOverflowTooltip: true, overHidden: true, width: 250 },
{ label: "创建时间", prop: "createTime", width: 200, align: "center" },
// 搜索字段(隐藏在表格中)
{ prop: "beforeUpdate", search: true, hide: true, placeholder: "修改前信息搜索", width: 250 },
{ prop: "afterUpdate", search: true, hide: true, placeholder: "修改后信息搜索" },
{
prop: "actionType",
placeholder: "操作类型",
search: true,
hide: true,
type: "select",
dicData: [
{ label: "新增", value: "1" },
{ label: "修改", value: "2" },
{ label: "禁用", value: "3" },
],
},
],
});
// 初始化加载数据
onMounted(() => {
fcrud.value.getList();
});
</script>
<style lang="scss" scoped></style>
效果图:
通过以上代码,只需配置option
(表格和表单结构)、operate
(接口地址)和formProps
(分页参数),即可实现完整的增删改查功能。
2. 封装层代码 (f-crud.vue
)
f-crud
组件基于AVue的avue-crud
,对其进行封装,统一处理数据请求、分页、表单等逻辑:
<template>
<div>
<avue-crud
v-loading="data.listLoading"
@search-reset="resetList"
element-loading-text="Loading..."
element-loading-spinner="svg"
ref="crud"
v-model:page="data.page"
v-model:search="data.tForm"
v-model="data.tForm"
@row-update="rowEdit"
@row-save="rowSave"
@search-change="handleFilter"
@size-change="sizeChange"
@current-change="currentChange"
@row-del="rowDelete"
@refreshChange="handleRefreshChange"
:data="data.list"
:option="option()"
:beforeOpen="beforeOpen"
@selection-change="selectionChange"
>
<template #menu="{ row }">
<slot name="menu" :row="row"></slot>
</template>
<template #menu-left="{ row }">
<slot name="menuLeft" :row="row"></slot>
</template>
<template #search-menu="{ row }">
<slot name="search-menu" :row="row"></slot>
</template>
<template #appLogoUrl="{ row }">
<slot name="appLogoUrl" :row="row"></slot>
</template>
</avue-crud>
</div>
</template>
<script setup lang="ts">
import { reactive, watch, ref, onMounted } from "vue";
import { useTable } from "@/utils/use-table";
interface Props<T = any> {
operate: T;
modelValue: T;
formProps?: T;
option: T;
}
const props = withDefaults(defineProps<Props>(), {
operate: { getList: "", postUrl: "", putUrl: "", delUrl: "" },
modelValue: {},
formProps: {},
option: () => {},
});
const crud = ref<any>(null);
const data = reactive({
page: {
total: 0,
currentPage: 1,
pageSize: props.formProps.pageSize || 20,
isAsc: false,
},
listLoading: true,
getList: props.operate.getList,
postUrl: props.operate.postUrl,
putUrl: props.operate.putUrl,
delUrl: props.operate.delUrl,
props: props.formProps,
query: {},
list: [],
tForm: {},
});
const emit = defineEmits(["update:modelValue", "selectionChange"]);
const beforeOpen = (done: Function) => {
crud.value.dicInit();
done();
};
watch(() => data.tForm, (value) => emit("update:modelValue", value));
onMounted(() => {
data.tForm = props.modelValue;
});
const selectionChange = (list) => emit("selectionChange", list);
const resetList = () => getList();
const { getList, rowSave, rowEdit, rowDelete, handleFilter, handleRefreshChange, currentChange, sizeChange } =
useTable(data);
defineExpose({ getList, rowSave, rowEdit, rowDelete, handleFilter, handleRefreshChange, currentChange, sizeChange, data, crud });
</script>
3. 逻辑层代码 (use-table.ts
)
use-table
是一个独立的工具函数,封装了CRUD的核心逻辑,包括数据获取、分页处理和表单操作:
import { ElMessageBox, ElNotification } from "element-plus";
import Api from "@/api/common";
export function useTable(data: any) {
const getList = (params: any = {}) => {
params = Object.assign(params, data.tForm, data.props || {});
data.listLoading = true;
return Api.getList(
{ ...params, pageNum: data.page.currentPage, pageSize: data.page.pageSize },
data
).then((response: any) => {
data.list = response.data.resultData?.list || response.data.resultData;
data.page.total = response.data.resultData?.total;
data.listLoading = false;
});
};
const handleFilter = (param: any, done: Function) => {
data.page.currentPage = 1;
getList(param);
done && done();
};
const handleRefreshChange = (current?: number) => {
if (current) data.page.currentPage = current;
getList();
};
const sizeChange = (pageSize: number) => {
data.page.pageSize = pageSize;
handleRefreshChange();
};
const currentChange = (current: number) => {
data.page.currentPage = current;
handleRefreshChange();
};
const rowSave = async (form: any, done: Function, loading: Function) => {
if (data.props) Object.assign(form, data.props);
await Api.post(form, data).then(() => {
ElNotification({ message: "操作成功", type: "success" });
done();
loading();
handleRefreshChange();
}).catch(loading);
};
const rowEdit = async (form: any, index: number, done: Function, loading: Function) => {
if (data.props) Object.assign(form, data.props);
await Api.put(form, data).then(() => {
ElNotification({ message: "操作成功", type: "success" });
done && done();
loading && loading();
handleRefreshChange();
}).catch(loading);
};
const rowDelete = (form: any) => {
ElMessageBox.confirm("此操作将永久删除该数据,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(async () => {
await Api.del(form.id, data);
ElNotification({ message: "操作成功", type: "success" });
handleRefreshChange();
});
};
return { getList, rowSave, rowEdit, rowDelete, handleFilter, handleRefreshChange, currentChange, sizeChange };
}
4. 请求层代码 (common.ts
)
封装Axios请求,提供统一的接口调用方法:
import request from "@/plugins/axios";
const common = {
getList: (params: {}, { getList = "", id = "" }) =>
request({ url: getList + id, method: "get", params }),
post: (data: {}, { postUrl = "", postMethod = "post" }) =>
request({ url: postUrl, method: postMethod, data }),
del: (id: string, { delUrl = "", delMethod = "delete" }, params = {}) =>
request({ url: delUrl + id, method: delMethod, params }),
put: (data: {}, { putUrl = "", putMethod = "put" }) =>
request({ url: putUrl, method: putMethod, data }),
};
export default common;
三、使用方法
- 引入组件:将
f-crud.vue
注册到项目中。 - 配置参数:
option
:定义表格列、搜索字段和样式。operate
:提供CRUD接口地址。formProps
:设置分页等额外参数。
- 绑定数据:通过
v-model
绑定搜索表单数据。 - 自定义扩展:通过插槽自定义列渲染或按钮。
四、优点与建议
优点
- 高效开发:只需少量配置即可实现完整CRUD功能。
- 代码复用:逻辑抽离到
use-table
,便于维护和复用。 - 灵活性:支持插槽和AVue原生属性,满足复杂需求。
建议
如果对样式要求不高,可以考虑使用百度的AMIS低代码框架。AMIS提供更强大的CRUD功能和更高的开发效率,尤其适合快速原型开发。
- AVue官网:https://avuejs.com/
- AMIS官网:https://aisuda.bce.baidu.com/amis/zh-CN/docs/index
希望这个封装方案能帮助你更高效地构建后台管理系统!如果有其他需求,可以随时交流优化方案。
这篇文章基于xai进行重新编辑