WebBuilder

218 阅读17分钟

组件

不同于我们常用的Element组件库,示例和代码都在一起,方便直接进行示例代码的复制粘贴。

v8pro的组件示例见示例,示例代码见模块-example示例,我们可以在示例代码中点击运行查看对应的组件效果。

1.布局组件

  • viewport  类比为 div ,可以通过设置layout属性调整其内部元素的布局(是一个全屏的盒子,页面上所有的元素必须为他的子级,否则展示不出来;但弹窗可以使用array组件与viewport平级 下 放 window组件)

image.png

  • pannel layout设置为row 内部表单元素会按照inline方式进行布局

  • container 容器组件,比如页面需要加载另一个组件,需要一个container容器

  • card 

以上几个没看出有啥区别

2.表单组件

    1. 没有专门的form组件,通常使用pannel进行包裹各表单项,进行表单项的布局,比如水平,垂直或inline
    1. 表单校验,示例:

if (Wb.verify(app.viewport1)) {
  Wb.ajax({
    url: xpath + '&xaction=save',
    comps: app.viewport1,
    success() {
      Wb.tipDone();
    }
  });
}

通常框架会自动进行校验。

  • 3.required ,tip ,validator 等都可以在属性中定义

  • 4.ajax提交数据时,可以直接通过下面方式获取表单数据


Wb.getValue(app.viewport1) //会收集所有含有value的键值对,比如{userName:'admin'}
app.Cid.getValue()  //获取某个组件实例的value值。 注意:cid必须是唯一的,比如页面的查询项和弹窗中的表单项不能取相同的cid,否则无法获取到值
Wb.apply({},Wb.getValue(app.viewport1)) // apply对象的合并

  • 5.表单重置
Wb.reset(app.panel1)
  • 6.表单项的laelWidth可以设置宽度,保证label对齐,避免有些项加了required,有些没加不对齐的情况;hint可以设置提示

3.表格组件grid

  • 1.表格如果需要mock数据,可以在grid组件中配置localData的值;若配置了localData则不会发送ajax请求到后端

  • 2.服务端渲染的表格 可以 url 链接到对应的代码 ;设置了autoLoad为true则初始化就会调用url服务

image.png

  • 3.增删改查后更新表格数据
app.grid1.reload() //会自动调用后端查询接口,默认使用最后一次load方法的参数

image.png

不同于 app.grid1.load()//会初始化所有查询参数

image.png

  • 4.带操作的表格列如果想要生效一定要把array组件的cid改成buttons;一定要把array组件的cid改成columns,否则内容不展示

image.png

  • 5.grid的toolbar组件如果要展示,必须设置isProperty属性为true,否则会当成表格内容进行渲染,加了后才会向下面这样渲染

image.png

  • 6.column必须设置的属性 fieldName 相当于element的prop,对应后端返回的字段名称
  • 7.text可以设置列标题,width可以设置列宽度,align属性只能设置表体单元格的居中,表头的居中需要配置titleAlign属性,render属性可以通过js自定义单元格HTML的输出

render示例一:根据不同状态设置单元格为不同的tag

//value可以直接获取单元格的值
if(value == '0'){
  let a = el.addEl('w-ta-center0', 'div');
  let b = a.addEl('w-tb-center0', 'div');
  b.textContent = '关闭';
} else if(value == '1'){
    let a = el.addEl('w-ta-center1', 'div');
    let b = a.addEl('w-tb-center1', 'div');
    b.textContent = '存续';
}

render示例二 :将单元格的内容渲染为html

//this.data可以获取该行的数据
let pTag = el.addEl('my-link','div')
pTag.innerHTML = this.data.privateStatute 
  • 8.设置editable属性为true可以设置表格为可编辑,可编辑表格必须作为对应的column设置editor属性,如果未设置editor属性那么该列仍然不可编辑。

  • 9.可编辑表格,编辑项是select下拉列表会存在鼠标移出单元格是undefined的情况,需要在当前列表的render函数中返回一个数组或者处理成字符串也可以

//初始化回显时,返回的是字符串,可以直接显示;当用户页面选择时,value值是数组对象,需要处理,否则显示为[object,object]
if(Wb.isArray(value)){
  // return value.map(item=>item.text)
  return value.map(item=>item.text).join(',')
}
return value
  • 10.当回显时该单元格实际返回一个字符串,也会出现鼠标聚焦该编辑项时undefined的情况。因为value和下拉select实际存的option不一致,也需要处理

解决方案如下:

console.log('value---', this.value) //初始化回显时value格式如:['开发者,baoweifang']
console.log('data---', this.data)
let item = this.value[0]
if (typeof item === 'string') {
  let transValue = item?.split(',') ?? [] //格式转换成:['开发者','baoweifang']
  if (transValue.length && typeof transValue[0] === 'string') {
    const selectedOptions = this.data.filter(item => transValue.includes(item.text))
    this.value = selectedOptions
  }
}
  • 11.表格添加行
app.editableGrid.addRecord();
  • 12.grid的操作列的按钮中 ready事件这样写可以设置按钮的隐藏或禁用
this.setVisible (false) // 隐藏
this.setDisabled (false) // 禁用

按照上述方式,可以避免在actionCol中写一堆动态添加元素和移除元素的逻辑,像下面这样的:

const PROJECT_STATUS = this.data.PROJECT_STATUS
let row = this;
function addClaimBtn(el) {
  row.addFooter({
    cname: 'button', returnType: 'int', text: "认领", width: '5em', type: "primary",
    events: {
      click(event) {
        handleClaim()
      }
    }
  }, el);
}

//已发布状态才可以认领
if(PROJECT_STATUS === '1'){
  const wrap = createBtnWrap()
  addClaimBtn(wrap)
}else{
   row.removeAll(1)
}

  • 13.表格配置多选

image.png

获取多选数据时,直接在点击按钮时进行数据的过滤。

  • 14.实操中常用的属性
    • data 获取显示的数据,和element-ui一致。

注意:如果要实现页面的响应式更新,需要替换一个数组,单纯的调用数组的变异方法是不生效的,这点有别与vue的列表渲染

示例:

function handleConfirm(){
  Wb.request({
    url:xpath + '&xaction=post',
    params:Wb.getValue(app.modalWin),
    success(res){
      const addedData = Wb.getValue(app.modalWin)
      //  app.grid1.data.push(addedData) //不会触发页面更新
      //  app.grid1.reload() //  这个方法也不会
       app.grid1.data = [...app.grid1.data,addedData] // ok
       Wb.tipSucc('添加成功')
       app.modalWin.close()
    }
  })
}
handleConfirm()
  • selection 当前选中行实例,selection.data获取对应行的数据,等同与下面的selectionData

selection gridItem.png

  • selectionData  获取当前选中的行数据

selectionData Object.png

  • selections/selectionsData 是数组类型,但是如果没有选中行,是空数组,而非全部

image.png

  • originData 也可以获取当前选中的行数据,不知道和selectionData有啥区别

originData Object.png

  app.grid1.selections
  app.grid1.selectionData
  app.grid1.originData  
  Wb.getActionHint(recs, 'id') //获取选中数据的唯一标识,调用getActionHint方法进行二次确认
  delRecords 删除行,直接删掉了,无需刷新列表;但实际并没有重新计算分页,所以建议删除后还是调用app.grid1.reload()

image.png

image.png

4.弹窗组件

  - 1.使用的是window组件,设置弹窗的标题是title属性,记住不能设置text,text代表的是body里面的内容,如果设置了text,则下面的子元素会不显示。

  - 2.打开关闭弹窗

 ```js

app.autoResetWin.show();
app.autoResetWin.close();

 ```

  - 3.如果在关闭之前需要执行一定操作,需要配置closeAction为destory,同时添加destory事件

  - 4.新增和编辑时打开弹窗,示例

 function handleEdit(){
    const selectedData = app.grid1.selectionData
    if (!selectedData){
        Wb.warn('请选择一条数据')
        returnset
    }
    app.modalWin.show()
    Wb.setValue(app.modalWin,selectedData)//数据回显
    app.modalWin.title="编辑"
 }
 handleEdit()

  注意:弹窗里面的表单元素的cid建议与表格里面column的fieldName一致,方便直接解构。如果不一致,setValue的第二个参数中的键名需要与表单的cid一致,因为setValue方法是把值按键名对应组件cid进行赋值的。

var sels = app.grid1.selectionData;
console.log(sels);
if (sels == undefined) {
  Wb.warn('请选择一条数据');
  return;
}else{
  app.deptEditWin.show();
  Wb.setValue(app.deptEditWin,{
    USER_NAME_edit:sels.USER_NAME,
    DISPLAY_NAME_edit:sels.DISPLAY_NAME
  });
  app.deptEditWin.title = '修改';
}

setValue方法说明如下: image.png

  - 5.弹窗确定事件,比如保存表单数据,关闭弹窗,刷新列表数据

需要设置弹窗的ok事件,示例代码见上面 handleConfirm

  • 6.新增和编辑共用一个弹窗

    • 6.1可以通过给app赋值一个变量,比如app.isEdit ,该变量只在当前页面生效
    • 6.2新增时清空编辑的数据,数据的值需要和组件的数据一致,统一给undefined并不生效,比如我用的是input和textarea,给空字符串是可以的,示例如下:
      const params = Wb.getValue(app.draftModal) 
      for(let key of Object.keys(params)){
         params[key] = ""
       }
      Wb.setValue(app.draftModal,params)
      app.draftModal.show()
      app.isEdit = false
      app.draftModal.title = "新增底稿"
    

注意: NO!可以直接设置resetDialog后则每次打开弹窗都会重置,新增时可以无需清空数据,减少一步操作。是不是更方便啦!

BUT:resetDialog只会重置表单元素数据,如果打开的弹窗里面还有其他类型组件,比如文本或表格等,需要在colse事件中清空下。避免数据未请求回来时是上一个列表对应的数据。

如果需要在关闭弹窗时执行保存事件,不应使用close事件,而应使用hide事件。close事件会把数据重置为初始值

7.设置弹窗的defaults属性,可以使所有的子表单元素组件的labelWidth进行统一;这样可以避免给表单的每一项设置labelWidth

image.png

8.如果不需要展示弹窗底部的取消,确定按钮,可以去掉dialog:true的配置或者改成false即可.如果配置了resetDialog,也需要去掉

image.png

5.tree和select

  • 1.数据来源于接口,只需要配置url属性即可
  • 2.可以在success事件中对请求回来的数据做处理,比如
let newArr = response[0].children

newArr.forEach(item => {
  addLeafProperty(item)
})
app.deptTree.setData(response)

function addLeafProperty(node) {
  node._leaf = node.children.length === 0;
  node._expanded = true;
  node._disabled = node.children.length > 0 ;
  if (node.children.length > 0) {
    for (let child of node.children) {
      addLeafProperty(child);
    }
  }
}

注意:框架目前如果父级设置了_disabled为true,即使子级_disabled为false,子级也会被禁用。可以在beforeSelect中阻止对应节点的选中

if (!item.leaf) {
  Wb.tip('Please select a leaf node');
  return false;
}
  • 3.需要手动加上leaf属性,才会展示会文件形式,否则都是文件夹目录

  • 4.subTextField可以设置副文本,值为后端对应的字段名称

image.png

image.png

5.treeSelect只有设置了treePicker {itemsPath:'children'}子级才会正确展示,itemsPath为子级展示的后端字段名称

image.png

6.tree组件的子节点字段是items不是children,在扁平化app.deptTree.data时需要注意

7.treeSelect 如果想要正确回显,需要同时设置该组件的textField和valueField值,默认读取的是text和value.

image.png

示例代码:

let rec = app.deptTree.selectionData
app.deptEditWin.show();
app.deptEditWin.title = '修改'
app.PARENT_ADD.visible = false
app.PARENT.visible = true
app.CODE.setDisabled(true)
Wb.setValue(app.deptEditWin,rec)
Wb.setValue(app.deptEditWin,{NAME:rec.NAME,CODE:rec.CODE,PARENT:rec.PARENT_ID});

app.PARENT.value = {
    NAME:rec.PARENT_NAME,
    ID:rec.PARENT_ID
}
//如果选中的是第一级,清空弹窗中父级的展示,避免展示'0'
 if(app.deptTree.selectionData?.PARENT_ID == '0'){
    Wb.setValue(app.deptEditWin, { PARENT:'' })
  }
app.isEdit = true;

8.treeSelect如果需要加请求参数,可以在beforeload事件中这样写

params.foo = 'bar';

9.select下拉框如果想要同时获取选中选项的label和value值,可以设置bindField,且bindFied的值会自动传给后端,无需处理参数。比如设置bindFied为DEPT_NO,则{DEPT_NO:ID}会传给后端,DEPT_NAME会绑定textField的值传给后端

image.png 传给后端的参数{DEPT_NO:ID,DEPT_NAME:NAME}。如果不设置默认给后端传递的参数只有{DEPT_NAME:ID}

10.select如果设置多选,multiSelect:true。cid绑定的值默认为数组,且包含text和value,类似 [{text:'bwf',value:1},{text:'wmy',value:2}],如果要正确回显,对应的字段也必须保持一致的数据格式。所以在入参时需要进行拆分,回显时进行组合拼接。 配置如下:

image.png

编辑时确认参数拆分:

if(params.GROUP_MEMBERS && params.GROUP_MEMBERS.length){
  const members = [...params.GROUP_MEMBERS]
  params.GROUP_MEMBERS = members.map(item=>item.EMP_NAME).join(',')
  params.GROUP_MEMBERS_NO = members.map(item=>item.EMP_NO).join(',')
}else{
  params.GROUP_MEMBERS = null
}

回显时将字段进行合并

let GROUP_MEMBERS = selectionData.GROUP_MEMBERS
if (GROUP_MEMBERS && GROUP_MEMBERS.length) {
  const GROUP_MEMBERS_NAME = selectionData.GROUP_MEMBERS.split(',')
  const GROUP_MEMBERS_NO = selectionData.GROUP_MEMBERS_NO.split(',')
  GROUP_MEMBERS = GROUP_MEMBERS_NAME.map((item, index) => {
    return {
      EMP_NAME: GROUP_MEMBERS_NAME[index],
      EMP_NO: GROUP_MEMBERS_NO[index]
    }
  })
}
//设置组员的回显
Wb.setValue(app.GROUP_MEMBERS, { GROUP_MEMBERS })

10.多选时设置required属性时,即使已经选择了值,但还是会校验不通过,建议在提交时做校验。

数据请求 

  • Wb.ajax

  • Wb.request

  • Wb.Request.ajax

常用的serveScript

建表语句

CREATE TABLE IF NOT EXISTS bwf_user (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    password VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

这个语句创建了一个名为users的表,包含以下字段:
 id: 主键,自动递增。  
 username: 一个最大长度为50个字符的字符串,不能为空。  
 password: 一个最大长度为50个字符的字符串,不能为空。       
 email: 一个最大长度为100个字符的字符串。  
 created_at: 时间戳,默认为当前时间。  
IF NOT EXISTS是可选的,用来避免在表已经存在的情况下重复创建。

1.增,在xwl文件的serveScript中写下面代码

//后端
方式1,执行sql语句,参数使用{?USERNAME?}的方式
Wb.sql(`insert into bwf_user (USERNAME,PASSWORD,EMAIL) values ({?USERNAME?},{?PASSWORD?},{?EMAIL?})`)

方式2Wb.sendRowx(`insert into bwf_user (USERNAME,PASSWORD,EMAIL) values ({?USERNAME?},{?PASSWORD?},{?EMAIL?})`)

方式3//const ID = Wb.getId() //新增时不需要传入ID,系统会自动生成ID;传入时还会有重复的出错提示
 const USERNAME = Wb.get('USERNAME')
 const PASSWORD = Wb.get('PASSWORD')
 const EMAIL = Wb.get('EMAIL')
 Wb.sync({ tableName: 'bwf_user', insert: { USERNAME:USERNAME,PASSWORD:PASSWORD, EMAIL:EMAIL},send:true});
 
 注意:只有sql语句可以通过{?USERNAME?}获取前端传递的参数,名称一致;sync时需要通过Wb.get('USERNAME')的方式手动获取参数
 
 //前端
 //1.打开弹窗
 app.addModal.show()
app.addModal.title = '新增'

//2.弹窗ok确认事件
let params = {
  USERNAME:app.USERNAME_add.getValue(),
  PASSWORD:app.PASSWORD_add.getValue(),
  EMAIL:app.EMAIL_add.getValue(),
}
Wb.request({
  url:xpath+'/insert',
  params,
  success() {
      Wb.info('新增成功!');
      app.addModal.close();
      app.grid1.reload();
  }
})

2.删

//方式1,
const userData = Wb.sql(
  {
    sql: `
      delete from 
      bwf_user 
      where  ID = {?ID?}`,
  }
);
Wb.send(userData) //发送数据给客户端,必须加,否则只是数据库加了,页面不会立马展示

//方式2
Wb.sendRowx('delete bwf_user where ID ={?ID?}')

TODO:sync方式实验没有成功,报错 TypeError: e.getMessage is not a function
Wb.sync({
   tableName: 'bwf_user',
   del:{
     ID:Wb.get('ID')
   },
   whereFields:"ID"
})

//前端
function handleDel(){
  let grid = app.grid1, recs = grid.selections;
  if (!recs.length) {
    Wb.tipSelect();
    return;
  }
  Wb.confirm(Wb.getActionHint(recs, 'ID'), f => {
    Wb.ajax({
      url: xpath+'/del',
      params: { ID: app.grid1.selectionData.ID},
      success() {
         Wb.info('success')
        grid.delRecords(); //纯前端删除选中的行,实际不会删除数据库中的数据;不会重新计算分页
        // app.grid1.reload() //会发送请求
      }
    });
  });
}
handleDel()

image.png

3.改

//后端
//方式1
 Wb.sendRowx('update bwf_user set USERNAME={?USERNAME?},PASSWORD={?PASSWORD?} ,EMAIL={?EMAIL?} where ID ={?ID?}')
 
 
//方式2
Wb.sql('update bwf_user set USERNAME={?USERNAME?},PASSWORD={?PASSWORD?} ,EMAIL={?EMAIL?} where ID ={?ID?}')

//方式3
 const ID = Wb.get('ID')
 const USERNAME = Wb.get('USERNAME')
 const PASSWORD = Wb.get('PASSWORD')
 const EMAIL = Wb.get('EMAIL')

//报错 TypeError: e.getMessage is not a function
 Wb.sync({ tableName: 'bwf_user', update: {ID, USERNAME,PASSWORD, EMAIL},whereFields:'ID',send:true});


//前端
//1.打开弹
 function handleEdit(){
    const selectedData = app.grid1.selectionData
    if (!selectedData){
        Wb.warn('请选择一条数据')
        return 
    }
    app.editModal.show()
    Wb.setValue(app.editModal,{
      USERNAME_edit:selectedData.USERNAME,
      PASSWORD_edit:selectedData.PASSWORD,
      EMAIL_edit:selectedData.EMAIL
    })
    app.editModal.title="编辑"
 }
 handleEdit()
//2.弹窗点击确定ok事件
let params = {
  ID:app.grid1.selectionData.ID,
  USERNAME:app.USERNAME_edit.getValue(),
  PASSWORD:app.PASSWORD_edit.getValue(),
  EMAIL:app.EMAIL_edit.getValue(),
}
Wb.request({
  url:xpath+'/update',
  params,
  success() {
      Wb.info('修改成功!');
      app.editModal.close();
      app.grid1.reload();
  }
})

4.查,注意条件要加下null,否则初始化查询没有数据

//后端:
//方式1:USER_NAME 变量为调用load方法传过来的
Wb.sendRowx(`select * from bwf_user where {?USERNAME?} is null or USERNAME = {?USERNAME?}`)

//方式2:
var userData = Wb.sql(
  {
    sql: `
      select * from 
      bwf_user 
      where  {?USERNAME?} is null or username = {?USERNAME?}`,
  }
);
Wb.send(userData) //发送数据给客户端

//前端:
const USERNAME = app.USERNAME.getValue();
app.grid1.load({
  params:{
    USERNAME
  }
})

组件化与模块化

common.js

AMD

CMD

esmodule

1.页面中如何引入一个组件,示例,下面是一个按钮的点击事件

let ct = app.runModulePanelCt;  //runModulePanelCt 是页面的容器组件
ct.destroyAll();
Wb.run({
  url: 'm?xwl=myapp/module-test/sub', // url是引入组件的路径
  success(scope) {
    //scope 对像是顶层的module实例,包含所有的子元素实例
    ct.add(scope.main);
  }
});

runModulePanelCt image.png

scope页面结构 image.png

log scope image.png

2.怎么给弹窗传递回调函数

main 按钮的点击事件

Wb.run({
  url: 'm?xwl=myapp/module-test/common',
  owner: 'window1', //destroy the module when window1 is destroyed
  success(scope) {
    scope.add(3, 5, (total, win) => {
      Wb.tip('Total value is: ' + total);
      win.close();
    });
  }
});

弹窗的initialize事件,初始化时给组件实例注册了一个add方法

Wb.apply(app, {
  /** @property {Function} callback The callback function. @priv */
  /**
   * Perform add.
   * @param {Number} value1 value 1.
   * @param {Number} value2 value 2.
   * @param {Function} callback The callback function after completion.
   * @param {Number} .total The total value.
   * @param {Wb.Window} .win The edit window.
   */
  add(value1, value2, callback) {
    app.number1.value = value1;
    app.number2.value = value2;
    app.callback = callback;
    app.window1.show();
  }
});

弹窗的ok事件,调用callback方法传入实参

app.callback(app.number1.value + app.number2.value, this);

效果 点击确定时会弹出数字的总和。

3.加载js

注意:创建js需要在应用/web创建,模块里面只能创建xwl后缀的文件。

示例:

Wb.load(
  'wb/comCheck/js/sysConfig.js',
  f => {
  setConfigBtnVisible('IMPORT_CLASS_EVALUATION',app.importBtn)
  }
);

//loadx是load的promise版本,奇怪可以避免一些空指针的问题
Wb.loadx('wb/comCheck/js/sysConfig.js').then(f=>{
   setConfigBtnVisible('IMPORT_CLASS_EVALUATION',app.importBtn)
});

image.png

image.png

如果只是单纯的引入js,css则可以直接在module中配置links熟悉,如

image.png 注意:引入的links必须写在第一行,否则会引入不成功

路径的表示方式

1. xpath 代表当前路径,具体到文件类型

image.png

image.png

image.png

image.png

2.使用的绝对路径,目录用/

Wb.open('m?xwl=myapp/test');

3.相对路径,相对的是项目根目录

url:'./sub'

image.png

权限配置

  • 1.给对应的按钮配置bindModule属性,对应一个xwl的标识位,里面可以不用写逻辑

image.png 配置完后对应菜单会增加一个编辑树的选项

  • 2.通过用户角色给对应权限赋值

其他一些用到的

  • 1.fireEvent可以调用别的组件的事件
app.addDeptBtn.fireEvent('click')
  • 2.在多个弹窗组件外面包一层array组件会导致tree控件 contextMenu的点击事件与toolbar的点击事件打开的弹窗元素不一致

  • 3.接口调用地址,参数后面拼接的是&xaction

m?xwl=comCheck/checkManagement/service/checkDraftService&xaction=queryDraft

根据实际应用场景进行调整

  • 4.设置组件的禁用
app.editDeptBtn.setDisabled(true);
  • 5.下拉框的数据来源于后端接口,如果直接配置url为serveScript的地址,则每次点击下拉箭头都会发送请求;写在ready中请求接口地址,或者可以配置loadOnce属性为true,则会在页面初始化时进行请求一次

  • 6.select 树形控件如果设置了autoLoad为true,则调用了app.PARENT.refresh(),下拉列表也不刷新

  • 7.Wb.raise() 可以用于后端抛出异常,则接口不会是success

  • 8.gridColumn可以设置表单占据几行

image.png

  • 9.样式问题,设置style向下面这样设置
    border:1px solid #a0cfff

  • 10.toggle开关后端返回的“0” 和 “1”会被框架自动隐式转换;实际保存的值是布尔类型,需要处理

  • 10 textarea组件不像element一样,有输入字数提示和超限阻止输入的功能,改进建议

  • 1.1增加suffix给字数提示
  • 1.2change事件中更新输入字数并判断字数超限了阻止用户输入 代码如下:
this.suffix = [this.value.length,this.maxLength].join('/')
if(this.value.length>this.maxLength){
   event.preventDefault() 
    console.log('this.value---before',this.value)
   this.value = this.value.substring(0, this.maxLength);
    console.log('this.value--after', this.value)
}

但是有个问题,输入字数大于maxLength时有时候也会显示出来,再输入一次后又恢复正常,异步更新原因? 建议只增加一个计数的,不阻止用户输入,因为超限时最终表单也无法提交

11.grid column 设置width:-1会自适应,但是不建议轻易使用,因为列过多且文字过长时会导致文案被遮挡且没有滚动条;可以用在列少的情况下,让其单元格自适应。

12.一些操作元素和样式的api

  • addFooter
  • addEl
  • setStyle

13.普通提示建议使用 Wb.tip(),会自动关闭;Wb.info()还要点击按钮才会关闭

14.点击按钮时如果请求接口数据并给弹窗中的grid赋值,表格的分页会不展示;但是如果给grid配置url,autoLoad为false(避免还没有打开弹窗就请求了列表),在点击时调用grid的load方法并传入参数就可以了

//bad

image.png

//good

image.png

15.treeItem调用doSelect方法,可以设置某个符合条件的节点进行选中,并且会自动触发selectionChange事件

image.png

16.tree组件如果设置了autoSelect会true,会默认选中树的第一个节点;当设置autoSelect为false时,初始化打开ID为undefined,动态设置节点选中后会有值,如果通过ID进行判断会命中一些为空的提示

17.gird控件设置单元格的minWidth,实际还是会出现大屏上表格有留白的情况

image.png 因为table没有设置width为100%,而是width为1px。导致table的宽度由各单元格的width总和构成(注意不是minWdith)

image.png

而如果设置其中一个单元格的minWdith和width:-1,则应该不会有问题,会增加一个类,设置table的宽度为100%,占满剩余的整个屏幕大小,如下:

image.png 注意:需要设置minWidth,避免在小屏上没有剩余空间,该列不显示的问题

18.column中render的代码即使注释了也会影响子组件的渲染

19.setListeners可用于给组件设置监听事件

20.multiSelect设置具体的值,譬如85% 可以调整select输入框的样式;tagWrap 当允许{#multiSelect}多选时,选择的标签按钮是否允许换行

遇到的坑,请慎用

  • 1.可编辑表格 Editable grid,使用前端渲染表格的方式,实际在点第一次保存时并未真正触发保存事件,示例中给的是方式是可以的,但未实践是怎么做的
  • 2.接1,改成在column的render方法动态创建表单元素的方式,但是textArea控件敲回车时无法换行,代码示例如下

image.png

  • 3.还是可编辑表格,可编辑单元格是一个Select组件,在ready中给组件的data动态赋值,发现每次新打开弹窗时,数据都是第一次的数据。

场景如下:在列表页面的表格操作栏中点击一个按钮,此时会获取该列表中的一个字段的数据为LIST,打开一个弹窗,弹窗里面有一个可编辑表格组件,其中一个单元格的editor配置的是一个Select组件,在ready中给该组件的data赋值,成功。但是关闭弹窗(弹窗已设置resetDialog属性为true),重新点击列表的另一行数据,此时LIST变更了,但是弹窗中的Select组件的ready函数没有重走,导致下拉数据没有更新。