frontend-crud
Auto-generates standard CRUD pages for Vue 3 projects.
Install
mkdir -p .claude/skills/frontend-crud && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/12986" && unzip -o skill.zip -d .claude/skills/frontend-crud && rm skill.zipInstalls to .claude/skills/frontend-crud
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
为 mfish-nocode-pro 项目前端(Vue3 + TypeScript)生成标准增删改查页面代码,包括 Model、API、data、index.vue、Modal、ViewModal 六个文件。当用户说"帮我生成前端增删改查"、"新增前端页面"、"生成前端CRUD"时使用此 skill。Key capabilities
- →Generate Model type definitions for entities
- →Create API request functions for CRUD operations
- →Configure data files for table columns and form schemas
- →Generate `index.vue` for the list page
- →Create `Modal.vue` for add/edit pop-ups
- →Develop `ViewModal.vue` for detail viewing pop-ups
How it works
The skill collects entity information from the user and then generates six standard frontend files following the `mfish-nocode-pro` project's architecture and technology stack conventions.
Inputs & outputs
When to use frontend-crud
- →Generate new CRUD page
- →Build API and table boilerplate
- →Add form components
- →Setup view modals
About this skill
前端 CRUD 代码生成器
项目前端架构
mfish-nocode-view/src/
├── api/
│ └── {apiPrefix}/ # 如:sys、demo、nocode
│ ├── model/
│ │ └── {类名}Model.ts # 接口类型定义
│ └── {类名}.ts # API 请求函数
└── views/
└── {apiPrefix}/
└── {entity-kebab-case}/ # 如:demo-order、sys-dict
├── {变量名}.data.ts # 表格列、搜索表单、表单Schema、详情Schema
├── index.vue # 列表页主页面
├── {类名}Modal.vue # 新增/编辑弹窗
└── {类名}ViewModal.vue # 详情查看弹窗
技术栈约定
- 框架:Vue 3 + TypeScript +
<script lang="ts" setup> - HTTP:
defHttp(来自@mfish/core/utils/http/axios) - 表格:
BasicTable+useTable(来自@mfish/core/components/Table) - 弹窗:
BasicModal+useModal/useModalInner(来自@mfish/core/components/Modal) - 表单:
BasicForm+useForm(来自@mfish/core/components/Form) - 详情:
Description+useDescription(来自@mfish/core/components/Description) - 字典:
buildDictTag+getDictProps(来自@mfish/core/components/DictTag) - 权限:
v-auth="'{apiPrefix}:{entityName}:操作'"(insert/update/delete/query/export) - 基础类型:
BaseEntity<string>、PageResult<T>、ReqPage(来自@mfish/core/api) - ID 类型:默认
string,数值型主键时为number
生成步骤
第一步:收集信息
询问用户(如未提供):
- 模块名(apiPrefix)(如:
demo、sys、nocode) - 类名(PascalCase)(如:
DemoOrder) - 中文名称(如:销售订单)
- 字段列表:字段名(camelCase)、TS 类型(
string/number/boolean)、中文描述、是否可选 - 搜索字段:哪些字段出现在搜索表单中(及组件类型:
Input/ApiSelect+字典编码 /DatePicker) - 表单字段:哪些字段出现在新增/编辑表单中(及组件类型,是否必填)
- 字典字段:哪些字段使用字典渲染(需提供字典编码)
第二步:生成六个文件
按以下顺序生成,所有文件路径基于 mfish-nocode-view/src/ 目录。
1. Model 类型定义(api/{apiPrefix}/model/{类名}Model.ts)
import { BaseEntity, PageResult, ReqPage } from "@mfish/core/api";
/**
* @description: {中文名称}
* @author: mfish
* @date: {当前日期}
* @version: V2.4.1
*/
export interface {类名} extends BaseEntity<string> {
//{字段注释}
{字段名}?: {TS类型};
// ... 更多字段
}
export interface Req{类名} extends ReqPage {
//{搜索字段注释}
{搜索字段名}?: {TS类型};
// ... 更多搜索字段
}
//分页结果集
export type {类名}PageModel = PageResult<{类名}>;
字段类型映射规则:
| Java/DB 类型 | TS 类型 |
|---|---|
String、Date | string |
Integer、Long、Short、Double、BigDecimal | number |
Boolean | boolean |
2. API 请求文件(api/{apiPrefix}/{类名}.ts)
import { defHttp } from "@mfish/core/utils/http/axios";
import { {类名}, Req{类名}, {类名}PageModel } from "@/api/{apiPrefix}/model/{类名}Model";
/**
* @description: {中文名称}
* @author: mfish
* @date: {当前日期}
* @version: V2.4.1
*/
enum Api {
{类名} = "/{apiPrefix}/{变量名}"
}
/**
* 分页列表查询
*/
export const get{类名}List = (req{类名}?: Req{类名}) => {
return defHttp.get<{类名}PageModel>({ url: Api.{类名}, params: req{类名} });
};
/**
* 通过id查询
*/
export function get{类名}ById(id: string) {
return defHttp.get<{类名}>({ url: `${Api.{类名}}/${id}` });
}
/**
* 导出{中文名称}
*/
export function export{类名}(req{类名}?: Req{类名}) {
return defHttp.download({ url: `${Api.{类名}}/export`, params: req{类名} });
}
/**
* 新增{中文名称}
*/
export function insert{类名}({变量名}: {类名}) {
return defHttp.post<{类名}>({ url: Api.{类名}, params: {变量名} }, { successMessageMode: "message" });
}
/**
* 修改{中文名称}
*/
export function update{类名}({变量名}: {类名}) {
return defHttp.put<{类名}>({ url: Api.{类名}, params: {变量名} }, { successMessageMode: "message" });
}
/**
* 删除{中文名称}
*/
export function delete{类名}(id: string) {
return defHttp.delete<boolean>({ url: `${Api.{类名}}/${id}` }, { successMessageMode: "message" });
}
/**
* 批量删除{中文名称}
*/
export function deleteBatch{类名}(ids: string) {
return defHttp.delete<boolean>({ url: `${Api.{类名}}/batch/${ids}` }, { successMessageMode: "message" });
}
若 ID 类型为数值型(
number),delete{类名}参数类型改为number。
3. data 配置文件(views/{apiPrefix}/{entity-kebab-case}/{变量名}.data.ts)
import { BasicColumn, FormSchema } from "@mfish/core/components/Table";
import { DescItem } from "@mfish/core/components/Description";
// 有字典字段时引入(无字典字段则删除)
import { buildDictTag, getDictProps } from "@mfish/core/components/DictTag";
/**
* @description: {中文名称}
* @author: mfish
* @date: {当前日期}
* @version: V2.4.1
*/
// ========== 表格列定义 ==========
export const columns: BasicColumn[] = [
// 普通字段
{
title: "{字段中文名}",
dataIndex: "{字段名}",
width: 120
},
// 字典字段(有字典时使用 customRender)
{
customRender: ({ record }) => {
return buildDictTag("{字典编码}", record.{字段名});
},
title: "{字段中文名}",
dataIndex: "{字段名}",
width: 120
}
];
// ========== 搜索表单 Schema ==========
export const searchFormSchema: FormSchema[] = [
// 普通输入框
{
field: "{字段名}",
label: "{字段中文名}",
component: "Input",
colProps: { xl: 5, md: 6 }
},
// 字典下拉(单选)
{
field: "{字段名}",
label: "{字段中文名}",
component: "ApiSelect",
componentProps: getDictProps("{字典编码}"),
colProps: { xl: 5, md: 6 }
},
// 字典下拉(多选)
{
field: "{字段名}",
label: "{字段中文名}",
component: "ApiSelect",
componentProps: { ...getDictProps("{字典编码}"), mode: "multiple" },
colProps: { xl: 5, md: 6 }
}
];
// ========== 新增/编辑表单 Schema ==========
export const {变量名}FormSchema: FormSchema[] = [
{
field: "id",
label: "唯一ID",
component: "Input",
show: false
},
// 文本输入
{
field: "{字段名}",
label: "{字段中文名}",
component: "Input",
required: true // 必填时加上
},
// 数值输入
{
field: "{字段名}",
label: "{字段中文名}",
component: "InputNumber"
},
// 字典下拉
{
field: "{字段名}",
label: "{字段中文名}",
component: "ApiSelect",
componentProps: getDictProps("{字典编码}")
},
// 日期(仅日期)
{
field: "{字段名}",
label: "{字段中文名}",
component: "DatePicker",
componentProps: {
valueFormat: "YYYY-MM-DD",
format: "YYYY-MM-DD",
getPopupContainer: () => document.body
}
},
// 日期时间
{
field: "{字段名}",
label: "{字段中文名}",
component: "DatePicker",
componentProps: {
valueFormat: "YYYY-MM-DD HH:mm:ss",
format: "YYYY-MM-DD HH:mm:ss",
showTime: { format: "HH:mm:ss" },
getPopupContainer: () => document.body
}
}
];
// ========== 详情查看 Schema ==========
export class {类名}Desc {
viewSchema: DescItem[] = [
{
label: "id",
field: "id",
show: () => false
},
// 普通字段
{
field: "{字段名}",
label: "{字段中文名}"
},
// 字典字段
{
render: (val) => {
if (val === undefined) return;
return buildDictTag("{字典编码}", val);
},
field: "{字段名}",
label: "{字段中文名}"
}
];
}
组件选择规则:
| 字段类型 | 表单组件 |
|---|---|
string(普通文本) | Input |
number(整数/小数) | InputNumber |
string(日期) | DatePicker(dateFormat: YYYY-MM-DD) |
string(日期时间) | DatePicker(showTime) |
| 有字典 | ApiSelect + getDictProps("{字典编码}") |
4. 列表主页面(views/{apiPrefix}/{entity-kebab-case}/index.vue)
<!--
@description: {中文名称}
@author: mfish
@date: {当前日期}
@version: V2.4.1
-->
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<AButton type="primary" @click="handleCreate" v-auth="'{apiPrefix}:{变量名}:insert'">新增</AButton>
<AButton color="warning" @click="handleExport" v-auth="'{apiPrefix}:{变量名}:export'">导出</AButton>
<AButton color="error" @click="handleBatchDelete" v-auth="'{apiPrefix}:{变量名}:delete'">批量删除</AButton>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: 'ant-design:info-circle-outlined',
onClick: handleQuery.bind(null, record),
auth: '{apiPrefix}:{变量名}:query',
color: 'success',
tooltip: '查看'
},
{
icon: 'ant-design:edit-outlined',
onClick: handleEdit.bind(null, record),
auth: '{apiPrefix}:{变量名}:update',
tooltip: '修改'
},
{
icon: 'ant-design:delete-outlined',
color: 'error',
popConfirm: {
title: '是否确认删除',
placement: 'left',
confirm: handleDelete.bind(null, record)
},
auth: '{apiPrefix}:{变量名}:delete',
tooltip: '删除'
}
]"
/>
</template>
</template>
</BasicTable>
<{类名}Modal @register="registerModal" @success="handleSuccess" />
<{类名}ViewModal @register="registerViewModal" />
</div>
</template>
<script lang="ts" setup>
import { BasicTable, useTable, TableAction } from "@mfish/core/components/Table";
import { useModal } from "@mfish/core/components/Modal";
import { Button as AButton } from "@mfish/core/components/Button";
import { deleteBatch{类名}, delete{类名}, export{类名}, get{类名}List } from "@/api/{apiPrefix}/{类名}";
import {类名}Modal from "./{类名}Modal.vue";
import {类名}ViewModal from "./{类名}ViewModal.vue";
import { columns, searchFormSchema } from "./{变量名}.data";
import { {类名} } from "@/api/{apiPrefix}/model/{类名}Model";
import { ref } from "vue";
import { useMessage } from "@mfish/core/hooks";
defineOptions({ name: "{类名}Management" });
const [registerModal, { openModal }] = useModal();
const [registerViewModal, { openModal: openViewModal }] = useModal();
const selectedRowKeys = ref<any[]>([]);
const [registerTable, { reload, getForm }] = useTable({
title: "{中文名称}列表",
api: get{类名}List,
rowKey: "id",
columns,
formConfig: {
name: "search_form_item",
labelWidth: 100,
schemas: searchFormSchema,
autoSubmitOnEnter: true
},
useSearchForm: true,
showTableSetting: true,
bordered: true,
showIndexColumn: false,
rowSelection: {
onChange: (rowKeys: any[]) => {
selectedRowKeys.value = rowKeys;
}
},
actionColumn: {
width: 120,
title: "操作",
dataIndex: "action"
---
*Content truncated.*
When not to use it
- →When the project is not Vue 3 + TypeScript
- →When not using `@mfish/core` components for UI
- →When not following the specified project frontend architecture
Limitations
- →The skill assumes the use of Vue 3 + TypeScript
- →The skill assumes the use of `@mfish/core` components
- →The skill requires specific naming conventions for generated files
How it compares
This workflow automates the generation of a complete set of CRUD frontend files based on specific project architecture and component libraries, which is faster and more consistent than manual file creation.
Compared to similar skills
frontend-crud side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| frontend-crud (this skill) | 0 | 2mo | Review | Beginner |
| vue-best-practices | 19 | 5mo | No flags | Intermediate |
| vue-pages | 1 | 6mo | No flags | Intermediate |
| pinia | 1 | 6mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
vue-best-practices
antfu
MUST be used for Vue.js tasks. Strongly recommends Composition API with `<script setup>` and TypeScript as the standard approach. Covers Vue 3, SSR, Volar, vue-tsc. Load for any Vue, .vue files, Vue Router, Pinia, or Vite with Vue work. ALWAYS use Composition API unless the project explicitly requires Options API.
vue-pages
JaguarJack
Generate Vue frontend pages using catch-table for CatchAdmin module with full component features.
pinia
antfu
Pinia official Vue state management library, type-safe and extensible. Use when defining stores, working with state/getters/actions, or implementing store patterns in Vue apps.
code-generation
plentymarkets
Automates component, composable, and CMS block scaffolding using the PlentyONE Shop CLI. Use this when creating new code structures instead of manually copying existing files.
vue
valibali
Use when editing .vue files, creating Vue 3 components, writing composables, or testing Vue code - provides Composition API patterns, props/emits best practices, VueUse integration, and reactive destructuring guidance
vue
TerminalSkills
|