CRUD 快速开发
作者:唐亚峰 | battcn
字数统计:1.6k 字
学习目标
掌握使用 Fast Crud 快速开发增删改查页面
简介
Wemirr Platform 集成了 ,只需几行代码即可完成 CRUD 页面开发。http://fast-crud.docmirror.cn/
基础示例
最简示例
vue
<script setup lang="ts">
import { useFs } from '@fast-crud/fast-crud';
import { getUserList, createUser, updateUser, deleteUser } from './api';
const { crudBinding, crudRef } = useFs({
crudOptions: {
request: {
pageRequest: async (query) => {
const res = await getUserList(query);
return { records: res.data.records, total: res.data.total };
},
addRequest: async ({ form }) => await createUser(form),
editRequest: async ({ form }) => await updateUser(form.id, form),
delRequest: async ({ row }) => await deleteUser(row.id),
},
columns: {
username: {
title: '用户名',
type: 'text',
search: { show: true },
form: { rules: [{ required: true, message: '请输入用户名' }] },
},
email: {
title: '邮箱',
type: 'text',
search: { show: true },
},
status: {
title: '状态',
type: 'dict-select',
dict: {
data: [
{ label: '启用', value: 1, color: 'success' },
{ label: '禁用', value: 0, color: 'error' },
],
},
},
createTime: {
title: '创建时间',
type: 'datetime',
form: { show: false },
column: { width: 180 },
},
},
},
});
</script>
<template>
<fs-crud ref="crudRef" v-bind="crudBinding" />
</template>只需要上面这段代码,就能得到一个完整的 CRUD 页面,包含:
- ✅ 数据列表
- ✅ 分页
- ✅ 搜索
- ✅ 新增
- ✅ 编辑
- ✅ 删除
配置详解
columns 列配置
typescript
const columns = {
// 字段名
username: {
title: '用户名', // 列标题
type: 'text', // 字段类型
// 列表配置
column: {
width: 120, // 列宽
fixed: 'left', // 固定列
show: true, // 是否显示
sortable: true, // 是否可排序
align: 'center', // 对齐方式
formatter: (context) => {}, // 格式化
},
// 表单配置
form: {
show: true, // 是否显示
disabled: false, // 是否禁用
rules: [], // 校验规则
component: {}, // 组件配置
col: { span: 12 }, // 栅格配置
},
// 搜索配置
search: {
show: true, // 是否显示
component: {}, // 组件配置
},
// 新增表单配置(覆盖 form)
addForm: {},
// 编辑表单配置(覆盖 form)
editForm: {},
// 查看表单配置
viewForm: {},
},
};字段类型
| 类型 | 说明 | 示例 |
|---|---|---|
text | 文本输入 | 用户名、标题 |
textarea | 多行文本 | 备注、描述 |
number | 数字输入 | 年龄、数量 |
password | 密码输入 | 密码 |
select | 下拉选择 | 状态 |
dict-select | 字典下拉 | 带字典的选择 |
radio | 单选 | 性别 |
checkbox | 多选 | 爱好 |
switch | 开关 | 是否启用 |
date | 日期 | 生日 |
datetime | 日期时间 | 创建时间 |
daterange | 日期范围 | 查询时间段 |
time | 时间 | 开始时间 |
cascader | 级联选择 | 省市区 |
tree-select | 树选择 | 部门 |
upload | 文件上传 | 头像、附件 |
editor | 富文本 | 文章内容 |
request 请求配置
typescript
const crudOptions = {
request: {
// 分页查询
pageRequest: async (query) => {
// query 包含: page, limit, 搜索条件
const { page, limit, ...searchParams } = query;
const res = await api.list({ current: page, size: limit, ...searchParams });
return {
records: res.data.records,
total: res.data.total,
currentPage: res.data.current,
pageSize: res.data.size,
};
},
// 新增
addRequest: async ({ form }) => {
return await api.create(form);
},
// 编辑
editRequest: async ({ form, row }) => {
return await api.update(row.id, form);
},
// 删除
delRequest: async ({ row }) => {
return await api.delete(row.id);
},
// 详情(查看时获取)
infoRequest: async ({ row }) => {
return await api.getInfo(row.id);
},
},
};高级功能
字典使用
typescript
const columns = {
status: {
title: '状态',
type: 'dict-select',
dict: {
// 方式一:静态数据
data: [
{ label: '启用', value: 1, color: 'success' },
{ label: '禁用', value: 0, color: 'error' },
],
// 方式二:远程字典
url: '/api/dict/status',
// 方式三:自定义获取
getData: async () => {
const res = await getDictData('sys_status');
return res.data;
},
},
},
};表单联动
typescript
const columns = {
type: {
title: '类型',
type: 'dict-select',
dict: { data: [{ label: '个人', value: 1 }, { label: '企业', value: 2 }] },
form: {
valueChange: ({ form, value }) => {
// 切换类型时,清空关联字段
if (value === 1) {
form.companyName = '';
} else {
form.idCard = '';
}
},
},
},
companyName: {
title: '公司名称',
type: 'text',
form: {
// 仅企业类型显示
show: compute(({ form }) => form.type === 2),
},
},
idCard: {
title: '身份证号',
type: 'text',
form: {
// 仅个人类型显示
show: compute(({ form }) => form.type === 1),
},
},
};自定义列渲染
typescript
const columns = {
avatar: {
title: '头像',
type: 'text',
column: {
cellRender: ({ row }) => {
return <a-avatar src={row.avatar} />;
},
},
},
status: {
title: '状态',
type: 'dict-select',
column: {
cellRender: ({ row }) => {
const color = row.status === 1 ? 'green' : 'red';
const text = row.status === 1 ? '启用' : '禁用';
return <a-tag color={color}>{text}</a-tag>;
},
},
},
};自定义操作按钮
typescript
const crudOptions = {
rowHandle: {
width: 200,
buttons: {
view: { show: true }, // 查看按钮
edit: { show: true }, // 编辑按钮
remove: { show: true }, // 删除按钮
// 自定义按钮
audit: {
text: '审核',
type: 'primary',
show: compute(({ row }) => row.status === 0),
click: async ({ row }) => {
await auditApi(row.id);
crudExpose.doRefresh();
},
},
},
},
};工具栏配置
typescript
const crudOptions = {
toolbar: {
buttons: {
add: { show: true }, // 新增按钮
refresh: { show: true }, // 刷新按钮
export: { show: true }, // 导出按钮
// 自定义按钮
import: {
text: '导入',
icon: 'ant-design:upload-outlined',
click: () => {
// 打开导入弹窗
},
},
},
},
};权限控制
typescript
import { usePermission } from '@/hooks/web/usePermission';
const { hasPermission } = usePermission();
const crudOptions = {
toolbar: {
buttons: {
add: { show: hasPermission('user:add') },
},
},
rowHandle: {
buttons: {
edit: { show: hasPermission('user:edit') },
remove: { show: hasPermission('user:delete') },
},
},
};完整示例
以下是一个用户管理页面的完整示例:
vue
<script setup lang="ts">
import { ref } from 'vue';
import { useFs, compute } from '@fast-crud/fast-crud';
import { message } from 'ant-design-vue';
import { getUserList, createUser, updateUser, deleteUser, resetPassword } from './api';
import { usePermission } from '@/hooks/web/usePermission';
const { hasPermission } = usePermission();
const { crudBinding, crudRef, crudExpose } = useFs({
crudOptions: {
request: {
pageRequest: async (query) => {
const { page, limit, ...params } = query;
const res = await getUserList({ current: page, size: limit, ...params });
return { records: res.data.records, total: res.data.total };
},
addRequest: async ({ form }) => {
await createUser(form);
message.success('创建成功');
},
editRequest: async ({ form }) => {
await updateUser(form.id, form);
message.success('更新成功');
},
delRequest: async ({ row }) => {
await deleteUser(row.id);
message.success('删除成功');
},
},
toolbar: {
buttons: {
add: { show: hasPermission('user:add') },
},
},
rowHandle: {
width: 220,
buttons: {
view: { show: true },
edit: { show: hasPermission('user:edit') },
remove: { show: hasPermission('user:delete') },
resetPwd: {
text: '重置密码',
type: 'link',
show: hasPermission('user:resetPwd'),
click: async ({ row }) => {
await resetPassword(row.id);
message.success('密码已重置为 123456');
},
},
},
},
columns: {
id: {
title: 'ID',
type: 'text',
column: { width: 80 },
form: { show: false },
},
avatar: {
title: '头像',
type: 'avatar-uploader',
column: {
width: 80,
align: 'center',
},
},
username: {
title: '用户名',
type: 'text',
search: { show: true },
form: {
rules: [{ required: true, message: '请输入用户名' }],
},
editForm: { disabled: true },
},
nickname: {
title: '昵称',
type: 'text',
form: {
rules: [{ required: true, message: '请输入昵称' }],
},
},
email: {
title: '邮箱',
type: 'text',
search: { show: true },
form: {
rules: [
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入有效的邮箱地址' },
],
},
},
mobile: {
title: '手机号',
type: 'text',
search: { show: true },
form: {
rules: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号' }],
},
},
deptId: {
title: '部门',
type: 'tree-select',
form: {
component: {
loadData: async () => {
const res = await getDeptTree();
return res.data;
},
fieldNames: { label: 'name', value: 'id' },
},
},
column: {
formatter: ({ row }) => row.deptName,
},
},
roleIds: {
title: '角色',
type: 'select',
form: {
component: {
mode: 'multiple',
options: async () => {
const res = await getRoleList();
return res.data.map((item) => ({ label: item.name, value: item.id }));
},
},
},
column: { show: false },
},
status: {
title: '状态',
type: 'dict-select',
search: { show: true },
dict: {
data: [
{ label: '启用', value: 1, color: 'success' },
{ label: '禁用', value: 0, color: 'error' },
],
},
},
createTime: {
title: '创建时间',
type: 'datetime',
form: { show: false },
column: { width: 180 },
},
},
},
});
</script>
<template>
<div class="user-manage">
<fs-crud ref="crudRef" v-bind="crudBinding" />
</div>
</template>
<style scoped>
.user-manage {
padding: 16px;
}
</style>