200多行代码,实现前端自动化工具(一行命令帮你创建后台管理系统的一个模块功能)

本文已参与「新人创作礼」活动,一起开启掘金创作之路

在读了element初始化组件源码的功能之后,就有了现在的想法,因为我现在的项目主要在做后台管理系统,后台管理系统每个菜单的首页都有两部分组成,搜索区域表格区域 还有对应的api文件 store文件 ,如果使用了ts 可能还会又对应的type文件,每次新增这些文件都会很繁琐,浪费很多时间,新增完文件之后可能还要去复制其他页面的代码粘贴到刚新增的文件中,此插件就是为了解决这个问题的。

1.在终端输入命令

node build/new.js ceshi  

2.自动在你设置的src目录下增加文件夹

image.png

(1)index.vue

image.png

(2)components/table.vue

image.png

(3)components/condition.vue

image.png

3.自动增加api文件

image.png

4.自动增加store文件

store/index.ts image.png store/ceshi/Ceshi.ts image.png

5.项目源码

const path = require('path');
const fs = require('fs'); // 文件模块
const fileSave = require('file-save'); // 保存文件插件
const uppercamelcase = require('uppercamelcase'); // 驼峰命名工具
// 检测进程退出
process.on('exit', () => {
    console.log('退出进程');
});
//必须增加文件夹名称
if (!process.argv[2]) {
    console.error('[文件夹名]必填 - Please enter new files name');
    process.exit(1); // 默认参数0表示进程执行成功,
}
const fileName = process.argv[2] //获取文件名称
const filePath = path.resolve(__dirname, '../src', fileName)
const fileApiPath = path.resolve(__dirname, '../src/api', fileName)
const fileStorePath = path.resolve(__dirname, '../src/store/modules', fileName)
//获取src下的所有目录
const files = fs.readdirSync(path.resolve(__dirname, '../src'))
let readFilesName = []
files.forEach(r => {
    //排除单文件
    if (r.indexOf('.') == -1) {
        readFilesName.push(r)
    }
});
//判断src目录下是否已经包含即将姚创建的目录名称
if (readFilesName.find(r => r === fileName)) {
    console.error(`[${fileName}]文件夹名已存在`);
    process.exit(1);
}
//文件名字转大写
const fileCaseName = uppercamelcase(fileName)
//模板文件内容
const Files = [
    {
        fileName: 'index.vue',
        type: 'view',
        content: `
 <!--${fileName}首页-->
 <template>
     <div class="app-container">
        <Condition @updateCondition="search" />
        <p></p>
        <Table :data="tableData"  @getList="getList" />
        <el-row :gutter="20">
            <el-col :span="24">
               <Pagination @pagination="getList" v-show="tableData.total > 0" :total="tableData.total"
                    v-model:page="data.queryParams.page" v-model:limit="data.queryParams.limit" />
            </el-col>
        </el-row>
    </div>
</template>
<script setup name="" lang="ts">
    import { ref, reactive, computed, getCurrentInstance } from 'vue';
    import { useStore } from 'vuex';
    import Condition from './components/condition.vue'; //搜索条件
    import Table from './components/table.vue'; //表格
    import Pagination from "src/components/Pagination/index.vue"; //分页
    import { xx } from 'src/api/${fileName}/${uppercamelcase(fileName)}Api';
    const store = useStore()
    const { proxy } = getCurrentInstance()
    const data = reactive({
        queryParams: {
            page: 1,
            limit: 10,
        },
    })
    //从vuex读取列表数据
    const tableData = computed(() => store.state)
    //获取table列表数据
    function getList(){}
    //搜索方法
    function search(params){}
</script>

<style lang="scss" scoped></style>`
    },
    {
        type: 'view',
        fileName: 'components/condition.vue',
        content: `<template>
    <el-form ref="formRef" :model="queryParams" style="width: 100%;" label-width="100px">
        <el-row :gutter="10">
            <el-col :span="8"> 
                <el-form-item prop="keyword" label="关键字搜索">
                    <el-input v-model="queryParams.keyword" style="width: 100%;" placeholder="请输入关键字搜索">
                    </el-input>
                </el-form-item>
            </el-col>
        </el-row>
    </el-form>
    <el-row :gutter="10" class="mb8">
    <el-col :span="1.5">
        <el-button type="success" @click="search(formRef)">搜索</el-button>
        <el-button type="info" plain @click="reset(formRef)">重置</el-button>
    </el-col>
</el-row>    
</template>
        
 <script setup name="" lang="ts">
    import { computed, reactive, ref } from "vue";
    import { useStore } from 'vuex'
    import { useRouter } from "vue-router";
    const formRef = ref();   
    const emit = defineEmits(["updateCondition"]);
    const store = useStore();
    const router = useRouter();
    const queryParams = reactive({
        keyword: '',
    })
    //搜索
    function search() {
        emit("updateCondition", queryParams);
    }
    //重置搜索
    function reset(formEl) {
        formEl.resetFields();
        emit("updateCondition", queryParams);
    }
 </script>

 <style lang="scss" scoped></style>`
    },
    {
        type: 'view',
        fileName: 'components/table.vue',
        content: `<template>
    <el-table :data="props.data.records" row-key="id" highlight-current-row>
        <el-table-column prop="id" align="center" :width="90" label="Id" sortable  show-overflow-tooltip />
    </el-table>    
</template>
        
<script setup name="" lang="ts">
    import { ref, onMounted, computed, reactive, getCurrentInstance } from 'vue'
    const { proxy } = getCurrentInstance()
    const props = defineProps({
        data: {
            type: Object,
            default: () => { },
        },
    });
</script>

<style lang="scss" scoped></style> `
    },
    // 创建api
    {
        fileName: `${uppercamelcase(fileName)}Api.ts`,
        type: 'api',
        content: `
import { defHttp } from "@/http/request"; //自己封装的api请求 
export function getMarkeingList(params) {
    return defHttp.get({
      url: "xxxxx",
      params,
    });
  }
  export function getAddProductList(data) {
    return defHttp.post({
      url: "xxxx",
      data,
    });
  } `},
    //创建store
    {
        type: 'store',
        fileName: `${uppercamelcase(fileName)}.ts`,
        content: `import { Module } from "vuex";
import { RootState } from "../..";
import { xxxx } from "@/api/${fileName}/${fileCaseName}Api.ts";
interface List {
    total: number;
    records: any[];
    limit: number;
}
export interface State {
    list: List;
}
const category: Module<State, RootState> = {
    namespaced: true,
    state: {
        list: {
            total: 0,
            records: [],
            limit: 10,
          }, 
    }
    mutations: {
        SET_lIST: (state, payLoad: List) => {
            state.list.total = payLoad.total;
            state.list.records = payLoad.records;
            state.list.limit = payLoad.limit;
          },
    },
    actions: {
        async get_list({ commit, state }, payLoad) {
            if (!payLoad) {
              payLoad = {
                limit: state.list.limit,
                page: 1,
              };
            }
            const result = await xxx(payLoad);
            commit("SET_lIST", {
              records: result.data.records,
              total: result.data.total,
              limit: result.data.size,
            });
          },
    }

}
export default category;`}
]
//处理store index.ts文件
const elementTsPath = path.join(__dirname, '../src/store/index.ts');
let elementTsText = `${fs.readFileSync(elementTsPath)} `
const index = elementTsText.indexOf('const') - 1; // 找到 const 在字符串中首次出现的位置
const importString = `import ${fileCaseName} from './modules/${fileName}/${fileCaseName}'`;
elementTsText = elementTsText.slice(0, index) + importString + '\n' + elementTsText.slice(index);
const objectIndex = elementTsText.indexOf('{') + 1
elementTsText = elementTsText.slice(0, objectIndex) + '\n' + fileCaseName + ',' + elementTsText.slice(objectIndex);
fileSave(elementTsPath)
    .write(elementTsText, 'utf8')
    .end('\n');
//开始生成模板
Files.forEach(file => {
    switch (file.type) {
        case 'api':
            fileSave(path.join(fileApiPath, file.fileName))
                .write(file.content, 'utf8')
                .end('\n');
            break;
        case 'store':
            fileSave(path.join(fileStorePath, file.fileName))
                .write(file.content, 'utf8')
                .end('\n');
            break;
        default:
            fileSave(path.join(filePath, file.fileName))
                .write(file.content, 'utf8')
                .end('\n');
            break;
    }
});
console.log('创建完成!');

做这个功能只是为了减少我工作中的复杂操作,大家也可以借鉴我这个方法来简化你们的流程,当然代码中可能还有些地方有问题,大家可以复制下来自己去修改变成自己想要的,我这只是提供一个思路,大家有更好的工具的话欢迎留言!!!

猜你喜欢

转载自juejin.im/post/7114490629165416484