阅读前请按照顺序参看系列文章,效果更佳!
Vue中路由到一个公共组件,然后根据路径中是否存在文件动态加载组件
让前端工程师躺平——后端大佬教你写通用组件的开发系列一
据说系列文章很难火爆,因为知识点包袱不够多,所以大家看往后不太愿意收藏
, 不过没关系了, 系列文章的好处是看着舒服,碎片化时间内很快就看完了,不是吗?
第一篇基本属于探索阶段,经过摸索,Vue的功能还是非常强大的!
作为前端重要的输出武器,可谓是小李飞刀,例无虚发,刀刀致命。
既然通过验证,说明公共组件可以完成工作,并且可以很方便的进行扩展。即使自动生成的公共组件无法满足需求,也可以让躺平的前端工程师爬起来,继续按需码代码,何乐而不为呢?
第二篇则从大的思路上结合前后端的优势,试图纵览需求。
整体配合完成一个近似不可能的需求(让前端小弟们躺平,也不知道是拯救前端,还是残害前端)。
前端兄弟们,当你们弄清楚这些的时候,应该已经进阶高阶了吧,不会在意这点小小压力 吧?
其实真正完成这个需求,还是一个蛮大的工程,期间遇到的问题依然不少,这不我又来了。
书接上回,话说经那厮后端甩锅王的设计,接口的表设计已经成型,这不,产品经理A又找到门上。
产品经理A:“锅~~, 王~~~,在吗?”
正在码代码的后端甩锅王,忽然听到耳边传来熟悉而带有魔性的声音,不仅心里一颤,随不情愿,但还是故作精神百倍的应了一声,
“哎哎,在这呢。”
产品经理A:“活来了! 上次你说的那个设计挺好的,能不能今天搞几个接口,让前端童鞋玩玩?”
后端甩锅王:“见外了吧,这不是分分钟的事吗? 不过前端童鞋不是请假了吗(躺平)?”
产品经理A:“这不是请假排队打疫苗,到了才发现去的太早,排了个第一名,所以又来上班了。”
后端甩锅王心理暗骂,这倒霉孩子,我的摸鱼时间啊!嘴上却说:“好嘞,这下就让他有活干!”
产品经理A拍了拍后端甩锅王,“大神啊,我就看好你!”
后端甩锅王默默打开编码神器Visual Studio 2019 全球企业规划旗舰版,
右键打开 EF Core Power Tools ,只见他操作如下,不过片刻功夫,三下五除二搞定了实体定义。
如果没有定义,默认就按照数字\文本\日期返回控件吧.
private string GetDefaultType(Type type)
{
if (numberType.Contains(type))
{
return "Number";
}
else if (dateType.Contains(type)){
return "DateTime";
}
else
{
return "Text";
}
}
自动按照EF定义获取列信息,并生成列表和表单的定义插入数据库.
//以插入列表为例,看下面代码,超简单
var type = _unitOfWork.DbContext.Model.GetEntityTypes().FirstOrDefault(x=>x.ClrType.Name == model.ModelName);
if(type == null)
{
throw new BusinessException(PublicError.NotFoundModelName, model.ModelName);
}
List<UdfListTemplate> templates = new List<UdfListTemplate>();
var sortId = 10;
foreach(var p in type.GetProperties())
{
//var f = p.FieldInfo;
templates.Add(new UdfListTemplate
{
ModelGuid = model.ModelGuid,
ModelName = model.ModelName,
SysId = model.SysId,
FieldName = p.Name,
ControlType = GetDefaultType(p.ClrType),
SortId = sortId,
Width = 140,
IsEncrypte = 0,
IsFixed = 0,
IsHidden = 0,
IsSearch = 1,
IsSort = 1,
IsSum = 0
}) ;
sortId += 10;
}
//一把插入,速度惊人(可查询之前的批插入文章)
await _unitOfWork.DbContext.BulkInsertAsync<UdfListTemplate>(templates);
定义下前端童鞋的访问接口
[HttpGet("{modelGuid}")]
[AllowAnonymous]
public Task<GetModelDefineResult> GetModelDefine(string modelGuid)
{
return _modelService.GetModelDefine(modelGuid);
}
打完,测试下,收工!
摸摸鱼,可以交付前端童鞋了。
我就是那个前端。
话说那天,本来想给产品经济一个突然袭击,没想到锅王的锅还是砸到我的头上了,哎,这倒霉孩子~~~
为了觉得我很重视这个功能,我先来个ES6的类,开开胃。
一般对于接口这种变化的类,通常我们前端的原则是随用随取,设计毛线的类!
比如后端A\B接口返回的格式如下:
//A
{
"data": {
"a":123,
"b":"aaaaaabbbb"
}
}
//B
{
"list":["a","b"],
"saa":"aaa"
}
那我基本是从api获取数据后,拿来就用, 假设data是api的返回值,则
const a = data.data.a
const b = data.data.b
const c = data.list[0]
const d = data.saa
好爽~~~ ,
要是出错了 就BB后端, 你咋不返回XXX呢?你咋能是 null呢!
这次设计类,有点闲! 摘抄一部分吧~~
// 模块信息定义
class UdfModelInfo {
constructor(modelGuid,
modelName,
sysId,
name,
businessPk,
functionOperate,
formColNum,
listShowSelect,
listShowPage,
listShowNo,
listShowTree,
udf1,
udf2,
udf3) {
this.modelGuid = modelGuid
this.modelName = modelName
this.sysId = sysId
this.name = name
this.businessPk = businessPk
this.functionOperate = functionOperate
this.formColNum = formColNum
this.listShowSelect = listShowSelect
this.listShowPage = listShowPage
this.listShowNo = listShowNo
this.listShowTree = listShowTree
this.udf1 = udf1
this.udf2 = udf2
this.udf3 = udf3
}
}
// 模块\列表\表单 定义
export default class UdfModel {
constructor(model, listTemplate, formTemplate) {
this.model = new UdfModelInfo(...model)
this.listTemplate = new UdfListTemplate(...listTemplate)
this.formTemplate = new UdfFormTemplate(...formTemplate)
this.createDate = new Date()
}
}
export {
UdfListTemplate,
UdfFormTemplate,
UdfModelInfo,
}
这里采用了vuex来存储每次请求的配置信息。我们计划每10分钟内的数据保存到vuex内,如果超过10分钟,则重新请求后端api。
存储数据采用Map来按modelGuid存储,因为它是唯一的。
const state = {
udf: new Map(),
}
给它增加2个 mutations 方法 操作udf
const mutations = {
SET_UDF_MODEL: (state, key, data) => {
const udfModel = new UdfModel(data.model, data.listTemplate, data.formTemplate)
state.udf.set(key, udfModel)
},
CLEAR_CACHE: (state) => {
state.udf.clear()
},
}
按照vuex规范,我们换需要增加异步Actions,在这里,我们按照存储的时间判断下值是否超过10分钟,如果超过了,则从http里获取配置信息,并更新它。
const actions = {
setUdfModel({
commit }, guid) {
if (state.udf.has(guid)) {
const val = state.udf.get(guid)
if (val && Math.abs(((new Date()).getTime() - val.createDate.getTime())) > 1000 * 60 * 10) {
return
}
}
const http = new Http()
http.fetch({
url: `/Tools/GetModelDefine/${
guid}`,
method: 'get',
}).then(data => {
if (data && 'model' in data) {
commit('SET_UDF_MODEL', guid, data)
}
})
},
clearCache({
commit }) {
commit('CLEAR_CACHE')
},
}
好了,最后导出这些变量。
export default {
namespaced: true,
state,
mutations,
actions,
}
因为vuex并没有持久化存储,因此用户按F5,则会清理掉所有缓存。 为了让频繁登陆的用户体验更爽,我们在登陆时,主动清理缓存。
在登陆用户的组件内,增加 mapActions
的引用.
import {
mapActions } from 'vuex'
在组件方法中引入清理缓存方法:
methods: {
...mapActions({
clearUdfCache: 'udf/clearCache',
}),
}
然后呢,在组件创建时,调用它.
created() {
this.clearUdfCache()
}
之前有说过,我们的公共操作方法,已经提取到 mixus类内,因此我们定义 组件的创建方法时,对数据进行加载。
由于是采用同一个公共组件,完成所有模块的增删改查操作,根据Vue的优化策略,再切换路由后,公共组件并没有被销毁,而是被重用,所以其 Created 钩子方法仅仅调用一次。
为了使得切换路由后,仍可以调用新的配置,我们采用监控路由的方法实现载入数据。
watch: {
$route: function(to, from) {
// todo
// console.log('watch', to)
let guid = ''
if (to.meta && 'guid' in to.meta) {
guid = to.meta.guid
} else {
let arr = to.path.split('/').slice(0, 3)
arr = arr.map(item => item ? item[0].toUpperCase() + item.substring(1) : '')
guid = arr.join('')
}
console.log('guid=', guid)
this.$store.dispatch('udf/setUdfModel', guid)
},
},
经过上述一系列操作,已经可以很好的载入配置数据了!
躺平每一位工程师都不是我希望看到的,我们是希望能更快速的帮助新手们节省时间,把精力投入到更复杂的需求逻辑上。
因此不要焦虑,上述对话只是个玩笑而已,软件工程师是系统开发不可缺失的人才,当然我们也应该多开动下脑筋,让繁琐重复远离人类!这也是软件工程师神圣的职责之一吧~~~