plpo vue实战版教程
vue实战版教程
- 什么是plpo
- 安装
- 1.将plop添加到您的项目
- 2.全局安装plop(可选,但建议使用方便)
- 3.在项目根目录下创建一个 plopfile.js
- vue 实战(后台管理系统 - 增删改查)
- 所需文件
- 文件介绍
- 创建配置文件 plopfile.js
- 创建模板和脚本命令文件
- 模板创建逻辑
- 根据自己的需求创建view模板文件
- view --list 文件夹
- view --detail/drawer文件夹
- view --detail/modal文件夹 同理(不做描述了)
- 运行创建项目
- 效果展示
什么是plpo
它是一个命令行工具,专门用于构建生成器,这些生成器可以帮助开发者快速生成代码模板,特别是对于大型的后台管理系统,页面很多相似的内容,重复率很高的项目,我们可以设立一个模板(列表、详情、路由等)(vue、js、css文件等’),一次构建重复创建。
安装
1.将plop添加到您的项目
npm install --save-dev plop
- ps: 如果你没有全局安装plop,你需要在设置一个 npm 脚本命令来为你运行polp:
//package.json
{...,"scripts":{"plop":"plop"},...
}
2.全局安装plop(可选,但建议使用方便)
npm install -g plop
3.在项目根目录下创建一个 plopfile.js
创建一个基本的生成器
// plop 的入口文件 plopfile.js
// 需要导出一个函数,函数接收一个plop对象,用于创建生成器任务
module.exports = plop => {// setGenerator可以设置一个生成器,每个生成器都可用于生成特定的文件// 接收两个参数,生成器的名称和配置选项plop.setGenerator('component', {// 生成器的描述description: 'create a component',// 发起命令行询问(将来生成器工作时发起的询问,它是一个数组,每个对象都是一次询问)prompts: [{// 类型type: 'input',// 接收变量的参数name: 'name',// 询问提示信息message: 'component name',// 默认值default: 'MyComponent'}],// 完成命令行后执行的操作,每个对象都是动作对象actions: [{// 动作类型type: 'add',// 生成文件的输出路径path: 'src/views/${name}/list/index.vue',// template 模板的文件路径,目录下的文件遵循hbs的语法规则templateFile: 'plopTemplate/view/list/index.vue'}]})
}
- actions 之 path
path中的{{name}}是一种变量书写形式,它对应的就是prompts对象数组中的name接收的值
假如 name='Test1',那这里的path就相当于src/views/Test1/list/index.vue
- actions 之 templateFile
templateFile 就是该文件的模板文件路径
我们在模板中同样可以使用{{变量名称}},的方式来传入我们在prompts中接收的变量
多个动作
同样的,如果我们希望生成多个文件,就可以配置多个动作对象和对应的模板即可即可,如
actions: [{type: "add",path: `src/views/${name}/list/index.vue`,templateFile: "plopTemplate/view/list/index.vue",}, {type: "add",path: `src/views/${name}/detail/index.vue`,templateFile: "plopTemplate/view/detail/index.vue",}]
vue 实战(后台管理系统 - 增删改查)
所需文件
文件介绍
在项目跟目录下,创建配置文件 plopfile.js,内容如下:
创建配置文件 plopfile.js
const viewGenerator = require("./plopTemplate/prompt");
module.exports = (plop) => {plop.setGenerator("create", viewGenerator);
};
plopfile.js 中导出一个函数,该函数接受 plop 对象作为它的第一个参数;
plop 对象公开包含 setGenerator(name, config)函数的 plop api 对象。
创建模板和脚本命令文件
在项目根目录下新建文件夹(如plopTemplates),放置模板(view/list view/detail文件)和脚本命令(prompt.js)文件
模板创建逻辑
prompt.js脚本命名如下,提示语和创建动作
1.手动输入页面名称
2.是否添加增改组件
3.选择是(选择添加抽屉or弹窗)
4.是否添加路由
module.exports = {description: "新建一个模块",prompts: [{type: "input",name: "name",message: "页面名称:",validate(name) {if (!name) {return "请输入页面名称";}return true;},},{type: "confirm",name: "hasDetail",message: "你想要给新页面添加详情组件(抽屉/弹窗)吗?",},{type: "confirm",name: "hasDrawer",message: "你想要给模块添加详情抽屉吗?",when: function (answer) {// 当hasDetail为true的时候才会到达这步return answer.hasDetail; // 只有我return true才会这个confirm},},{type: "confirm",name: "hasModal",message: "你想要给模块添加详情弹窗吗?",when: function (answer) {return !answer.hasDrawer && answer.hasDetail;},},{type: "confirm",name: "hasRoute",message: "你想要给模块增加路由吗?(在route下创建为name的路径)",},],actions: (data) => {const { hasDrawer, hasModal, hasRoute, name } = data;let listActions = [];const baseActions = [{type: "add",path: `src/views/${name}/list/index.vue`,templateFile: "plopTemplate/view/list/index.vue",},{type: "add",path: `src/views/${name}/list/index.less`,templateFile: "plopTemplate/view/list/index.less",},{type: "add",path: `src/views/${name}/list/columns.js`,templateFile: "plopTemplate/view/list/columns.js",},];listActions = listActions.concat(baseActions);const drawerActions = [{type: "add",path: `src/views/${name}/detail/drawer/index.vue`,templateFile: "plopTemplate/view/detail/drawer/index.vue",},{type: "add",path: `src/views/${name}/detail/drawer/index.less`,templateFile: "plopTemplate/view/detail/drawer/index.less",},];const modalActions = [{type: "add",path: `src/views/${name}/detail/modal/index.vue`,templateFile: "plopTemplate/view/detail/modal/index.vue",},{type: "add",path: `src/views/${name}/detail/modal/index.less`,templateFile: "plopTemplate/view/detail/modal/index.less",},];const routeAction = [{type: "add",path: `src/router/routes/${name}.js`,templateFile: "plopTemplate/route/index.js",},];if (hasDrawer) {listActions = listActions.concat(drawerActions);}if (hasModal) {listActions = listActions.concat(modalActions);}if (hasRoute) {listActions = listActions.concat(routeAction);}return listActions;},
};
根据自己的需求创建view模板文件
下面只做于逻辑参考
view --list 文件夹
- index.vue
<!-- eslint-disable -->
<template><div class="{{name}}Container"><divstyle="padding: 10px 0"class="search"><div class="search_form"><a-inputstyle="width: 200px"placeholder="请输入用户名"suffix-icon="el-icon-search"v-model="query.username"></a-input></div><div class="search_btns"><a-buttonclass="btn"type="primary"@click="getList(query)">搜索</a-button><a-buttonclass="btn"type="warning"@click="getList">重置</a-button></div></div><div class="btns">{{#if hasDetail}}<a-button@click="handleAdd"class="float-left"type="primary">新增</a-button>{{/if}}<div class="btns_right"><a-buttontype="primary"@click="handleImport">导入</a-button><a-buttontype="primary"@click="handleExport">导出</a-button></div></div><a-tablebordered:data-source="dataSource":columns="columns"><templateslot="action"slot-scope="text, record">{{#if hasDetail}}<a @click="handleDetail(record)">编辑</a>{{/if}}<astyle="color: red; margin-left: 10px"@click="handleDel(record)">删除</a></template></a-table>{{#if hasModal}}<modal-comv-model="detailVisible":detailData="detailData"></modal-com>{{/if}}{{#if hasDrawer}}<drawer-comv-model="detailVisible":detailData="detailData"></drawer-com>{{/if}}</div>
</template>
<script>
import getColumns from "./columns";
{{#if hasDrawer}}
import drawerCom from "../detail/drawer/index.vue";
{{/if}}
{{#if hasModal}}
import modalCom from "../detail/modal/index.vue";
{{/if}}
export default {name:"{{name}}",components: {{{#if hasDrawer}}drawerCom,{{/if}}{{#if hasModal}}modalCom,{{/if}}},data() {return{columns: getColumns.call(this),dataSource: [{ id:1,name: "李荣浩", age: 18, sex: "男" },{ id:2,name: "李菲儿", age: 20, sex: "女" },{ id:3,name: "李小龙", age: 26, sex: "男" },],detailVisible: false,detailData: {},query:{username:''}}},mounted() {this.getList();},methods: {// 请求数据getList(query={}){//请求逻辑},// 导入handleImport() {},// 导出handleExport() {},// 跳转添加handleAdd() {this.detailVisible = true;this.detailData={}},// 跳转详情handleDetail(record) {this.detailVisible = true;this.detailData=record},// 删除handleDel(record) {this.$confirm({title: "系统提示",content: "确定要删除该条数据吗",onOk: async () => {try {//删除逻辑} catch (e) {console.log(e);}},});},},
}
</script>
<style lang="less">
@import "./index.less";
</style>
- columns.js
const getColumns = function () {return [{title: "序号",dataIndex: "index",customRender: (text, record, index) => index + 1,fixed: "left",},{title: "姓名",dataIndex: "name",},{title: "年龄",dataIndex: "age",},{title: "性别",dataIndex: "sex",},{title: "操作",width: 120,scopedSlots: {customRender: "action",},fixed: "right",},];
};export default getColumns;
- index.less
.{{name}}Container {margin: 10px;.search {float: left;&_form {float: left;}&_btns {float: left;.ant-btn {margin-left: 10px;}}}.btns {height: 60px;display: flex;width: 100%;justify-content: space-between;position: relative;&_right {position: absolute;right: 0;}.ant-btn {margin: 10px 10px 10px 0px;}}
}
view --detail/drawer文件夹
- index.vue
<template><a-drawerwidth="500"placement="right":visible="visible"@close="handleCancel":z-index="2024":title="title"><div class="{{name}}Detail"><a-form:form="form"class="fields"><a-form-itemlabel="姓名"class="fields-item"><a-inputv-decorator="['name', { rules: [{ required: true, message: '请填写姓名!' }] }]"/></a-form-item><a-form-itemlabel="年龄"class="fields-item"><a-input v-decorator="['age', { rules: [{ required: true, message: '请填写年龄!' }] }]" /></a-form-item><a-form-itemlabel="性别"class="fields-item"><a-radio-groupv-decorator="['sex', { rules: [{ required: true, message: '请选择性别!' }] }]"><a-radio value="男">男</a-radio><a-radio value="女">女</a-radio></a-radio-group></a-form-item></a-form></div><div class="button-wrap"><a-buttontype="plain"class="mg-r_2"@click="handleCancel">取消</a-button><a-buttontype="primary":loading="loading"@click="onSave">保存</a-button></div></a-drawer>
</template><script>
export default {name: "{{name}}Detail",props: {value: {type: Boolean,default: false,},detailData: {type: Object,default: () => {},},},model: {prop: "value",event: "change",},computed: {visible: {get() {return this.value;},set(val) {this.$emit("change", val);},},title() {return this.detailData?.id ? "编辑" : "新增";},},watch: {visible(newVal) {if (newVal) {if (this.detailData.id) {const { age, sex, name } = this.detailData;this.$nextTick(() => {this.form.setFieldsValue({age,sex,name,});});} else {this.form.resetFields();}}},},data() {return {loading: false,form: this.$form.createForm(this),};},methods: {handleCancel() {this.$emit("change", false);},onSave() {this.form.validateFields(async (err, values) => {if (!err) {// 保存逻辑}});},},
};
</script><style lang="less">
@import "./index.less";
</style>
- index.less
.{{name}}Detail{position: relative;.fields {&-item {display: flex;width: 100%;.ant-form-item-control-wrapper {flex: 1;}}}}
.button-wrap {width: calc(100% - 60px);display: flex;justify-content: center;align-items: center;position: absolute;bottom: 20px;
}
view --detail/modal文件夹 同理(不做描述了)
运行创建项目
pnpm run plop view
效果展示
- 抽屉详情
- 弹窗详情
- 无详情