node实现自动生成vue页面,更新router

背景:最近负责将页面迁移到新系统,每次在新系统新增页面都需要做一系列重复操作:

  1. views下新建页面文件夹及index页面
  2. 更新router.js
  3. api文件夹下新增对应页面的js文件

管理系统的页面大差不差,基本围绕增删改查,加上系统有些需要注意的点,于是整理了个模板文件,减少工作量。

本来vscode也可以设置模板文件的,但是由于2和3,为了简化操作,使用node写了个脚本一次性完成

系统页面结构

如果不符合此页面结构的需要自行修改方法

  • src
    • xx(新页面)
      • component
        • add-dia.vue(新增功能封装的弹窗)
      • index.vue(主页面)
  • api
    • xx.js(新页面对应的api)
  • setting
    • router.js

router.js结构

js 复制代码
...
const router = {
mode: "hash",
  routerHook: router => {
    router.beforeEach((to, from, next) => {
      next();
    });
  },
  routes: [
    {
        path: "/xxx",
        name: "yyy",
        meta: {
          title: "zzz",
          constant: true,
          icon: "menu:jccs"
        },
        children: [
            {
              path: "/www",
              name: "www",
              component: () => import("views/mmm/www/index.vue"),
              meta: {
                title: "ppp",
                constant: true
              }
            },
        ]
        }

}

具体实现

创建文件夹、读写文件

js 复制代码
// 写入文件
const writeF = async (path, content) => {
  try {
    await fs.writeFile(path, content);
    console.log(`${path} 写入成功`);
  } catch (err) {
    console.error(`${path} 写入失败: ${err.message}`);
  }
};

// 读取文件
const readF = async (path) => {
  try {
    const data = await fs.readFile(path, "utf8");
    return data;
  } catch (err) {
    console.error(`${path} 读取失败: ${err.message}`);
    return null;
  }
};

// 创建文件夹并写入文件
const mkdirWithWriteF = async (path, needCom, fn) => {
  try {
    await fs.mkdir(path + (needCom ? compPath : ""), { recursive: true });
    console.log(`${path} 创建成功`);
    fn && fn();
  } catch (err) {
    console.error(`${path} 创建失败: ${err.message}`);
  }
};

模板变量替换

js 复制代码
// 变量替换
const replaceStr = (str, config) => {
  str = str.replace(/%selfMoudle%/g, config.selfMoudle); // 页面名
  return str;
};

模板页面变量示范:

创建index.vue

js 复制代码
const writePageTemp = async (paths, config) => {
  const templatePath = config.needEmpty ? emptyTempPath : defaultTempPath;
  const templateContent = await readF(templatePath);
  if (templateContent) {
    const str = replaceStr(templateContent, config);
    await writeF(paths + defaultVuePath, str);
  }
};

创建api.js

js 复制代码
const writeApiTemp = async (config) => {
  const paths = path.resolve(root + `/src/api/${config.selfMoudle}.js`);
  // if (!examFileExist(paths)) return;
  const apiContent = await readF(apiTempPath);
  if (apiContent) {
    await writeF(paths, apiContent);
  }
};

修改router.js

先找到要新增页面对应的目录,再写入值

只支持一级目录下新增路由,如果有二级目录需要自行修改

js 复制代码
const setAst = (config) => {
  return t.objectExpression([
    t.objectProperty(t.identifier("path"), t.stringLiteral("/" + config.selfMoudle)),
    t.objectProperty(t.identifier("name"), t.stringLiteral(config.selfMoudle)),
    t.objectProperty(
      t.identifier("component"),
      t.arrowFunctionExpression(
        [], // 无参数
        t.callExpression(t.import(), [t.stringLiteral(`views/${config.parentDir}/${config.selfMoudle}/index.vue`)])
      )
    ),
    t.objectProperty(
      t.identifier("meta"),
      t.objectExpression([
        t.objectProperty(t.identifier("title"), {
          type: "StringLiteral",
          value: config.title,
          extra: { raw: `"${config.title}"`, rawValue: config.title }, // 手动设置 raw 值
        }),
        t.objectProperty(t.identifier("constant"), t.booleanLiteral(true)),
      ])
    ),
  ]);
};

const updateRouterTemp = async (config) => {
  const filePath = path.resolve(root + `/src/setting/router.js`);
  console.log("updateRouterTemp", filePath);
  const fileContent = await readF(filePath);
  if (!fileContent) return;
  // 解析文件内容为 AST
  const ast = parser.parse(fileContent, {
    sourceType: "module",
  });

  // 遍历 AST,找到 router 变量
  traverse(ast, {
    VariableDeclarator(path) {
      if (path.node.id.name === "router") {
        const routerValue = path.node.init;
        const curRes = routerValue.properties.find((prop) => prop.key.name == "routes");
        curRes.value.elements.forEach((element) => {
          if (
            t.isObjectExpression(element) &&
            element.properties.some(
              (prop) => t.isObjectProperty(prop) && prop.key.name === "name" && prop.value.value === config.parentMoudle
            )
          ) {
            const childrenProp = element.properties.find(
              (prop) => t.isObjectProperty(prop) && prop.key.name === "children"
            );

            if (childrenProp && t.isArrayExpression(childrenProp.value)) {
              childrenProp.value.elements.push(setAst(config));
            } else {
              console.log("这个模块不存在children属性,检查是否出错");
            }
          }
        });
      }
    },
  });

  // 生成新的代码
  const newCode = generator(ast, {}, fileContent).code;

  // 写入文件
  await writeF(filePath, newCode);
};

主方法

js 复制代码
const addTemplate = async (params) => {
  console.log("传递的参数", params);
  if (params.length < 4) {
    console.log("传递母模块名,母文件夹名,自身模块名,页面名,是否需要空页面模板");
    return;
  }
  // 读取传递的变量
  const config = {
    parentMoudle: params[0],
    parentDir: params[1],
    // 驼峰改横线命名
    selfMoudle: params[2].replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
    title: params[3],
    needEmpty: params[4] || false,
  };
  console.log("config", config);
  const paths = path.resolve(root + `/src/views/${config.parentDir}/${config.selfMoudle}`);
  console.log("paths is:" + paths);
  // TODO: existsSync方法报错不存在,等解决后会更新
  // if (!examFileExist(paths)) {
  //   console.log("不存在同名父模块文件夹,现在新建一个");
  //   await mkdirWithWriteF(paths, false);
  // }

  console.log("开始生成模板页面");
  console.log("要添加的文件夹路径:" + paths);

  await mkdirWithWriteF(paths, true, async () => {
    const addDiaContent = await readF(addDiaTempPath); // 添加add-dia
    if (addDiaContent) {
      const str = replaceStr(addDiaContent, config);
      await writeF(path.join(paths, compPath, addDiaPath), str);
    }
    await writePageTemp(paths, config); // 添加index.vue
  });
  await writeApiTemp(config); // 添加api.js
  await updateRouterTemp(config); // 更新router.js

  console.log("生成结束,检查一下");
};

addTemplate(process.argv.slice(2));

运行

在package.json中配置命令

运行示例

将脚本文件夹放在项目中,运行: npm run cpage basic-params base-data agent-api-log API发送日志

相关推荐
贩卖纯净水.2 小时前
webpack的安装及其后序部分
前端·webpack·node.js
又双叒叕7781 天前
如何发布npm包?
前端·npm·node.js
FreeBuf_1 天前
恶意npm与VS Code包窃取数据及加密货币资产
前端·npm·node.js
天天打码1 天前
npm/yarn/pnpm安装时Sharp模块报错解决方法
前端·npm·node.js
好奇的菜鸟2 天前
如何使用 WebStorm 编写第一个 Node.js 项目
ide·node.js·webstorm
Bullet3432 天前
包管理工具npm与pnpm
前端·笔记·学习·npm·node.js
巴巴_羊2 天前
webpack CDN打包优化
前端·webpack·node.js
eggcode2 天前
npm run build后将打包文件夹生成zip压缩包
前端·npm·node.js
叶九灵不灵3 天前
node入门:安装和npm使用
前端·npm·node.js
好奇的菜鸟3 天前
Node.js全局函数详解:定时器与即时回调
node.js