分享一个自用的表格式流程编辑软件源码

By admin at 2021-11-19 • 0人收藏 • 692人看过

image.png

screenshots.gif

有时候客户需要灵活修改部分功能, 于是就需要你每次提需求改代码改功能, 太麻烦了有木有?


有了这款软件只需要你在你的主程序里解析编写好的流程文件, 然后按照一定的逻辑执行.

这个是我项目里一直再使用的, 给客户在你既定的功能上编写玩的, 然后你的主程序对编好的程序文件进行解析就可以了.


为了能灵活配置自动功能项 , 我把自动完成和自动提示功能用fsys.table来实现, 这样即使客户需要你增加其他功能的流程编辑功能, 只需要修改 流程数据/program.table 这个文件内容即可.

好了, 不藏私, 分享给大家 , 代码难免有一些不简洁的地方, 希望大家能留言共同改进!

表格控件使用了flexcell 6.3.4版本, 库代码在其他帖里找, 表格控件自己去百度.

使用方法:

  1. 先在左侧新增流程

  2. 右侧鼠标点击右键 , 看到好多菜单, 新增10行

  3. 双击[功能名称]那列, 会出现下拉菜单, 选择后, 在双击它后面的那列,继续选择, 后面那些参数会自动填充,

  4. 单击你编好的任意一行, 标题栏会自动改变每列的标题为此列的提示信息.

  5. 没啥可说了, 看代码吧

  6. 有用得到的人吱一声, 当然能留言继续改进更多功能就更赞了.

  7. 对了, 附上program.table文件

    流程数据.zip

import process.mutex;
var mutex = process.mutex("6908C972-AB73-4CDE-9DD7-9B5360C7E544") 
if( mutex.conflict ){
    import win.ui.atom;
    var atom,hwndConflict = win.ui.atom.find("E9CB8A6C-4F72-4B2F-8352-DFB3D083FF04")
    if( hwndConflict ) 
    {
    	win.setForeground(hwndConflict);
    	return;
    }
} 

import win.ui;
/*DSG{{*/
mainForm = win.form(text="流程编辑";right=1323;bottom=613;border="thin")
mainForm.add(
leftList={cls="static";left=7;top=9;right=240;bottom=568;bgcolor=12639424;db=1;dl=1;dt=1;z=1};
rightList={cls="static";left=245;top=9;right=1321;bottom=568;bgcolor=12632256;db=1;dr=1;dt=1;z=2};
splitter={cls="splitter";left=242;top=9;right=247;bottom=568;db=1;dt=1;frame=1;z=7};
保存={cls="button";text="保 存";left=1011;top=575;right=1159;bottom=610;db=1;dr=1;font=LOGFONT(h=-20);z=3};
取消={cls="button";text="取 消";left=1170;top=575;right=1318;bottom=610;db=1;dr=1;font=LOGFONT(h=-20);z=4};
新增={cls="button";text="新 增";left=245;top=575;right=334;bottom=610;db=1;dl=1;font=LOGFONT(h=-20);z=6};
新增名称={cls="edit";left=6;top=575;right=238;bottom=610;border=1;db=1;dl=1;font=LOGFONT(h=-20);z=5}
)
/*}}*/

import win.ui.atom; 
mainForm.atom("E9CB8A6C-4F72-4B2F-8352-DFB3D083FF04"); 

mainForm.splitter.split(mainForm.leftList,mainForm.rightList);

import fsys.table;
import FlexCellLib;
import win.ui.menu;
import string.database;
//定义左右两侧窗口
var flcLeft,flcRight;
//定义重命名前文件名
var preText;
//定义左侧右键删除项
var deleteRow;
//判断是否有需要保存的数据
var saveFlag = false;

flcLeft = FlexCellLib(mainForm.leftList,1,2);
flcRight = FlexCellLib(mainForm.rightList,1,11);

var 方程集 = fsys.table("\流程数据\progrem.table");

/*点击改变文本颜色{{*/
var rowTextColorChange = function(rowIndex){
    if(flcLeft.Rows>1){
    	for(i=1;flcLeft.Rows-1;1){
			if(rowIndex == i){
				flcLeft.Cell(rowIndex,1).ForeColor = 0x0000FF;
				flcLeft.Cell(rowIndex,1).Font.setBold(true);
			}else {
				flcLeft.Cell(i,1).ForeColor = 0x000000;
				flcLeft.Cell(i,1).Font.setBold(false);
			}
		}
    }
}
/*}}*/

/*CVS文件解析{{*/
var csvPara = function(fileName){
    //还原字符串
	var toStr = function(str){
		return string.fromto(str,936,65001)
	}
	var txt = string.load("\流程数据\"+fileName+".csv");
	var strDb = string.database(",");
	var data = strDb.parse(txt); //解析数据
	flcRight.AutoRedraw = false;
	if(flcRight.Rows>1){
		flcRight.Range(1,0,flcRight.Rows-1,0).DeleteByRow();
	}
	for(i=1;#data;1){
		if(i>(flcRight.Rows-1)){
			flcRight.AddItem("",false);
		}
		for(j=1;#data[i];1){
			flcRight.Cell(i,j).Text = toStr(data[i][j]);
		}
		if( data[i][1]=="" or data[i][1]==null ) flcRight.Range(i,1,i,flcRight.Cols-1).BackColor = 0x8273FF;			
	}
	flcRight.AutoRedraw = true;
	flcRight.Refresh();
}
/*}}*/

/*右键菜单{{*/
mainForm.Leftpopmenu = win.ui.popmenu(mainForm);//创建弹出菜单
mainForm.Leftpopmenu.add('移除此流程',function(id){
    if( deleteRow ){
    	var Text = flcLeft.Cell(deleteRow,1).Text;
    	var ret = fsys.delete("\流程数据\"++ Text ++ ".csv");
    	if(ret){
    		flcLeft.RemoveItem(deleteRow)
    		if(deleteRow < (flcLeft.Rows-1)){
    			rowTextColorChange(deleteRow);
    			csvPara(flcLeft.Cell(deleteRow,1).Text);
    		}else {
    			rowTextColorChange(flcLeft.Rows-1);
    			csvPara(flcLeft.Cell(flcLeft.Rows-1,1).Text);
    		}
    		mainForm.msgbox("删除 "++ Text ++" 流程成功!")
    	}else {
    		mainForm.msgboxErr("删除失败!")
    	}
    	deleteRow = null;	
    }
});
flcLeft.MouseUp = function(Button , Shift, x, y){
	if(Button == 2){
		if( flcLeft.HitTest(x,y) ){
			if(saveFlag){
    			var ret = mainForm.msgboxTest("发现有未保存的数据,是否需要返回并手动保存?");
    			if(ret){
    				return;
    			}
    			saveFlag = false;
    		}
    
			deleteRow = flcLeft.HitTest(x,y).Row;
			mainForm.Leftpopmenu.popup();	
		}
    }
}
//--------------------------------------------------------
mainForm.Rightpopmenu = win.ui.popmenu(mainForm);//创建弹出菜单
mainForm.Rightpopmenu.add('新增一行',function(id){
    //在下面输入菜单响应代码
    if(flcLeft.ActiveCell().Text !=""){
    	flcRight.AddItem("",true);
    	flcRight.Range(flcRight.Rows-1,1,flcRight.Rows-1,flcRight.Cols-1).BackColor = 0x8273FF;
    }else {
    	mainForm.msgboxErr('请先从左边中选择一个需要编辑的流程\n或者在左侧创建新的流程!');
    }
});
mainForm.Rightpopmenu.add('新增十行',function(id){
    //在下面输入菜单响应代码
    if(flcLeft.ActiveCell().Text !=""){
        for(i=1;10;1){
        	flcRight.AddItem("",true);
        	flcRight.Range(flcRight.Rows-1,1,flcRight.Rows-1,flcRight.Cols-1).BackColor = 0x8273FF;
        }	
    }else {
    	mainForm.msgboxErr('请先从左边中选择一个需要编辑的流程\n或者在左侧创建新的流程!');
    }
});
mainForm.Rightpopmenu.add('插入行',function(id){
    //在下面输入菜单响应代码
    if(flcRight.ActiveCell().Text != ""){
    	flcRight.Selection.InsertRows();
    	flcRight.Selection.BackColor = 0x8273FF;
    }else {
    	if(flcLeft.ActiveCell().Text !=""){
    		flcRight.AddItem("",true);
    		flcRight.Range(flcRight.ActiveCell().Row-1,1,flcRight.ActiveCell().Row-1,flcRight.Cols-1).BackColor = 0x8273FF;
    	}else {
    		mainForm.msgboxErr('请先从左边中选择一个需要编辑的流程\n或者在左侧创建新的流程!');
    	}
    } 
});
mainForm.Rightpopmenu.add('剪切',function(id){
    //在下面输入菜单响应代码
    flcRight.Selection.CutData();
});
mainForm.Rightpopmenu.add('复制',function(id){
    //在下面输入菜单响应代码
    flcRight.Selection.CopyData();
});
mainForm.Rightpopmenu.add('粘贴',function(id){
    //在下面输入菜单响应代码
    flcRight.Selection.PasteData();
});
mainForm.Rightpopmenu.add('移除选中行',function(id){
    //在下面输入菜单响应代码
    flcRight.Selection.DeleteByRow();
});
mainForm.Rightpopmenu.add('清除选中行内容',function(id){
    //在下面输入菜单响应代码
    flcRight.Selection.ClearText();
});

flcRight.MouseUp = function(Button , Shift, x, y){
	if(Button == 2){
		mainForm.Rightpopmenu.popup();
    }
}

/*}}*/

/*初始化界面{{*/
var setListStyle = function(flcobj){
    flcobj.BackColorScrollBar = 0xD3AF1C;
    flcobj.BackColorActiveCellSel = 0x8273FF;
	flcobj.BackColorFixedSel = 0xCCC899;
	flcobj.BackColorSel = 0x8273FF;
	flcobj.SelectionMode = 1/*_cellSelectionByRow*/;
	flcobj.DefaultFont.setSize(15)
}
var setColStyle = function(flcobj,Col,Width,Align=0xA,Text){
    if(Width != null){
    	flcobj.Column(Col).Width = Width;
    }
	flcobj.Column(Col).Alignment = Align;
	flcobj.Cell(0,Col).Text = Text;
}

//----------------
setListStyle(flcLeft);
setColStyle(flcLeft,0,0);
setColStyle(flcLeft,1,,6,"流程名称");
//---------------
setListStyle(flcRight);
setColStyle(flcRight,1,40,,"");
setColStyle(flcRight,2,400,6/*_cellLeftCenter*/,"备  注");
setColStyle(flcRight,3,150,,"功能名称");
setColStyle(flcRight,4,250,,"方法名称");
setColStyle(flcRight,5,150,,"参数一");
setColStyle(flcRight,6,130,,"参数二");
setColStyle(flcRight,7,130,,"参数三");
setColStyle(flcRight,8,130,,"参数四");
setColStyle(flcRight,9,130,,"参数五");
setColStyle(flcRight,10,130,,"参数六");

flcRight.ComboBox(0).Font.setSize(14);
flcRight.Column(1).CellType = 2/*_cellCheckBox*/;
flcRight.Column(3).CellType = 1/*_cellComboBox*/;
flcRight.Column(4).CellType = 1/*_cellComboBox*/;
flcRight.ComboBox(3).Font.setSize(14);
flcRight.ComboBox(4).Font.setSize(14);
/*}}*/

/*枚举所有流程文件{{*/
try{
	fsys.enum( "/流程数据", "*.csv",
		function(dirpath,dirname){
			flcLeft.AddItem(string.replace(dirname,".csv",""),true);
		}
	);	
}
/*}}*/

/*填充默认数据{{*/
if(flcLeft.Rows>1){
    //默认选中第一个流程
	flcLeft.Range(1,0,1,0).Selected();
	rowTextColorChange(1);
	csvPara(flcLeft.Cell(1,1).Text);
}
/*}}*/

/*重命名流程功能{{*/
flcLeft.DblClick = function(){
    var cellDD = flcLeft.HitTest(flcLeft.MouseRow,flcLeft.MouseCol);
    if( cellDD ){
    	preText = flcLeft.Cell(flcLeft.MouseRow,flcLeft.MouseCol).Text;
    }
}
flcLeft.CellChange = function(Row,Col){
	if(flcLeft.Cell(Row,Col).Text != ""){
		var ret = fsys.rename("\流程数据\"++ preText ++ ".csv","\流程数据\"++ flcLeft.Cell(Row,Col).Text ++ ".csv");	
	}else {
		flcLeft.Cell(Row,Col).Text = preText;
	}	
}
/*}}*/

/*左侧单击功能{{*/
flcLeft.Click = function(){
	if(flcLeft.HitTest(flcLeft.MouseRow,flcLeft.MouseCol)){
		rowTextColorChange(flcLeft.MouseRow);
		csvPara(flcLeft.Cell(flcLeft.MouseRow,1).Text);
	}	
}
/*}}*/

/*拦截左侧列表项改变{{*/
flcLeft.LeaveCell = function(Row,Col,newRow,newCol,Cancel){
	if(saveFlag){
		var ret = mainForm.msgboxTest("发现有未保存的数据,是否需要返回并手动保存?")
		if(ret){
			return newRow,newCol,true;//;
		}
		saveFlag = false;
		//改变颜色和选项
		rowTextColorChange(newRow);
		csvPara(flcLeft.Cell(newRow,1).Text);
	}
}
/*}}*/

/*保存功能{{*/
mainForm.保存.oncommand = function(id,event){
    var fileName = flcLeft.ActiveCell().Text;
    if(fileName != null and fileName != ""){
        saveFlag = false;
    	flcRight.ExportToCSV(fsys.getCurDir()++"\流程数据\"++ fileName ++ ".csv");
    }
}
/*}}*/

/*新增流程{{*/
mainForm.新增.oncommand = function(id,event){
    if(saveFlag){
    	var ret = mainForm.msgboxTest("发现有未保存的数据,是否需要返回并手动保存?");
    	if(ret){
    		return;
    	}
    	saveFlag = false;
    }
    
	if(mainForm.新增名称.text != null and mainForm.新增名称.text != ""){
		var ret = io.exist( "\流程数据\"++ mainForm.新增名称.text ++ ".csv" );
		if(ret == null){
			var ret = string.save("\流程数据\"++ mainForm.新增名称.text ++ ".csv","");
			if(ret){
				flcLeft.AddItem(mainForm.新增名称.text,true)
				flcLeft.Range(flcLeft.Rows-1,0,flcLeft.Rows-1,0).Selected();
				rowTextColorChange(flcLeft.Rows-1);
				flcLeft.Cell(flcLeft.Rows-1,1).EnsureVisible();
				csvPara(mainForm.新增名称.text);
				mainForm.msgbox("创建 "++ mainForm.新增名称.text ++" 流程成功!" )
			}	
		}else {
			mainForm.msgbox( "流程已存在!" );
		}	
	}
}
/*}}*/

/*方程集的数据表处理{{*/
var 获取参数类型和值 = function(tab,...){
    var namesTab = {...};
    if(#namesTab!=0){
        var tempTab = tab;
        for(i=1;#namesTab;1){
        	tempTab = tempTab[namesTab[i]]
        }
        select(tempTab.类型) {
        	case "下拉框" {
        		return 1/*_cellComboBox*/,tempTab.值;	
        	}
        	case "文本框" {
        		return 0/*_cellTextBox*/,tempTab.值;
        	}
        	case "复选框" {
        		return 2/*_cellCheckBox*/,tempTab.值; 
        	}
        	else {
        		return null; 
        	}
        }	
    }
	return null;	
}

var 获取参数提示 = function(tab,...){
    var namesTab = {...};
    if(namesTab[1]=="" and namesTab[2]=="" and namesTab[3]=="" and namesTab[4]=="" and namesTab[5]=="" and namesTab[6]==""){
    	return {"参数一","参数二","参数三","参数四","参数五","参数六";}	
    }
    if(#namesTab!=0){
        var tempTab = tab;
        for(i=1;#namesTab;1){
        	tempTab = tempTab[namesTab[i]]
        }
        return {(tempTab.参数一.提示=="")?"参数一":tempTab.参数一.提示,(tempTab.参数二.提示=="")?"参数二":tempTab.参数二.提示,(tempTab.参数三.提示=="")?"参数三":tempTab.参数三.提示,(tempTab.参数四.提示=="")?"参数四":tempTab.参数四.提示,(tempTab.参数五.提示=="")?"参数五":tempTab.参数五.提示,(tempTab.参数六.提示=="")?"参数六":tempTab.参数六.提示,tempTab.参数一.类型,tempTab.参数二.类型,tempTab.参数三.类型,tempTab.参数四.类型,tempTab.参数五.类型,tempTab.参数六.类型};
    }
	return {"参数一","参数二","参数三","参数四","参数五","参数六","文本框","文本框","文本框","文本框","文本框","文本框"};	
}

var 获取表名 = function(tab,...){
	var namesTab = {...};
	var keyNameTab={};
	if(#namesTab!=0){
        var tempTab = tab;
        for(i=1;#namesTab;1){
        	tempTab = tempTab[namesTab[i]]
        }
        
    	for(k,v in tempTab){
			table.push(keyNameTab,k);
		}
    }else {
    	for(k,v in tab){
			table.push(keyNameTab,k);
		}
    } 
	return keyNameTab;	
}
/*}}*/

/*右侧流程界面智能提示功能{{*/
flcRight.ComboDropDown = function(Row,Col){
    if(Col == 3){
        flcRight.ComboBox(3).Clear();
        var names = 获取表名(方程集);
        for(i=1;#names;1){
        	flcRight.ComboBox(3).AddItem(names[i]); 
        }
    }
    if(Col == 4){
        flcRight.ComboBox(4).Clear();
        var names = 获取表名(方程集,flcRight.Cell(Row,3).Text);
        for(i=1;#names;1){
        	flcRight.ComboBox(4).AddItem(names[i]); 
        }
    }
    var 设置下拉参数和值 = function(name){
    	var ret1,ret2 = 获取参数类型和值(方程集,flcRight.Cell(Row,3).Text,flcRight.Cell(Row,4).Text,name)
        for(i=1;#ret2;1){
        	flcRight.ComboBox(0).AddItem(ret2[i]); 
        }
    }
    
    if(Col == 5){
       设置下拉参数和值("参数一");
    }
    if(Col == 6){
       设置下拉参数和值("参数二");
    }
    if(Col == 7){
       设置下拉参数和值("参数三");
    }
    if(Col == 8){
       设置下拉参数和值("参数四");
    }
    if(Col == 9){
       设置下拉参数和值("参数五");
    }
    if(Col == 10){
       设置下拉参数和值("参数六");
    }
}

flcRight.DblClick = function(){
   	//防止双击到空白处
    if(flcRight.MouseRow != -1){
    	saveFlag = true;
    }
    
	if(flcRight.Cell(flcRight.MouseRow,flcRight.MouseCol).CellType == 6/*_cellDefault*/){
		win.delay(200)
        flcRight.ComboBox(flcRight.MouseCol).DropDown()	
	}elseif(flcRight.Cell(flcRight.MouseRow,flcRight.MouseCol).CellType == 1/*_cellComboBox*/){
		win.delay(200)
        flcRight.ComboBox(0).DropDown()
	}
}

flcRight.CellChange = function(Row,Col){
    if(Col==1){
        if(Row!=0){
        	if( !flcRight.Cell(Row,1).BooleanValue ) flcRight.Range(Row,1,Row,flcRight.Cols-1).BackColor = 0x8273FF;
			else flcRight.Range(Row,1,Row,flcRight.Cols-1).BackColor = (Row%2==0)?flcRight.BackColor2:flcRight.BackColor1;		
        }
    };
	if(Col==3){
		flcRight.Range(Row,4,Row,flcRight.Cols-1).ClearText()
		for(i=4;flcRight.Cols-1;1){
			flcRight.Cell(Row,i).text = "";	
		}
	}
	if(Col==4){
		flcRight.Range(Row,5,Row,flcRight.Cols-1).ClearText();
		if(flcRight.Cell(Row,3).Text!=""){
			var retTable = 获取参数提示(方程集,flcRight.Cell(Row,3).Text,flcRight.Cell(Row,4).Text);
			for(i=5;flcRight.Cols-1;1){
				flcRight.Cell(0,i).Text = retTable[i-4];
			}
			///--------------------------------------------------------------
			var 设置参数类型和值 = function(selcol,name){
				var ret1,ret2 = 获取参数类型和值(方程集,flcRight.Cell(Row,3).Text,flcRight.Cell(Row,4).Text,name)
				if(ret1 != null){
					
					flcRight.Cell(Row,selcol).CellType = ret1;
					if(type(ret2) == type.table){
						flcRight.Cell(Row,selcol).text = ret2[1];
					}else {
						flcRight.Cell(Row,selcol).text = ret2;
					}
				}else {
					flcRight.Cell(Row,selcol).text = "------";
					flcRight.Cell(Row,selcol).CellType = 0/*_cellTextBox*/;
				}
			}
			设置参数类型和值(5,"参数一");
			设置参数类型和值(6,"参数二");
			设置参数类型和值(7,"参数三");
			设置参数类型和值(8,"参数四");
			设置参数类型和值(9,"参数五");
			设置参数类型和值(10,"参数六");	
		}
	}
}
/*}}*/

/*流程界面行点击改变表头提示功能{{*/
flcRight.Click = function(){
	if( flcRight.HitTest(flcRight.MouseRow,flcRight.MouseCol) ){
		var retTable = 获取参数提示(方程集,flcRight.Cell(flcRight.MouseRow,3).Text,flcRight.Cell(flcRight.MouseRow,4).Text);
		for(i=5;flcRight.Cols-1;1){
			flcRight.Cell(0,i).Text = retTable[i-4];
		}
		var styleTab={};
		for(i=5;flcRight.Cols-1;1){
			table.push(styleTab,retTable[i-4+6]);
		}
		//-------------------
		for(i=1;6;1){	
			select(styleTab[i]) {
        		case "下拉框" {
        			flcRight.Cell(flcRight.MouseRow,i+4).CellType = 1;	
        		}
        	}
		}
	}
}
/*}}*/

mainForm.取消.oncommand = function(id,event){
    saveFlag = false;
   	if(flcLeft.ActiveCell().Text){
   		csvPara(flcLeft.ActiveCell().Text);
   	}
}

mainForm.onClose = function(hwnd,message,wParam,lParam){
    if(saveFlag){
    	var ret = mainForm.msgboxTest("发现有未保存的数据,是否需要返回并手动保存?");
    	if(ret){
    		//阻止关闭窗口
    		return true;
    	}
    	saveFlag = false;
    	//继续关闭	
    }
}


mainForm.show();
return win.loopMessage();


4 个回复 | 最后更新于 2021-11-22
2021-11-22   #1

会不会是不知道怎么使用? 感觉没人感兴趣

生成流程后使用方法:

通过string.database解析流程文件(例如 demo.csv)为table表,

然后写一个解析每行数据的函数就可以了.

var doLine = function(lineTab){
	select(lineTab[3]) {//我上面是从第三列开始是首程序函数名,后面是参数1,参数2...
		case "硬件控制" {
			select(lineTab[4]) {
				case "脉冲发生器" {
					select(lineTab[5]) {
						case "启动" {
							启动(...);
						}
						case "停止" {
							停止(...);
						}
						case "单次运行" {
							单次运行(...);
						}
						else {
						}
					}
				}
				case "IO控制" {
					select(lineTab[5]) {
						case "demo1" {
							你的函数(demo1);
						}
						case "demo2" {
							你的函数(demo2);
						}
						else {
						}
					}
				}
				else {
				}
			}
		}
		case "运动控制" {
			select(lineTab[4]) {
				case "相对运动" {
					var 轴号 = 返回轴号(lineTab[5]);
					var 速度 = tonumber(lineTab[6]);
					var 距离 = tonumber(lineTab[7]);
					你的运动函数(轴号,速度,距离);
				}
				case ... {
					你的函数(...)
				}
				else {
				}
			}
		}
		case "方法调用" {
			...
		}
		else {
		}
	}
}


2021-11-24   #2

回复#1 @admin :

没仔细看,看你这else都是空的,不用select,直接遍历表去执行预配置的方法表可能更方便   list[i]  ==>   isFunc(funcTab[[list[i]]])  and funcTab[[list[i]]]()

2021-11-24   #3

2021-11-24   #4

回复#2 @nlysh007 :

这样写会限制功能名,用case是可能有更多函数明情况,而且不需要同名指定。可能实际使用更灵活一点。

登录后方可回帖

登 录
信息栏
公告:
私人站, 专注分享, 可在分享中适当提问, 但谢绝纯提问, 否则不再提醒一律删帖, 谢谢合作!



本站域名:HtmLayout.Cn
aardio可以快速开发上位机,本站主要记录了学习过程中遇到的问题和解决办法及aardio代码分享

这里主要专注于aardio学习交流和经验分享.
纯私人站,当笔记本用的,学到哪写到哪.

Aardio 官方站:Aardio官方
Aardio最新功能:Aardio官方更新日志
本 站 主 站:Stm32cube中文网
Sciter中文在线文档Sciter在线学习文档
空间赞助:才仁机械
Loading...