FR

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.zip

Installs 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。
151 charsno explicit “when” trigger
Beginner

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

You give it
module name, class name, Chinese name, and field list for an entity
You get back
six frontend files (Model, API, data, index.vue, Modal, ViewModal) for CRUD operations

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

生成步骤

第一步:收集信息

询问用户(如未提供):

  1. 模块名(apiPrefix)(如:demosysnocode
  2. 类名(PascalCase)(如:DemoOrder
  3. 中文名称(如:销售订单)
  4. 字段列表:字段名(camelCase)、TS 类型(string/number/boolean)、中文描述、是否可选
  5. 搜索字段:哪些字段出现在搜索表单中(及组件类型:Input / ApiSelect+字典编码 / DatePicker
  6. 表单字段:哪些字段出现在新增/编辑表单中(及组件类型,是否必填)
  7. 字典字段:哪些字段使用字典渲染(需提供字典编码)

第二步:生成六个文件

按以下顺序生成,所有文件路径基于 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 类型
StringDatestring
IntegerLongShortDoubleBigDecimalnumber
Booleanboolean

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.

SkillInstallsUpdatedSafetyDifficulty
frontend-crud (this skill)02moReviewBeginner
vue-best-practices195moNo flagsIntermediate
vue-pages16moNo flagsIntermediate
pinia16moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry