1、vue-cli新建一个vue3项目
vue-cli需要3.0版本,可以使用vue -V查看版本,node需要16版本及以上(node:v16.20.2,npm:8.19.4
,vue-cli: 5.0.8)
新建一个名为test3-vue的vue3的项目
vue create test1-vue3
新建成功后进入项目文件夹
cd test1-vue3
2、安装plop
根据第一步新建项目的时候选的yarn或者npm,来使用相应的指令安装(版本:^4.0.0)
// yarn
yarn add plop --dev
// npm
npm install plop
3、安装其他插件(inquirer-file-path、read-metadata)
// inquirer-file-path插件可以使用指令选择文件名称(版本:^1.0.1)
npm install --save inquirer-file-path
// read-metadata根据地址获取json文件的内容(版本:^1.0.0)
npm install read-metadata
vue2项目可以使用inquirer-parse-json-file插件替代这两个插件
这两个插件的目的是在指令中选择配置文件并把配置文件中的内容与命令行的内容合并保存在配置项中,配置文件:plop_template/config,主要是存放每个页面不同的数据内容,比如列表页中的查询参数、table中的显示项、接口名称等信息。
4、在package.json中配置启动plop指令
在scripts中加入:“plop”: “plop”
5、新建一个plop_template文件夹
目录:
config:json文件,每个页面具体数据的配置文件
default.json内容:
{
"model":[{
"prop":"title",
"label":"标题"
},{
"prop":"sub_title",
"label":"子标题"
},{
"prop":"type_id",
"label":"类型"
},{
"prop":"enabled",
"label":"启用",
"type":"Boolean"
},{
"prop":"remark",
"label":"备注",
"type":"textarea",
"mode":["form"],
"placeholder":"占位内容",
"default":"这是备注"
}],
"api":{
"list": "/2685-11",
"view": "/2685-21",
"add": "/2685-2",
"edit": "/2685-3",
"del": "/2685-3"
},
"query":[{
"prop":"title",
"placehold":"搜索占位"
}]
}
script:一些模板中需要用到的助手代码方法
module.exports = function (plop) {
plop.setHelper('ifcode', function (listitem, opts) {
if (!listitem.mode || listitem.mode.find(item => item === 'table')) { // 是否显示
if (listitem.isview) { // 有详情页点击链接列
return opts.fn(this)
} else {
return opts.inverse(this)
}
} else {
return ''
}
})
plop.setHelper('difcode', function (val, opts) {
if (!val || val.find(item => item === 'form')) {
return opts.fn(this)
} else {
return opts.inverse(this)
}
})
plop.setHelper('modetype', function (val, opts) {
let result = ''
switch (val.type) {
case 'select':
result = '<el-select v-model="listdetail.' + val.prop + '" :disabled="editdisable" placeholder="' + val.placeholder + '" class="w-full mr-3">' +
'<el-option label="选项" value="1" /></el-select>'
break
case 'textarea':
result = '<el-input v-model="listdetail.' + val.prop + '" :disabled="editdisable" type="textarea" placeholder="' + val.placeholder + '" />'
break
case 'number':
result = '<el-input v-model="listdetail.' + val.prop + '" :disabled="editdisable" type="number" placeholder="' + val.placeholder + '" />'
break
default:
result = '<el-input v-model="listdetail.' + val.prop + '" :disabled="editdisable" placeholder="' + val.placeholder + '" />'
break
}
return result
})
plop.setHelper('eachIndex', function (context, options) {
let ret = ''
for (let i = 0, j = context.length; i < j; i++) {
ret = ret + options.fn({ it: context[i], isLast: i == context.length - 1 })
}
return ret
})
plop.setHelper('rulesIndex', function (context, options) {
let ret = ''
const newliat = []
for (let i = 0, j = context.length; i < j; i++) {
if (context[i].isrequired) {
newliat.push(context[i])
}
}
for (let ii = 0, j = newliat.length; ii < j; ii++) {
ret = ret + options.fn({ it: newliat[ii], isLast: ii == newliat.length - 1 })
}
return ret
})
}
template:生成页面所需的模板 ,detail.hbs:详情页面,list.hbs列表页面
detail.hbs页面内容:
<template>
<div>
<el-drawer
:title="!listdetail?'添加数据':'修改数据'"
:modal="false"
size="30%"
custom-class="absolute"
:modal-append-to-body="false"
:append-to-body="false"
:wrapper-closable="false"
:visible.sync="drawershow"
direction="rtl"
:before-close="handleClose"
>
<div class="pl-5 pr-5">
<el-form ref="form" label-position="right" label-width="80px" :model="form" :rules="rules">
{{#each config.model}}
{{#difcode mode}}
<el-form-item label="{{label}}"{{#if isrequired}} prop="{{prop}}"{{/if}}>
{{#modetype this}}{{/modetype}}
</el-form-item>
{{/difcode}}
{{/each}}
<el-form-item class="text-center">
<el-button v-if="editdisable" type="primary" @click="formdisable = false">
<span>修改</span>
</el-button>
<el-button v-if="!editdisable" type="primary" @click="drawersubmit('form')">
<span>\{{ !listdetail?'确认添加':'确认修改' }}</span>
</el-button>
<el-button @click="handleClose">
取消
</el-button>
</el-form-item>
</el-form>
</div>
</el-drawer>
</div>
</template>
<script>
import { clone } from 'lodash'
export default {
props: {
listdetail: {
type: [Object, Boolean]
},
drawershow: {
type: Boolean,
default: false
}
},
data () {
return {
form: {},
formdisable: true,
rules: {
{{#rulesIndex config.model}}
{{#if it.isrequired}}
{{it.prop}}: [
{ required: true, trigger: 'blur', message: '请输入{{it.label}}' }
]{{#if isLast}}{{else}},{{/if}}
{{/if}}
{{/rulesIndex}}
}
}
},
computed: {
editdisable: {
get () {
return this.listdetail && this.formdisable
},
set (val) {}
}
},
watch: {
listdetail: {
handler (cval, oval) { // 监听传入的数据变化(type没变的时候如果改变传参没有办法重新计算editdisable)
this.initForm(cval)
if (oval._id !== cval._id) { // 如果数据改变需要重新设置editdisable为true,输入框修改为不可输入状态
this.formdisable = true
}
if (this.$refs.form) {
this.$refs.form.resetFields()
}
}
},
drawershow: { // 可能parent有变化,需要做initForm
handler (cval, oval) {
if (!cval) {
this.initForm(false)
}
}
}
},
methods: {
initForm (obj) {
if (this.$refs.form) {
this.$refs.form.resetFields()
}
this.editdisable = !!obj
if (obj) {
console.log(obj)
this.form = clone(this.listdetail)
} else {
this.form = {}
}
},
drawersubmit (formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$emit('submit', this.form)
}
})
},
handleClose () {
this.$emit('close')
}
}
}
</script>
<style scoped>
</style>
5、配置plopfile.js文件
const helpler = require('./plop_template/script/helper') // 引入助手文件
const inquirerDirectory = require('inquirer-file-path') // 选择文件
const read = require('read-metadata'); // 从json中读取json数据
module.exports = function (plop) {
helpler(plop)
plop.setPrompt('directory', inquirerDirectory)
plop.setGenerator('page', { // 这里的 test 是一个自己设定的名字,在执行命令行的时候会用到
description: '分页增删查改', // 这里是对这个plop的功能描述
prompts: [
{
type: 'confirm', // 问题的类型
name: 'haspagelist', // 问题对应得到答案的变量名,可以在actions中使用该变量
message: '是否需要分页:', // 在命令行中的问题
default: true // 问题的默认答案
},
{
type: 'input', // 问题的类型
name: 'filePath', // 问题对应得到答案的变量名,可以在actions中使用该变量
message: '在page目录下生成文件:', // 在命令行中的问题
suffix: 'pages/',
default: 'index.vue' // 问题的默认答案
},
{
type: 'confirm', // 问题的类型
name: 'hasDetail', // 问题对应得到答案的变量名,可以在actions中使用该变量
message: '是否需要详情抽屉:', // 在命令行中的问题
default: true // 问题的默认答案
},
{
type: 'directory', // 问题的类型
name: 'config', // 问题对应得到答案的变量名,可以在actions中使用该变量
message: '选择配置文件', // 在命令行中的问题
basePath: 'plop_template/config',
default: {}, // 问题的默认答案
}
],
actions: (data) => {
read(`plop_template/config/${data.config}`, function(err, res){
// 因为inquirer-parse-json-file插件不兼容,没有找到合适的插件,所以只好获取文件名称,再手动去获取文件内容
data.config = res
});
const path = `pages/${data.filePath}`
const folder = path.substring(0, path.lastIndexOf('/'))
const actions = [
{
type: 'add', // 操作类型,这里是添加文件
path, // 模板生成的路径
templateFile: 'plop_template/template/page/list.hbs', // 模板的路径
data
}
]
if (data.hasDetail) {
actions.push({
type: 'add', // 操作类型,这里是添加文件
path: `${folder}/-detail.vue`, // 模板生成的路径
templateFile: 'plop_template/template/page/-detail.hbs', // 模板的路径
data
})
}
return actions
}
})
}
6、运行plop
npm run plop
根据提示问题进行选择
最终生成的文件目录
生成的detail.vue的文件内容
<template>
<div>
<el-drawer
:title="!listdetail?'添加数据':'修改数据'"
:modal="false"
size="30%"
custom-class="absolute"
:modal-append-to-body="false"
:append-to-body="false"
:wrapper-closable="false"
:visible.sync="drawershow"
direction="rtl"
:before-close="handleClose"
>
<div class="pl-5 pr-5">
<el-form ref="form" label-position="right" label-width="80px" :model="form" :rules="rules">
<el-form-item label="标题">
<el-input v-model="listdetail.title" :disabled="editdisable" placeholder="undefined" />
</el-form-item>
<el-form-item label="子标题">
<el-input v-model="listdetail.sub_title" :disabled="editdisable" placeholder="undefined" />
</el-form-item>
<el-form-item label="类型">
<el-input v-model="listdetail.type_id" :disabled="editdisable" placeholder="undefined" />
</el-form-item>
<el-form-item label="启用">
<el-input v-model="listdetail.enabled" :disabled="editdisable" placeholder="undefined" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="listdetail.remark" :disabled="editdisable" type="textarea" placeholder="占位内容" />
</el-form-item>
<el-form-item class="text-center">
<el-button v-if="editdisable" type="primary" @click="formdisable = false">
<span>修改</span>
</el-button>
<el-button v-if="!editdisable" type="primary" @click="drawersubmit('form')">
<span>{{ !listdetail?'确认添加':'确认修改' }}</span>
</el-button>
<el-button @click="handleClose">
取消
</el-button>
</el-form-item>
</el-form>
</div>
</el-drawer>
</div>
</template>
<script>
import { clone } from 'lodash'
export default {
props: {
listdetail: {
type: [Object, Boolean]
},
drawershow: {
type: Boolean,
default: false
}
},
data () {
return {
form: {},
formdisable: true,
rules: {
}
}
},
computed: {
editdisable: {
get () {
return this.listdetail && this.formdisable
},
set (val) {}
}
},
watch: {
listdetail: {
handler (cval, oval) { // 监听传入的数据变化(type没变的时候如果改变传参没有办法重新计算editdisable)
this.initForm(cval)
if (oval._id !== cval._id) { // 如果数据改变需要重新设置editdisable为true,输入框修改为不可输入状态
this.formdisable = true
}
if (this.$refs.form) {
this.$refs.form.resetFields()
}
}
},
drawershow: { // 可能parent有变化,需要做initForm
handler (cval, oval) {
if (!cval) {
this.initForm(false)
}
}
}
},
methods: {
initForm (obj) {
if (this.$refs.form) {
this.$refs.form.resetFields()
}
this.editdisable = !!obj
if (obj) {
console.log(obj)
this.form = clone(this.listdetail)
} else {
this.form = {}
}
},
drawersubmit (formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$emit('submit', this.form)
}
})
},
handleClose () {
this.$emit('close')
}
}
}
</script>
<style scoped>
</style>