组件
不同于我们常用的Element组件库,示例和代码都在一起,方便直接进行示例代码的复制粘贴。
v8pro的组件示例见示例,示例代码见模块-example示例,我们可以在示例代码中点击运行查看对应的组件效果。
1.布局组件
- viewport 类比为 div ,可以通过设置layout属性调整其内部元素的布局(是一个全屏的盒子,页面上所有的元素必须为他的子级,否则展示不出来;但弹窗可以使用array组件与viewport平级 下 放 window组件)
-
pannel layout设置为row 内部表单元素会按照inline方式进行布局
-
container 容器组件,比如页面需要加载另一个组件,需要一个container容器
-
card
以上几个没看出有啥区别
2.表单组件
-
没有专门的form组件,通常使用pannel进行包裹各表单项,进行表单项的布局,比如水平,垂直或inline
-
表单校验,示例:
js
if (Wb.verify(app.viewport1)) {
Wb.ajax({
url: xpath + '&xaction=save',
comps: app.viewport1,
success() {
Wb.tipDone();
}
});
}
-
required ,tip ,validator 等都可以在属性中定义
-
ajax提交数据时,可以直接通过下面方式获取表单数据
js
Wb.getValue(app.viewport1) //会收集所有含有value的键值对,比如{userName:'admin'}
app.Cid.getValue() //获取某个组件实例的value值。 注意:cid必须是唯一的,比如页面的查询项和弹窗中的表单项不能取相同的cid,否则无法获取到值
Wb.apply({},Wb.getValue(app.viewport1)) // apply对象的合并
- 表单重置
js
Wb.reset(app.panel1)
3.表格组件grid
-
1.表格如果需要mock数据,可以在grid组件中配置localData的值
-
2.服务端渲染的表格 可以 url 链接到对应的代码
- 3.增删改查后更新表格数据
js
app.grid1.reload() //会自动调用后端查询接口
- 4.带操作的表格列如果想要生效一定要把array组件的cid改成buttons;一定要把array组件的cid改成columns,否则内容不展示
- 5.grid的toolbar组件如果要展示,必须设置isProperty属性为true,否则会当成表格内容进行渲染,加了后才会向下面这样渲染
- 6.column必须设置的属性 fieldName 相当于element的prop,对应后端返回的字段名称
- 7.text可以设置列标题,width可以设置列宽度,renderer属性可以通过js自定义单元格HTML的输出
render示例一:根据不同状态设置单元格为不同的tag
js
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
js
let pTag = el.addEl('my-link','div')
pTag.innerHTML = this.data.privateStatute // this.data可以获取该行的数据
-
8.设置editable属性为true可以设置表格为可编辑,可编辑表格必须作为对应的column设置editor属性,如果未设置editor属性那么该列仍然不可编辑。
-
9.表格添加行
js
app.editableGrid.addRecord();
- 10.实操中常用的属性
- data 获取显示的数据,和element-ui一致。
注意:如果要实现页面的响应式更新,需要替换一个数组
,单纯的调用数组的变异方法是不生效的,这点有别与vue的列表渲染
示例:
js
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
- selectionData 获取当前选中的行数据
- selections/selectionsData 是数组类型,但是如果没有选中行,是空数组,而非全部
- originData 也可以获取当前选中的行数据,不知道和selectionData有啥区别
js
app.grid1.selections
app.grid1.selectionData
app.grid1.originData
Wb.getActionHint(recs, 'id') //获取选中数据的唯一标识,调用getActionHint方法进行二次确认
delRecords 删除行,直接删掉了,无需刷新列表
4.弹窗组件
-
1.使用的是window组件,设置弹窗的
标题是title属性
,记住不能设置text,text代表的是body里面的内容
,如果设置了text,则下面的子元素会不显示。 -
2.打开关闭弹窗
```js
app.autoResetWin.show();
app.autoResetWin.close();
```
-
3.如果在关闭之前需要执行一定操作,需要配置closeAction为destory,同时添加destory事件
-
4.新增和编辑时打开弹窗,示例
js
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进行赋值的。
js
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方法说明如下:
- 5.弹窗确定事件,比如保存表单数据,关闭弹窗,刷新列表数据
需要设置弹窗的ok事件,示例代码见上面 handleConfirm
-
6.新增和编辑共用一个弹窗
- 6.1可以通过给app赋值一个变量,相当于是全局变量,比如app.isEdit 是否会存在变量污染?
- 6.2新增时清空编辑的数据,数据的值需要和组件的数据一致,统一给undefined并不生效,比如我用的是input和textarea,给空字符串是可以的,示例如下:
jsconst 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 = "新增底稿"
5.tree和select
- 1.数据来源于接口,只需要配置url属性即可
- 2.可以在success事件中对请求回来的数据做处理,比如
js
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中阻止对应节点的选中
js
if (!item.leaf) {
Wb.tip('Please select a leaf node');
return false;
}
-
3.需要手动加上leaf属性,才会展示会文件形式,否则都是文件夹目录
-
4.subTextField可以设置副文本,值为后端对应的字段名称
5.treeSelect只有设置了treePicker {itemsPath:'children'}子级才会正确展示,itemsPath为子级展示的后端字段名称
数据请求
-
Wb.ajax
-
Wb.request
-
Wb.Request.ajax
常用的serveScript
建表语句
js
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中写下面代码
js
//后端
方式1,执行sql语句,参数使用{?USERNAME?}的方式
Wb.sql(`insert into bwf_user (USERNAME,PASSWORD,EMAIL) values ({?USERNAME?},{?PASSWORD?},{?EMAIL?})`)
方式2,
Wb.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.删
js
//方式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.load() //会发送请求
}
});
});
}
handleDel()
3.改
js
//后端
//方式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,否则初始化查询没有数据
js
//后端:
//方式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
}
})
注意事项总结: 1.项目中的目录结构为,xpath代表同名目录,所以请求地址可以写成url:xpath+'/update'
2.项目中新增和修改用的是2个弹窗,因为ok事件调用的接口不一样。暂时还不知道怎么在点击事件时给弹窗传递标识(区分是新增还是编辑),后期可以再看看
组件化与模块化
common.js
AMD
CMD
esmodule
1.页面中如何引入一个组件,示例,下面是一个按钮的点击事件
js
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
scope页面结构
log scope
2.怎么给弹窗传递回调函数
main 按钮的点击事件
js
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方法
js
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方法传入实参
js
app.callback(app.number1.value + app.number2.value, this);
效果 点击确定时会弹出数字的总和。
3.加载js 。TODO:无法创建js后缀的文件
路径的表示方式
1. xpath 代表当前路径,具体到文件类型
2.使用的绝对路径,目录用/
Wb.open('m?xwl=myapp/test');
3.相对路径,相对的是项目根目录
url:'./sub'
其他一些用到的
- 1.fireEvent可以调用别的组件的事件
js
app.addDeptBtn.fireEvent('click')
-
2.在多个弹窗组件外面包一层array组件会导致tree控件 contextMenu的点击事件与toolbar的点击事件打开的弹窗元素不一致
-
3.接口调用地址,参数后面拼接的是&xaction
m?xwl=comCheck/checkManagement/service/checkDraftService&xaction=queryDraft
根据实际应用场景进行调整
- 4.设置组件的禁用
js
app.editDeptBtn.setDisabled(true);
- 5.下拉框的数据来源于后端接口,如果直接配置url为serveScript的地址,则每次点击下拉箭头都会发送请求;写在ready中请求接口地址,则会在页面初始化时进行请求一次