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">
    <div
      style="padding: 10px 0"
      class="search"
    >
      <div class="search_form">
        <a-input
          style="width: 200px"
          placeholder="请输入用户名"
          suffix-icon="el-icon-search"
          v-model="query.username"
        ></a-input>
      </div>
      <div class="search_btns">
        <a-button
          class="btn"
          type="primary"
          @click="getList(query)"
          >搜索</a-button
        >
        <a-button
          class="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-button
          type="primary"
          @click="handleImport"
          >导入</a-button
        >
        <a-button
          type="primary"
          @click="handleExport"
          >导出</a-button
        >
      </div>
    </div>
    <a-table
      bordered
      :data-source="dataSource"
      :columns="columns"
    >
      <template
        slot="action"
        slot-scope="text, record"
      >
        {{#if hasDetail}}
        <a @click="handleDetail(record)">编辑</a>
        {{/if}}
        <a
          style="color: red; margin-left: 10px"
          @click="handleDel(record)"
          >删除</a
        >
      </template>
    </a-table>
    {{#if hasModal}}
    <modal-com
      v-model="detailVisible"
      :detailData="detailData"
    ></modal-com>
    {{/if}}
    {{#if hasDrawer}}
    <drawer-com
      v-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-drawer
    width="500"
    placement="right"
    :visible="visible"
    @close="handleCancel"
    :z-index="2024"
    :title="title"
  >
    <div class="{{name}}Detail">
      <a-form
        :form="form"
        class="fields"
      >
        <a-form-item
          label="姓名"
          class="fields-item"
        >
          <a-input
            v-decorator="['name', { rules: [{ required: true, message: '请填写姓名!' }] }]"
          />
        </a-form-item>
        <a-form-item
          label="年龄"
          class="fields-item"
        >
          <a-input v-decorator="['age', { rules: [{ required: true, message: '请填写年龄!' }] }]" />
        </a-form-item>
        <a-form-item
          label="性别"
          class="fields-item"
        >
          <a-radio-group
            v-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-button
        type="plain"
        class="mg-r_2"
        @click="handleCancel"
        >取消</a-button
      >
      <a-button
        type="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

在这里插入图片描述

效果展示

在这里插入图片描述

  • 抽屉详情
    在这里插入图片描述

在这里插入图片描述

  • 弹窗详情
    在这里插入图片描述

在这里插入图片描述

  • 无详情
    在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/889572.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ELK中L的filebeat配置及使用(超详细)

上一次讲解了如何在linux服务器上使用docker配置ELK中的E和K&#xff0c;这期着重讲解一下L怎么配置。 首先L在elk中指的是一个数据处理管道&#xff0c;可以从多种来源收集数据&#xff0c;进行处理和转换&#xff0c;然后将数据发送到 Elasticsearch。L的全称就是&#xff1…

dbeaver_导入到数据库中的csv文件显示黑色方块

dbeaver_导入到数据库中的csv文件显示黑色方块 ** 解决方案&#xff1a; ** 数据库导出到CSV用Excel打开中文乱码问题

H3C OSPF 综合排错实验

H3C OSPF 综合排错实验 实验拓扑 ​​ 实验需求 按照图示配置 IP 地址&#xff0c;所有路由器配置环回口 IP 地址为 X.X.X.X/32​ 作为 Router-id&#xff0c;X 为设备编号&#xff08;R7 除外&#xff09;按照图示分区域配置 OSPFR1 和 R6 上分别配置环回口来模拟业务网段…

【华为】基于华为交换机的VLAN配置与不同VLAN间通信实现

划分VLAN&#xff08;虚拟局域网&#xff09;主要作用&#xff1a; 一、提高网络安全性 广播域隔离访问控制增强 二、优化网络性能 减少网络拥塞提高网络可管理性 sysytem-view #进入系统视图配置参数 vlan batch 10 20 #批量创建vlanLSW3: int g0/0/1 port …

MYSQL 事物隔离级别的区别与现象

事物的ACID属性本章不再赘述&#xff0c;本章主要描述事物的隔离级别及隔离级别导致的现象&#xff0c;日常工作中该如何选择MYSQL的隔离级别。 MYSQL事物的隔离级别及各隔离级别存在的问题如下&#xff1a; 隔离级别/问题脏读不可重复读幻读读未提交(Read-Uncommitted)✅✅✅…

PostgreSQL学习笔记四:GUI管理工具

PostgreSQL 是一款广泛使用的开源关系数据库管理系统&#xff0c;拥有许多图形用户界面&#xff08;GUI&#xff09;工具来帮助用户更高效地管理数据库。以下是一些流行的 PostgreSQL 管理工具&#xff1a; pgAdmin&#xff1a; 一个流行的开源 PostgreSQL GUI 工具&#xff0c…

半周期检查-下降沿发上升沿采

1. 半周期路径Half Cycle Path 如果在设计中&#xff0c;同时存在上升沿触发的D触发器&#xff0c;以及下降沿触发的D触发器&#xff0c;则视为半周期路径&#xff0c;Half Cycle Path。 2. Setup time检查 时序图 时序报告&#xff1a; 起始点 startpoint&#xff1a;UFF5&a…

Jenkins+kubernetes流水线构建java项目

在传统的业务环境中&#xff0c;我们的应用部署或者更新都是采用手工的方式&#xff0c;但是在企业内部&#xff0c;应用架构一般都采用微服务&#xff0c;大部分项目都会对应几十个、上百甚至上千个微服务&#xff0c;并且还不仅仅只有一个项目&#xff0c;所以采用收工方式上…

微信小程序开发-配置文件详解

文章目录 一&#xff0c;小程序创建的配置文件介绍二&#xff0c;配置文件-全局配置-pages 配置作用&#xff1a;注意事项&#xff1a;示例&#xff1a; 三&#xff0c;配置文件-全局配置-window 配置示例&#xff1a; 四&#xff0c;配置文件-全局配置-tabbar 配置核心作用&am…

【Linux】————进程控制

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux专栏 创作时间 &#xff1a;2024年10月10日 ​ ​ 一、程序地址空间&#xff1a; 1、C/C中的程序地址空间&#xff1a; ​ 在c中我们了解了这样的空间分布图。 我们应如何去创建和访问变量呢&#xff1f;…

VR线上展厅:超越时空的沉浸式展览,打造个性化、高效展览新模式

一、沉浸式漫游&#xff1a;打破时空限制&#xff0c;尽享虚拟之旅 VR线上展厅通过高度逼真的3D场景构建&#xff0c;为参观者营造了一个仿佛置身其中的虚拟世界。借助各种显示设备&#xff0c;用户能够自由穿梭于各个展区之间&#xff0c;无论是漫步在历史悠久的博物馆&#…

双语大脑的神经可塑性能力:来自健康和病理个体的见解

摘要 双语经验的神经印记对于理解大脑如何处理优势语言和非优势语言至关重要&#xff0c;但关于它的研究仍然没有定论。不同的研究表明神经处理存在相似性或差异性&#xff0c;这对患有脑肿瘤的双语患者具有重要意义。保留术后的双语功能需要考虑到术前的神经可塑性变化。在这…

【论文阅读笔记】End-to-End Object Detection with Transformers

代码地址&#xff1a;https://github.com/facebookresearch/detr 论文小结 本文是Transformer结构应用于目标检测&#xff08;OD&#xff09;任务的开山之作。方法名DETE&#xff0c;取自Detection Transformer。   作为2020年的论文&#xff0c;其表现精度在当时也不算高的…

pytorch导入数据集

1、概念&#xff1a; Dataset&#xff1a;一种数据结构&#xff0c;存储数据及其标签 Dataloader&#xff1a;一种工具&#xff0c;可以将Dataset里的数据分批、打乱、批量加载并进行迭代等 &#xff08;方便模型训练和验证&#xff09; Dataset就像一个大书架&#xff0c;存…

mongodb GUI工具(NoSQLBooster)

介绍 跨平台的MongoDB GUI工具&#xff0c;支持Windows、macOS和Linux。自带服务器监控工具、Visual Explain Plan、查询构建器、SQL查询等功能。提供免费版本&#xff0c;但功能相比付费版本有所限制。 免费版可供个人/商业使用&#xff0c;但功能有限。 安装成功后&#x…

大疆M2006+C610 pid参数调优

官方给的示例代码里给的是1.5, 0.1, 0 但试了下空转时显然不太行. 自己摸索出0.8, 0.03, 0 表现如图中的蓝色线 期望速度先两秒的1000,然后一秒的2000,一秒的3000, 0 2000 表现还不错,可以看到0.5秒后与期望值的差距控制在大概10%之内,但还是感觉有些过调 对了先说一下基础知识…

有什么方法可以保护ppt文件不被随意修改呢?

在工作或学习中&#xff0c;我们常常需要制作powerpoint演示文稿&#xff0c;担心自己不小心改动了或者不想他人随意更改&#xff0c;我们可以如何保护PPT呢&#xff1f;下面小编就来分享两个常用的方法。 方法一&#xff1a;为PPT设置打开密码 为PPT设置打开密码是最直接有效…

活动预告|博睿数据将受邀出席GOPS全球运维大会上海站!

第二十四届 GOPS 全球运维大会暨研运数智化技术峰会上海站将于2024年10月18日-19日在上海中庚聚龙酒店召开。大会将为期2天&#xff0c;侧重大模型、DevOps、SRE、AIOps、BizDevOps、云原生及安全等热门技术领域。特设了如大模型 运维/研发测试、银行/证券数字化转型、平台工程…

九寨沟,智慧旅游新名片

九寨沟属于自然类景区&#xff0c;以优美的自然风光取胜&#xff0c;景区文化内涵相对缺失。智慧化和文旅融合是智慧文旅景区的两个必备条件&#xff0c;九寨沟在智慧文旅景区建设过程中&#xff0c;经历了两个阶段&#xff0c;先是从传统景区迈向智慧景区&#xff0c;然后是充…

设计模式:单例

一.什么是单例模式 单例模式是一种设计模式&#xff0c;指在整个程序生命周期中有且仅有一个实例的类。可以分为懒汉式以及饿汉式。 懒汉式&#xff1a;只有在类的实例被使用时才生成唯一实例。但是存在线程安全以及内存泄露的问题。可以节省系统资源。 饿汉式&#xff1a;程序…