使用plop快速生成菜单模板文件

前言

之前要写管理后台的一个菜单,首先要手动在菜单表创建一条记录,然后在创建view文件和router文件,同时还有其他类似的模板文件要一个个手动创建。当菜单多的时候,真的是要写麻了

今天,咱们通过plop快速生成文件模板,在创建菜单记录的同时生成其他文件模板。

一、首先编写plop指令生成模板文件

1、创建plopfile.js
js 复制代码
const viewGenerator = require('./plop-templates/view/prompt')

module.exports = function(plop) {
  plop.setGenerator('view', viewGenerator)
}
2、编写模板指令文件
js 复制代码
const { notEmpty } = require("../utils.js");

module.exports = {
  description: "generate a view",
  prompts: [
    {
      type: "input",
      name: "rootDir",
      message: "输入根模块路径",
      validate: notEmpty("rootDir")
    },
    {
      type: "input",
      name: "dir",
      message: "输入模块路径",
      validate: notEmpty("dir")
    },
    {
      type: "input",
      name: "name",
      message: "输入模块名称",
      validate: notEmpty("name")
    },
    {
      type: "input",
      name: "menuName",
      message: "输入模块中文名称",
      validate: notEmpty("menuName")
    },
    {
      type: "input",
      name: "rootName",
      message: "输入根模块名称",
      validate: notEmpty("rootName")
    },
    {
      type: "input",
      name: "rootMenuName",
      message: "输入根模块中文名称",
      validate: notEmpty("rootMenuName")
    },
    {
      type: "checkbox",
      name: "blocks",
      message: "Blocks:",
      choices: [{
        name: "<template>",
        value: "template",
        checked: true
      },
        {
          name: "<script>",
          value: "script",
          checked: true
        },
        {
          name: "style",
          value: "style",
          checked: true
        }
      ],
      validate(value) {
        if (value.indexOf("script") === -1 && value.indexOf("template") === -1) {
          return "View require at least a <script> or <template> tag.";
        }
        return true;
      }
    }
  ],
  actions: data => {
    const dir = "{{dir}}";
    const rootDir = "{{rootDir}}";
    const rootName = "{{rootName}}";
    const rootMenuName = "{{rootMenuName}}";
    const name = "{{name}}";
    const menuName = "{{menuName}}";
    const actions = [{
      type: "add",
      path: `src/views/${rootDir}/${dir}/index.vue`,
      templateFile: "plop-templates/view/index.hbs",
      data: {
        dir: dir,
        rootDir: rootDir,
        rootName: rootName,
        rootMenuName: rootMenuName,
        name: name,
        menuName: menuName,
        template: data.blocks.includes("template"),
        script: data.blocks.includes("script"),
        style: data.blocks.includes("style")
      }
    },
      {
        type: "add",
        path: `src/api/${rootDir}/${dir}.js`,
        templateFile: "plop-templates/view/api.hbs",
        data: {
          name: name
        }
      },
      {
        type: "add",
        path: `src/views/${rootDir}/${dir}/data.ts`,
        templateFile: "plop-templates/view/data.hbs"
      },
      {
        type: "add",
        path: `src/views/${rootDir}/${dir}/modal-form.vue`,
        templateFile: "plop-templates/view/form.hbs",
        data:{
          template: data.blocks.includes("template"),
          script: data.blocks.includes("script"),
          style: data.blocks.includes("style")
        }
      }
    ];

    return actions;
  }
};
3、最后编写模板hbs文件(以其中的index.vue为例)
hbs 复制代码
{{#if template}}
  <template>
    <div class="container">
      <Breadcrumb :items="['{{rootMenuName}}','{{menuName}}']" />
      <a-card class="general-card" :title="$t('common.dataList')">
        <search-bar v-model:condition="searchParams" :search-items="searchItems" @search="getDataList"></search-bar>
        <div class="data-handle" style="margin-bottom: 16px">
          <a-button type="primary" @click="addItem">
            <template #icon>
              <icon-plus />
            </template>
            \{{$t('common.add')}}
          </a-button>
        </div>

        <a-table
          row-key="id"
          :loading="loading"
          :bordered="false"
          :columns="columns" :data="data">
          <template #optional="{ record }">
            <a-button type="text" @click="editItem(record)">
              <template #icon>
                <icon-edit />
              </template>
              \{{$t('common.edit')}}
            </a-button>
            <a-button type="text" status="danger" @click="deleteItem(record)">
              <template #icon>
                <icon-delete />
              </template>
              \{{$t('common.delete')}}
            </a-button>
          </template>
        </a-table>
      </a-card>

      <modalForm v-model:show="show" :form-data="form" @confirm="confirmSubmit" title="{{menuName}}" />

    </div>
  </template>
{{/if}}

{{#if script}}
<script setup lang="ts">
import { reactive, ref, getCurrentInstance } from "vue";
import {{name}}Api from "@/api/{{rootDir}}/{{dir}}";
import { cloneDeep } from "lodash";
import { Modal } from '@arco-design/web-vue';
import { useI18n } from "vue-i18n";
import modalForm from "./modal-form.vue";
import useData from "./data";

const { t } = useI18n();
const {proxy} = getCurrentInstance();
const loading = ref(false);
const show = ref(false);
const form = ref(null);
const data = ref([]);
const { searchParams, searchItems, columns } = useData();

const addItem = () => {
  show.value = true;
  form.value = null
};

const editItem = (record: any) => {
  show.value = true
  form.value = cloneDeep(record)
};

const getDataList = () => {
  loading.value = true
  {{name}}Api.{{name}}List().then((res: any) => {
    if (res.code === 200) {
      loading.value = false
      data.value = res.data || [];
    }
  });
};

const confirmSubmit = (formData: any) => {
  {{name}}Api.update{{ properCase name }}({
    op: formData.id ? 1 : 0,
    ...formData
  }).then((res: any) => {
    if (res.code === 200) {
      show.value = false;
      getDataList();
      proxy.$notification.success(t("common.updateSuccess"));
    }
  });
};

const deleteItem = (record: any) => {
  Modal.warning({
    title: t("common.deleteRecord"),
    content: t("common.deleteRecordTip"),
    okText:t("common.confirm"),
    cancelText:t("common.cancel"),
    hideCancel: false,
    onOk:() => {
      {{name}}Api.update{{ properCase name }}({
        op:2,
        id:record.id
      }).then((res: any) => {
        if (res.code === 200) {
          getDataList();
          proxy.$notification.success(t("common.deleteSuccess"));
        }
      });
    }
  });
}


getDataList();

</script>
{{/if}}

{{#if style}}
<style scoped>
.container {
  padding: 0 20px 20px 20px;
}

.data-handle button {
  margin-right: 16px;
}
</style>
{{/if}}

上面只是以view文件为例,router文件同样可以按上面的流程编写

二、编写shell文件执行plop模板命令

bash 复制代码
#!/bin/bash
echo "开始生成..."

# 返回上一级目录
#cd ..


if [[ $7 == "root" ]]; then
  # 根菜单生成路由
  ./node_modules/.bin/plop parentRouter $1 $2 $3 $4 $8
  echo '根菜单生成路由完成 >>>>'
  git add ./src/router/routes/modules/$1.ts
  git add ./src/views/$1
  echo "git add ./src/router/routes/modules/$1.ts"
  echo "git add ./src/views/$1"
else
  # 生成文件
    echo "view $5 $1 $3 $2 $7 $6 template,script,style >>>>"
    ./node_modules/.bin/plop view $5 $1 $3 $2 $7 $6 template,script,style
    echo "view 文件生成完毕"
  # 生成路由
    echo "childRouter $5 $1 $7 $3 $2 $8 >>>>"
    ./node_modules/.bin/plop childRouter $5 $1 $7 $3 $2 $8
    echo "childRouter 路由生成完毕"
    git add ./src/api/$5
    git add ./src/views/$5/$1
    echo "git add ./src/api/$5"
    echo "git add ./src/views/$5/$1"
fi

echo "代码生成完毕!"

三、编写node执行shell脚本

js 复制代码
router.post("/create/menu", async ctx => {
  const data = ctx.request.body; 

  createFileShell(data);
  const response = await axios.post('http://*.*.*.*:9527/menu/list_op',data)

  ctx.body = response.data;
  // ctx.body = "success";
});


function createFileShell(data) {
  const options = {
    encoding: "utf8", // 设置输出编码为utf8
    stdio: "inherit"// 继承stdin和stdout,stderr通过pipe捕获
  };
  let nameCode = data.name;
  let menuName = data.menu_name;
  let icon = data.menu_icon;
  let path = data.path;
  let rootDir = data.path;
  let rootName = data.menu_name;
  if (data.parent) {
    rootDir = data.parent.path;
    rootName = data.parent.menu_name;
  }
  const sort = data.sort;
  const parent = data.parent_code;
  execFile("/bin/bash", [__dirname + "/create.sh", path, menuName, nameCode, icon, rootDir, rootName, parent,sort], options, (error, stdout, stderr) => {
    console.log(`stdout: ${stdout}`);
    if (error) {
      console.error(`执行脚本出错: ${error}`);
      return;
    }

  });
}

四、创建一个菜单

创建主菜单
创建子菜单
完成

本篇文章并非全部流程,只是提供一个思路,写的不好,望轻喷

相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼3 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax