前言
事先说明我只是个正常的前端开发者,没有使用过太多的后台框架, 最近公司要开发一个后台,让前端自己主选后台模板,查看了两时半之后,我选择了这一款Geeker-Admin | 一款简单易用的中后台模版 (spicyboy.cn)
选择它的原因主要是以下几点:
技术栈
技术栈符合我的技术范围,使用的也是比较流行的技术,vite+vue3+ts
那一套
功能完整
前端需要的功能大多都完善了,作者大大封装了element
的表格系统,代码初看了一遍还是非常方便的,它将搜索栏和操作栏,数据栏都转换为了配置项。
其次我非常喜欢的样式皮肤设置,在这里可以选择各种界面样式,当然暗黑模式也是在这里设置,适应各种管理者的操作习惯。
文档详细
还有一个比较重要的一点是文档详细,作者大大将他的组件都写了详细的文档,清晰易懂,还有各种小细节就不一一介绍了。
使用开发
拉取代码后观看一圈后,我开始尝试着编码菜单模块。
编码前,作者源代码是这样的
没有写弹框以及添加子菜单的操作,然后我就编码了这一个功能。
作为一个大三半年的新手程序员,看完代码之后还是学习到一些写法。
在index.vue同级下创建components/MenuDialog.vue,使用defineExpose将方法暴露给父组件,接收父组件操作名称,字段数据(编辑内容初始化),点击确认调用的api,以及更新表格数据。
html
<template>
<div>
<el-dialog v-model="dialogShow" :title="FData?.title + '菜单'" width="500">
<el-form ref="ruleFormRef" label-width="100px" label-suffix=" :" :rules="rules" :model="FData?.row">
<el-form-item label="路由地址" prop="path">
<el-input v-model="FData.row!.path" placeholder="请填写路由地址" clearable></el-input>
</el-form-item>
<el-form-item label="路由名称" prop="name">
<el-input v-model="FData.row!.name" placeholder="请填写路由名称" clearable></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogShow = false">取消</el-button>
<el-button type="primary" @click="handleSubmit()"> 确定 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="MenuDialog">
import { reactive, ref } from "vue";
import { ElDialog, ElMessage, FormInstance } from "element-plus";
const dialogShow = ref(false);
const FData = ref<FProps>({
title: "",
row: {}
});
const rules = reactive({
path: [{ required: true, message: "请填写路由地址" }],
name: [{ required: true, message: "请填写路由名称" }]
});
// 提交数据(新增/编辑)
const ruleFormRef = ref<FormInstance>();
const handleSubmit = () => {
ruleFormRef.value!.validate(async valid => {
if (!valid) return;
try {
await FData.value?.api!(FData.value?.row);
ElMessage.success({ message: `${FData.value?.title}菜单成功!` });
FData.value?.getTableList!();
dialogShow.value = false;
} catch (error) {
console.log(error);
}
});
};
interface FProps {
title: string;
row: Partial<Menu.MenuOptions>;
api?: (params: any) => Promise<any>;
getTableList?: () => void;
}
// 接收父组件传过来的参数
const acceptParams = (params: FProps) => {
FData.value = params;
dialogShow.value = true;
};
defineExpose({
acceptParams
});
</script>
<style lang="scss" scoped></style>
由于是假数据,编辑器会报错,换成Promise
请求就不会了,代码如下,使用ProTable
组件,配置请求api,columns
如果api
的返回不符合处理规范,使用data-callback
可以进行处理。
最后我有样学样的把操作都写在一个方法内,将数据处理后调用子组件方法,值得注意的是我尝试编码是没有写处理api的,把传给子组件的api
改成调用的就行。
html
<template>
<div class="table-box">
<ProTable
ref="proTable"
title="菜单列表"
row-key="path"
:indent="20"
:columns="columns"
:request-api="getAuthMenuListApi"
:data-callback="dataCallback"
>
<!-- 表格 header 按钮 -->
<template #tableHeader>
<el-button type="primary" :icon="CirclePlus" @click="menuOperate('新增')">新增菜单 </el-button>
</template>
<!-- 菜单图标 -->
<template #icon="scope">
<el-icon :size="18">
<component :is="scope.row.meta.icon"></component>
</el-icon>
</template>
<!-- 菜单操作 -->
<template #operation="scope">
<el-button type="primary" link :icon="CirclePlus" @click="menuOperate('新增子', scope.row)"> 新增子菜单 </el-button>
<el-button type="warning" link :icon="EditPen" @click="menuOperate('编辑', scope.row)"> 编辑 </el-button>
<el-button type="danger" link :icon="Delete" @click="menuOperate('删除', scope.row)"> 删除 </el-button>
</template>
</ProTable>
<MenuDialog ref="menuDialogRef"></MenuDialog>
</div>
</template>
<script setup lang="ts" name="menuMange">
import { ref } from "vue";
import { ColumnProps } from "@/components/ProTable/interface";
import { Delete, EditPen, CirclePlus } from "@element-plus/icons-vue";
import ProTable from "@/components/ProTable/index.vue";
import MenuDialog from "./components/MenuDialog.vue";
import { getAuthMenuListApi } from "@/api/modules/login";
import { ElMessage, ElMessageBox } from "element-plus";
const proTable = ref();
const menuDialogRef = ref<InstanceType<typeof MenuDialog> | null>(null);
const dataCallback = (data: any) => {
return {
list: data,
total: data.total,
pageNum: data.pageNum,
pageSize: data.pageSize
};
};
// 表格配置项
const columns: ColumnProps[] = [
{ prop: "meta.title", label: "菜单名称", align: "left", search: { el: "input" } },
{ prop: "meta.icon", label: "菜单图标" },
{ prop: "name", label: "菜单 name", search: { el: "input" } },
{ prop: "path", label: "菜单路径", width: 300, search: { el: "input" } },
{ prop: "component", label: "组件路径", width: 300 },
{ prop: "operation", label: "操作", width: 300, fixed: "right" }
];
const menuOperate = (title: string, row: Partial<Menu.MenuOptions> = {}) => {
if (title == "删除") {
ElMessageBox.confirm(`确认删除菜单 ${row.meta?.title} 及其子菜单`, `删除菜单 ${row.meta?.title} 及其子菜单`, {
type: "error"
}).then(() => {
ElMessage.success("删除成功");
});
return;
}
const params = {
title: title == "编辑" ? "编辑" + row.meta?.title : title == "新增子" ? "新增" + row.meta?.title + "子" : title,
row: title == "新增子" ? {} : { ...row },
api: () => {
console.log("操作成功");
},
getTableList: proTable.value?.getTableList
};
menuDialogRef.value?.acceptParams(params);
};
</script>
最终效果如下
总结
写起来还是非常方便的,刚刚开始可能看不太懂,写了一遍之后就好多了 , 项目中使用ts的范围非常广 , 我之前接触的项目,有一半都懒得给直接any
,但是初看下来框架中都给了类型,请求的返回值也给了,有些类型我看不懂,还在学习中。