CRUD 快速集成
2026/10/30大约 4 分钟
CRUD 快速集成(createCrudApi / useCrudDialog)
am_plugin 脚手架内置两套 CRUD 提速工具(与基座 am-web 同一实现,双端行为一致):
createCrudApi(@/api/crud):一条配置生成标准六方法 CRUD 接口useCrudDialog(@/hooks):收敛新增/编辑弹窗的骨架逻辑,弹窗文件只保留表单配置与业务差异
import { createCrudApi } from '@/api/crud'
import { useCrudDialog } from '@/hooks'createCrudApi:一行生成 CRUD 接口
import { createCrudApi } from '@/api/crud'
// 插件自己的后端(requester 传 pluginRequest,自动拼接 /app/{name} 前缀)
import pluginRequest from '@/utils/http/plugin'
export default createCrudApi('/order', { requester: pluginRequest })
// 基座系统接口(默认 request)
export default createCrudApi('/sys/role')生成的方法与 URL 约定:
| 方法 | 请求 | URL |
|---|---|---|
list(params) | GET | {base}/index |
read(id) | GET | {base}/read?id={id} |
save(data) | POST | {base}/save |
update(data) | POST | {base}/update |
delete(params) | POST | {base}/delete |
batchDelete(params) | POST | {base}/batchDelete |
若后端 CRUD 端点与上述约定不同,不要用工厂,直接手写业务方法即可(工厂不限制你扩展)。
选项
| 选项 | 说明 |
|---|---|
list | 附加到 list 请求的额外配置,如 { list: { decompress: true } }(列表接口压缩传输) |
requester | 请求器,默认 request;插件自己后端传 pluginRequest |
useCrudDialog:收敛弹窗骨架
适用本地 ElDialog + AmForm/ArtForm 的弹窗。骨架(formModel、标题、回填、提交、取消、关闭后重置)全部收敛进 composable,业务差异通过钩子注入。
<script setup lang="ts">
import { reactive } from 'vue'
import orderApi from '@/api/order' // 上一步 createCrudApi 生成的模块
import { useCrudDialog } from '@/hooks'
interface Props {
visible: boolean
type: string
editData?: any
}
const props = withDefaults(defineProps<Props>(), { visible: false, type: 'add' })
const emit = defineEmits<{ (e: 'update:visible', v: boolean): void; (e: 'submit', d: any): void }>()
const form = reactive({ id: 0, name: '', status: 1 })
const { formRef, formModel, dialogTitle, handleSubmit, handleCancel, handleClosed } = useCrudDialog({
form,
props,
emit,
api: orderApi,
resetFields: () => {
form.id = 0
form.status = 1
}
})
</script>模板与普通 ElDialog 一致:ref="formRef"、v-model="formModel"、标题用 dialogTitle,底部按钮绑定 handleSubmit / handleCancel,弹窗 @closed="handleClosed"。
必传选项
| 选项 | 说明 |
|---|---|
form | 表单 reactive 对象 |
props | 组件 props(读取 visible/modelValue、type、editData) |
emit | 组件 emit |
api | 需含 save / update 两个方法 |
常用钩子(可选)
| 选项 | 默认 | 说明 |
|---|---|---|
closeEvent | update:visible | 关闭事件名,用 modelValue 时传 update:modelValue |
submitEvent | submit | 成功事件名 |
emitData | true | 成功后是否回传表单数据 |
closeOnSuccess | true | 成功后是否关闭弹窗 |
resetOnCancel | false | 取消时是否重置表单 |
showValidationError | false | 校验失败是否提示"表单校验失败" |
titleText | 新建/编辑 | 自定义标题,如 (e) => e ? '编辑订单' : '新建订单' |
resetFields | 无 | 重置字段(不含 formRef.reset()) |
loadIsEdit | type === 'edit' | 编辑态判定 |
beforeWatchLoad | 无 | 弹窗打开时、加载前的重置(如 form.id = 0) |
afterLoad | 无 | 回填后处理(异步详情、关联数据解析) |
beforeSubmit | 无 | 校验通过后、提交前(二次表单校验、数据合并) |
buildPayload | { ...form } | 提交数据组装 |
onSuccess | 无 | 提交成功回调(emit 之前) |
接口失败时保持弹窗打开供重试,错误提示由 HTTP 拦截器统一处理;"表单校验失败"只在
showValidationError且校验未通过时出现。
完整示例:订单 CRUD
接口层 src/api/order.ts:
import { createCrudApi } from '@/api/crud'
import pluginRequest from '@/utils/http/plugin'
export default createCrudApi('/order', { requester: pluginRequest })弹窗 src/views/manage/order/modules/order-dialog.vue:
<template>
<ElDialog :title="dialogTitle" :model-value="visible" @update:model-value="handleCancel" width="500px" align-center @closed="handleClosed">
<AmForm ref="formRef" v-model="formModel" :items="formItems" :rules="rules" label-width="100px" :show-reset="false" :show-submit="false" />
<template #footer>
<ElButton @click="handleCancel">取 消</ElButton>
<ElButton type="primary" @click="handleSubmit">确 定</ElButton>
</template>
</ElDialog>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import orderApi from '@/api/order'
import { useCrudDialog } from '@/hooks'
interface Props {
visible: boolean
type: string
editData?: any
}
const props = withDefaults(defineProps<Props>(), { visible: false, type: 'add' })
const emit = defineEmits<{ (e: 'update:visible', v: boolean): void; (e: 'submit', d: any): void }>()
const form = reactive({ id: 0, name: '', amount: 0, status: 1 })
const { formRef, formModel, dialogTitle, handleSubmit, handleCancel, handleClosed } = useCrudDialog({
form,
props,
emit,
api: orderApi,
titleText: (e) => (e ? '编辑订单' : '新建订单'),
resetFields: () => {
form.id = 0
form.amount = 0
form.status = 1
},
beforeWatchLoad: () => {
form.id = 0
}
})
const rules = {
name: [{ required: true, message: '请输入订单名称', trigger: 'blur' }]
}
const formItems = [
{ label: '订单名称', key: 'name', type: 'input', props: { placeholder: '请输入订单名称' } },
{ label: '金额', key: 'amount', type: 'number', props: { min: 0 } },
{ label: '状态', key: 'status', type: 'dict', props: { dict: 'status', type: 'radio', mode: 'border' } }
]
</script>列表页配合 useTable 使用:
import { useTable } from '@/hooks'
import orderApi from '@/api/order'
const {
data, columns, loading, pagination,
getData, refreshData, refreshCreate, refreshUpdate,
handleSizeChange, handleCurrentChange
} = useTable({
core: {
apiFn: orderApi.list,
columnsFactory: () => [
{ prop: 'id', label: 'ID', width: 100 },
{ prop: 'name', label: '名称', minWidth: 120 },
{ prop: 'status', label: '状态', width: 70, amType: 'dict', dict: 'status' },
{ prop: 'operation', label: '操作', width: 100, fixed: 'right', useSlot: true }
]
}
})何时不用它
- 简单表单弹窗:可继续用
invoke('form.render')直接调宿主渲染表单(见 脚手架示例),无需本地组件 - 接口约定与 CRUD 六方法不符(如
create而非save):直接手写业务方法,工厂只做标准场景提速
