从WTM vue2版到 WtmPlus vue3
从2019年开始注意到WTM,觉得代码生成器真是能省不少事,大部分的项目都缺不了系统管理(包含用户,用户组,角色,角色权限,数据权限),这个通用功能WTM项目已经集成,开发者可以把主要精力投入到业务开发。对于几个人做的小型项目,想要MVC模式开发,在这个基础上进行开发,风格就能达到统一;
到2020年,vue版本发布了,想着现在前后端分离是前端开发热门,就尝试了一下,我谈谈我的粗浅认识。
对这个Vue版本,WTM群友的第一感觉就是前端写出了后端的感觉,能把这个WTM-Vue版本吃透,Vue从基础到高级用法差不多了解得差不多了。
对于初次接触的人,Vue 的 Mixins(混入)比较费解,但要了解WTM vue2 代码生成器生成的每个放在ClientApp/src/pages目录下的每个窗体页面,了解数据从webapi获取,在前端页面上面显示,然后响应按钮事件,Vue页面的生命周期,要细细,反反复复的看form-mixin.ts,action-minxin.ts,search.ts 这几个文件,要不都弄不清 store目录下的api.ts 里面写的
import { contentType } from "@/config/enum"; const reqPath = config.headerApi + "/_actionlog/"; // 列表 const search = { url: reqPath + "Search", method: "post", dataType: "array" }; // 批量删除 const batchDelete = { url: reqPath + "BatchDelete", method: "post" }; // 详情 const detail = { url: reqPath + "{ID}", method: "get" }; const exportExcel = { url: reqPath + "ExportExcel", method: "post", contentType: contentType.stream }; const exportExcelByIds = { url: reqPath + "ExportExcelByIds", method: "post", contentType: contentType.stream }; const getExcelTemplate = { url: reqPath + "GetExcelTemplate", method: "get", contentType: contentType.stream }; // 导入 const imported = { url: reqPath + "Import", method: "post" }; export default { search, batchDelete, detail, exportExcel, exportExcelByIds, getExcelTemplate, imported };
这些是怎么被调用。
另外就是 ClientApp/src/components/ 下面的文件,这个可以
<template> <wtm-dialog-box :is-show.sync="isShow" :status="status" :events="formEvent"> <wtm-create-form :ref="refName" :status="status" :options="formOptions"> </wtm-create-form> </wtm-dialog-box> </template> <script lang="ts"> import { Component, Vue } from "vue-property-decorator"; import { Action, State } from "vuex-class"; import formMixin from "@/vue-custom/mixin/form-mixin"; @Component({ mixins: [formMixin()] }) export default class Index extends Vue { // 表单结构 get formOptions() { return { formProps: { "label-width": this.$t("actionlog.LabelWidthForm") }, formItem: { "Entity.ID": { isHidden: true }, "Entity.ModuleName": { type: "input", label: this.$t("actionlog.ModuleName") }, "Entity.ActionName": { type: "input", label: this.$t("actionlog.ActionName") }, "Entity.ITCode": { type: "input", label: "ITCode" }, "Entity.ActionUrl": { type: "input", label: "Url" }, "Entity.ActionTime": { type: "input", label: this.$t("actionlog.ActionTime") }, "Entity.Duration": { type: "input", label: this.$t("actionlog.Duration") }, "Entity.IP": { type: "input", label: "IP" }, "Entity.Remark": { type: "input", label: this.$t("actionlog.Remark") } } }; } } </script>
把表单结构转换成element组件,注意分析下 ClientApp/src/components/page/CreateForm/utils.tsx 文件,里面的generateWtmFormItemComponent 值得好好分析;
生成普通的页面,例如下图页面就很容易,
但是generateWtmFormItemComponent 生成复杂页面就力不从心,
要怎么把element组件集成到wtm-create-form里面 ,
<wtm-create-form :ref="refName" :status="status" :options="formOptions"> </wtm-create-form>
我自己是修改 ClientApp/Src/Components/page/CreateForm/index.ts
render(h) { const components = _.keys(this.options.formItem).map((key) => { const createItem=this.options.formProps["createItem"]; const item = this.options.formItem[key]; if (_.isFunction(item.isHidden)) { if (item.isHidden(this.getFormData(), this.status)) { return; } } if ((_.isBoolean(item.isHidden) && item.isHidden) || !item.type) { return; } const itemComp = componentObj[item.type]; const option = { ...item, key }; if (createItem == false) { return; } const contentComp = itemComp ? itemComp.call(this, h, option) : null; return componentObj.wtmFormItem.call(this, h, option, contentComp); }); const props = { ...this.options.formProps, disabled: this.status === "detail", model: this.sourceFormData || this.formData, }; const slots = this.$scopedSlots["default"]; if(props["createItem"]==false){ return ( <el-form ref={this.elFormRefKey} {...{ props }}> <el-row class={this.elRowClass}> {slots && slots({})} </el-row> </el-form> ); } if(props["tabs"]){ const tbpanes=props["tabs"].panes.map((key)=>{ const tbpanes_Components=components.slice(Number(key.start),Number(key.end)+1); return ( <el-tab-pane label={key.label} name={key.name}> {tbpanes_Components} </el-tab-pane> ); }); return ( <el-form ref={this.elFormRefKey} {...{ props }}> <el-row class={this.elRowClass}> <el-tabs v-model={props["tabs"].activeName} type="border-card"> {tbpanes} </el-tabs> {slots && slots({})} </el-row> </el-form> ); } else{ return ( <el-form ref={this.elFormRefKey} {...{ props }}> <el-row class={this.elRowClass}> {components} {slots && slots({})} </el-row> </el-form> ); }
其中一个用法是根据组件顺序,把指定范围的组件放到 一个 el-tab-pane,但是用到这种的情况比较少,另一个就是
<template> <wtm-dialog-box :is-show.sync="isShow" :status="status" :events="formEvent"> <wtm-create-form :ref="refName" :status="status" :options="formOptions" :sourceFormData="mergeFormData"> <el-form-item prop="Entity.Name" :rules="formOptions.formItem['Entity.Name'].rules"> <el-input v-model="mergeFormData.Entity.Name" placeholder="请输入库名" @blur="blur"></el-input> </el-form-item> <el-form-item> <el-input v-model="mergeFormData.Entity.Address" placeholder="请输入地址" @blur="blur"></el-input> </el-form-item> </wtm-create-form> </wtm-dialog-box> </template> <script lang="ts"> import { Component, Vue } from "vue-property-decorator"; import { Action, State } from "vuex-class"; import formMixin from "@/vue-custom/mixin/form-mixin"; import UploadImg from "@/components/page/UploadImg.vue"; //:sourceFormData="mergeFormData" @Component({ mixins: [formMixin()] }) export default class Index extends Vue { mergeFormData ={ Entity:{ "ID":0, Name:"Name", Address:"Address", } } // 表单结构 get formOptions() { const filterMethod = (query, item) => { return item.label.indexOf(query) > -1; }; return { formProps: { "label-width": "100px", "createItem":false, }, formItem: { "Entity.ID": { isHidden: true }, "Entity.Name":{ label: "Name", rules: [{ required: true, message:"库名不能为空",trigger: "blur" }], type: "input", modifier:"trim",//v-model.trim v-model.number,v-model.lazy slotKey:"SlotName" }, "Entity.Address":{ label: "Address", rules: [{ required: true, message: "地址"+this.$t("form.notnull"),trigger: "blur" }], type: "input" } } }; } beforeRequest(formData?: object): object | void { console.log("beforeRequest:", formData); return formData; } blur(){ console.log("this.form",this.$refs[this.refName]); console.log("blur",this.mergeFormData); this.$refs[this.refName].setFormDataItem("Entity.Name",this.mergeFormData.Entity.Name); this.$refs[this.refName].validate((valid) => { if (valid) { } else { return false; } }); } } </script>
当 "createItem":false 时,在 wtm-create-form 里面自己绑定组件;总之Vu2版本要写项目,还是要进行各种改造,不像 Layui版本那种绑定风格,自由度感觉受到限制;
有看过 WTM react 版本,react版本跟layUI版本的绑定比较类似,自己把实体绑定到每个组件上,后来听说WTM vue版本要进行WTM VU3重制,我不知道了解信息有没有错误,是WTM react操刀人要进行WTM vue3版本重制,我就想风格应该会跟React类似;
千呼万唤,2021.11.18 WTMPlus Vu3版本正式发布,听说刘总为了发布都熬通宵了;
第一时间生成几个Model试了一下,看看有啥变化。
整个目录结构都变了,UI也由Vue element 换成了 Vue antd,看绑定,是不是跟 layUI版本有点类似了。
<template> <WtmDetails :queryKey="queryKey" :loading="Entities.loading" :onFinish="onFinish"> <a-row :gutter="6"> <a-col :span="12"> <WtmField entityKey="NameAdd"/> </a-col> <a-col :span="12"> <WtmField entityKey="Id_NoAdd"/> </a-col> <a-col :span="12"> <WtmField entityKey="Day_of_birthAdd"/> </a-col> </a-row> <div style="text-align:right;"> </div> <template #button> <a-divider type="vertical" /> <a-button type="primary" html-type="submit"> <template v-slot:icon> <SaveOutlined style='margin-right:5px'/> </template> <i18n-t :keypath="$locales.SysSubmit" /> </a-button> <a-divider type="vertical" /> <a-button type="primary" @click.stop.prevent="__wtmBackDetails()"> <template v-slot:icon> <RedoOutlined style='margin-right:5px'/> </template> <i18n-t :keypath="$locales.SysClose" /> </a-button> </template> </WtmDetails> </template>
还刚接触Vue3版本的,等认识更深刻后再发表看法;
最后夸一夸 WTMPlus,拥有多个微软MVP的团队倾力打造的低码平台,而且项目源码全部可以下载,开发者可以进行自由发挥,对于一个程序员,源码在手,天下我有的感觉是不一样的。
网址:http://wtmplus.walkingtec.cn