在使用O2OA進行項目定製化開發時,我們可以開發新的前端組件(x_component)以擴展O2OA來實現更多的業務。這種新增前端組件或者前端業務的開發通常會配合後端自定義應用實現的服務來完成系統內數據的交互。在當系統默認的界面不符合系統UI/UE設計需要,希望重新設計現有功能的前端展現時,我們也需要新增或者修改原有的組件源碼。
上述要求我們除了使用O2OA平臺的門戶頁面設計達到目標,針對於複雜的前端交互設計,開發屬於自己的x_component也是一個有效的方式。
本文主要介紹幾種前端二次開發的方式,以及與流行前端框架(Vue、React)的集成。
先決條件
-
獲取到O2OA的源碼,參照前面的課程所介紹的內容。
-
有一臺運行的O2OA服務器,作爲開發服務器。
-
開發機安裝了Node.js環境,版本要求16及以上版本。
-
爲了方便開發,最好有您熟悉的前端開發IDE工具,如WebStorm 或 VSCode。但這並不是強制的。本文後續的操作,我們以VSCode爲例。
用VSCode打開O2OA的源碼目錄,並打開終端,輸入以下命令:
npm install -g @o2oa/component-tools
此命令全局安裝了@o2oa/component-tools,用於創建O2OAcomponent。建議每次需要創建component時運行此命令,以獲取最新的更新。
然後進入o2web/source目錄,然後運行npm install。
cd o2web/source
npm install
執行完成後,就可以創建component了。
o2oa原生方式的component
創建component組件
創建component的命令格式是:o2-cmpt create name ,其中的“name”就是要創建的component名稱。如創建一個名爲“custom.homepage”的component。
o2-cmpt create custom.homepage
運行後需要選擇使用什麼框架模式來創建component,目前支持兩種方式:vue3、o2oa原生方式。(對於其它框架如react或者Angular的集成可以查詢官方文檔,有比較詳細的步驟和說明。)
現在我們先介紹o2oa原生方式。選擇“o2_native”然後回車,看到以下界面,就說明創建成功了。
此時就可以在目錄列表中看到“x_component_custom_homepage”這個目錄了。
展開目錄後,看到文件結構是:
$Main/目錄:存放靜態文件資源,包括圖標、css、html模板文件等
lp/目錄:存放國際化語言包,zh-cn.js、en.js
Main.js:component入口js文件。
如果在運行命令時,使用的是windowss的PowerShell,可能會報錯“…… 因爲在此係統上禁止運行腳本……”的錯誤,有兩個辦法解決:1、用管理員權限打開Windows PowerShell,運行 set-executionpolicy remotesigned 命令,然後先擇 Y ,回車即可運行腳本;2、是切換到Command,如下圖:
部署組件
前端的打包部署使用了gulp,爲了能快速部署組件到開發服務器,需要增加一個配置文件:o2web/gulpconfig.js,用於設置部署到服務器的信息。
內容如下:
module.exports = {
"dev": { //"dev"爲配置名稱
'upload': 'sftp', //sftp, ftp, 或 local
'location': 'E:/o2server/servers/webServer/', //本地服務器部署路徑,僅upload爲local時有效
'host': 'px.o2oa.net', //sftp或ftp時的服務器地址
'user': 'root', //sftp或ftp 的登錄用戶
'pass': '********', //sftp或ftp 登錄用戶的密碼
"port": 22, //sftp或ftp 的端口
"remotePath": "/data/o2server/servers/webServer/", //sftp或ftp 的服務器部署路徑
"dest": "dest" //打包後生成的本地路徑
}
};
上面的這個配置表示,如果在打包時傳入"dev"作爲參數的話,會自動打包,並通過sftp部署到px.o2oa.net服務器的“/data/o2server/servers/webServer/”路徑下,這就是正在運行開發服務器的web路徑,所以當我們部署上去後,就可以在開發服務器直接運行component了。
配置完成後,就可以運行打包部署腳本了:
gulp x_component_custom_homepage --ev dev
如果不出意外,運行正確後,出現下面的信息:
此時我們新創建的component就部署到開發服務器上了。
運行組件
然後就可以訪問開發服務器查看custom.homepage的運行情況,有三種方法:
1、通過瀏覽器打開:http://hostaname/x_desktop/index.html?app=custom.homepage
2、通過瀏覽器打開:http://hostaname/x_desktop/app.html?app=custom.homepage
以上兩種方法中的“hostname”替換爲開發服務器地址。
3、通過瀏覽器打開:http://hostaname/x_desktop/index.html,然後用管理員身份登錄,打開主菜單-系統設置
進入應用後選擇“系統部署”,點擊最後的“部署組件”:
然後填入合適的信息
組件名稱:可隨意填寫,但不能與已有組件重名
組件標題:可隨意填寫,此信息其實已經無效,實際的組件標題會自動獲取component的語言包的title數據。
組件路徑:這個必須和我們創建組件時的名稱一致,這個例子中,就是“custom.homepage”
組件圖標:會自動獲取component組件目錄下的appicon.png
是否可見:表示此組件是否會在主菜單中出現。
可訪問列表:可以選擇個人、羣組和角色,設定此組件只有哪些人可訪問,爲空時表示不限制,
拒絕訪問列表:可以選擇個人、羣組和角色,設定此組件哪些人不可訪問,爲空時表示不限制,
當“可訪問列表”和“拒絕訪問列表”都爲空時,表示所有人都可以訪問。
填寫好信息後,點擊“部署組件”。
然後就可以在主菜單中看到這個組件了,點擊就可以打開它。
打開後組件頁面是這個樣子的:
這個就是默認的O2OAcomponent組件,它主要實現了列示當前用戶最新的5個待辦,以及打開日程安排、打開組織管理,啓動流程等樣例功能。
組件源碼介紹
在組件源碼目錄下的$Main/下存放了靜態資源文件,幾個主要文件如下:
appicon.png:作爲組件在平臺主菜單中顯示的圖標
default/style.css:組件使用的css文件,此文件的css內容僅對當前組件生效
lp/目錄存放語言包文件,包含了zh-cn.js和en.js,其中的title就是組件顯示的標題。
Main.js文件是組件的入口,它定義了一個類,我們簡單介紹一下主要的方法和參數:
//這一行定義了custom.homepage組件是否可以在平臺中打開多個窗口
MWF.xApplication.custom.homepage.options.multitask = false;
//此處定義了MWF.xApplication.custom.homepage.Main類,組件被打開時,會實例化這個類
MWF.xApplication.custom.homepage.Main = new Class({
//它繼承了MWF.xApplication.Common.Main類,這是系統定義的所有組件的基類
Extends: MWF.xApplication.Common.Main,
Implements: [Options, Events],
options: {
"style1": "default",
//此處定義了組件使用的默認樣式,它對應$Main/default目錄,系統會自動加載對應目錄的css和資源
"style": "default",
"name": "custom.homepage", //組件的名稱
"mvcStyle": "style.css", //要加載的css文件名
"icon": "icon.png", //圖標的文件名
"title": MWF.xApplication.custom.homepage.LP.title //組件的標題,從lp/目錄下的語言包獲取
},
//此方法在組件被加載之前運行,此處只是賦值了語言信息
onQueryLoad: function(){
this.lp = MWF.xApplication.custom.homepage.LP;
},
//loadApplication是組件對象加載的入口方法,在此處爲組件創建dom對象
//一個最簡單的組件,只需要實現此方法即可。它是組件中唯一一個必須要實現的方法
//組件的this.conent是一個div容器,它是組件的容器對象,組件的所有dom元素都應該被this.conent包裹
loadApplication: function(callback){
var url = this.path+this.options.style+"/view.html";
this.content.loadHtml(url, {"bind": {"lp": this.lp}, "module": this}, function(){
this.loadTaskView();
}.bind(this));
},
//---------------------------------------------------------------------
//之後所有的方法都是當前組件特有的方法,這需要根據組件的功能自行添加
loadTaskView: function(){
o2.Actions.load("x_processplatform_assemble_surface").TaskAction.listMyPaging(1,5, function(json){
debugger;
this.taskListView.loadHtml(this.path+this.options.style+"/taskView.html", {"bind": {"lp": this.lp, "data": json.data}, "module": this}, function(){
this.doSomething();
}.bind(this));
}.bind(this));
},
doSomething: function(){
},
//通過o2.api獲取平臺api運行環境,然後可以在api文檔鍾查詢具體方法和對象的使用方法
openTask: function(id, e, data){
o2.api.page.openWork(id);
},
openCalendar: function(){
o2.api.page.openApplication("Calendar");
},
openOrganization: function(){
o2.api.page.openApplication("Org");
},
openInBrowser: function() {
this.openInNewBrowser(true);
},
startProcess: function(){
o2.api.page.startProcess();
},
createDocument: function(){
o2.api.page.createDocument();
}
});
接着來看一下 loadApplication 方法做了什麼
loadApplication: function(callback){
//先計算view.html的路徑
var url = this.path+this.options.style+"/view.html";
//然後通過dom對象的loadHtml方法,將view.html的內容,渲染到this.content中。
//在 view.html 內容被加載完成後,通過回調方法,調用loadTaskView方法。
//loadHtml是O2OA爲dom對象的一個擴展方法,它實現了一個簡單的模板引擎。
//第一個參數是要加載的html模板的url
//第二個參數是options對象,其中bind是要綁定到模板的json格式數據,此處我們就是綁定了語言信息
// module是html中的元素要綁定到的組件。
//第三個參數是加載完成後的回調函數。
this.content.loadHtml(url, {"bind": {"lp": this.lp}, "module": this}, function(){
this.loadTaskView();
}.bind(this));
},
那就需要看一下view.html的內容了:
<div class="hello">
<img class="logo" alt="O2OA logo" src="../x_component_Empty/$Main/default/icons/o2logo.png">
<h1>{{ $.lp.welcome }}</h1>
<p>
For more O2OA development document,<br>
check out the
<a href="https://www.o2oa.net/develop.html" target="_blank" rel="noopener">O2OA develop documentation</a>.
</p>
<div class="task" data-o2-element="taskListView"></div>
</div>
其中的{{$.xxx}}的內容引用了綁定過來的bind信息。“$”就是綁定的數據(前面所說的bind的數據)。
其中data-o2-element屬性是給這個dom對象一個名稱,這樣我們就可以在組件腳本中,通過this.[名稱]獲取到這個dom對象了。這個例子中,就是this.taskListView 了。
在view.html加載完成後,會執行 this.loadTaskView方法,那我們就詳細看看這個方法
loadTaskView: function(){
//o2.Actions對象的load方法可以載入指定上下文的後端服務,並將其轉換爲前端可調用的方法
//所有的平臺restful api,都可以通過http://develop.o2oa.net/x_program_center來查看
//這裏載入了x_processplatform_assemble_surface服務,此服務定義了與流程相關的一組restful api
//TaskAction.listMyPaging方法用於獲取當前用戶的待辦列表
//第一個參數是獲取待辦的頁碼;第二個參數是每頁獲取幾條待辦;第三個參數的是獲取成功的回調函數
o2.Actions.load("x_processplatform_assemble_surface").TaskAction.listMyPaging(1,5, function(json){
//成功回調後,調用 this.taskListView.loadHtml方法,這裏的this.taskListView就是上述view.html
//中data-o2-element屬性爲“taskListView”的div元素。
//通過loadHtml方法載入了taskView.html,並綁定json.data,json.data就是獲取的待辦列表對象數組
this.taskListView.loadHtml(this.path+this.options.style+"/taskView.html", {"bind": {"lp": this.lp, "data": json.data}, "module": this}, function(){
//載入taskView.html後,可以做一些其他事情...
this.doSomething();
}.bind(this));
}.bind(this));
}
再來看看 taskView.html
<h3>{{$.lp.taskListTitle}}</h3>
<br>
<table align="center" class="taskListTable" border="1">
<tr>
<th>{{$.lp.taskTitle}}</th>
<th>{{$.lp.taskProcess}}</th>
<th>{{$.lp.taskTime}}</th>
</tr>
{{each $.data}}
<tr>
<td><a href="#" data-o2-events="click:openTask:{{$.work}}">{{$.title}}</a></td>
<td>{{$.processName}}</td>
<td>{{$.startTime}}</td>
</tr>
{{end each}}
</table>
<br>
<button data-o2-events="click:openCalendar">{{$.lp.openCalendar}}</button>
<button data-o2-events="click:openOrganization">{{$.lp.openOrganization}}</button>
<button data-o2-events="click:startProcess">{{$.lp.startProcess}}</button>
<br>
<button data-o2-events="click:openInBrowser">{{$.lp.openInBrowser}}</button>
這裏有一個 {{each $.data}} 和 {{end each}},這代表循環$.data數據,並處理內部的html內容。$.data必須是一個可迭代的數據。
data-o2-events自定義屬性用於綁定事件,屬性值中,如果有多個事件綁定,使用分號“;”分隔,每個事件綁定的格式爲:“事件名:方法:[參數1]:[參數2]...”,在loadHtml時,將component組件綁定到了“module”屬性,所以這裏的“方法”需要在component組件中定義。如: data-o2-events="click:openCalendar",在Main.js中就定義了這一個方法,用於打開日程安排組件。
openCalendar: function(){
layout.openApplication(null, "Calendar");
}
組件修改
接着來修改組件內容,一般情況首先要修改的是組件標題,所以我們打開語言包,修改title內容:
//zh-cn.js
MWF.xApplication.custom.homepage.LP = {
"title": "我的主頁",
"welcome": "Welcome to O2OA Component",
"taskListTitle": "此處列出您的5個最新待辦",
"taskTitle": "標題",
"taskProcess": "流程",
"taskTime": "到達時間",
"openCalendar": "打開日程管理",
"openOrganization": "打開組織管理",
"startProcess": "啓動流程",
"openInBrowser": "在新瀏覽器窗口中打開"
};
//en.js
MWF.xApplication.custom.homepage.LP = {
"title": "MyHomepage",
"welcome": "Welcome to O2OA Component",
"taskListTitle": "Here are your top 5 task",
"taskTitle": "Title",
"taskProcess": "Process",
"taskTime": "Time",
"openCalendar": "Open Calendar",
"openOrganization": "Open Organization",
"startProcess": "Start Process",
"openInBrowser": "Open In Browser"
};
保存後,再次運行部署命令:
gulp x_component_custom_homepage --ev dev
運行完成後在刷新瀏覽器就可以看到修改後的效果了。
每次修改後運行部署命令,是令人不愉快的,所以我們可以運行以下命令,來監聽文件變化,並自動部署:
gulp watch --src x_component_custom_homepage --ev dev
之後每次修改組件的任何文件時,就會自動部署到開發環境“dev”了。
至此,native類型的component的框架搭建好了。
vue3類型的component
O2OA支持使用vue3創建component
創建組件
我們再次打開終端,到o2web/source目錄,運行創建component命令,創建一個名爲“custom.workplace”的component。
o2-cmpt create custom.workplace
然後選擇“vue3”,會自動創建基於vue3的component。在安裝完部分依賴之後,會詢問幾個問題:
1、需要輸入開發服務器的hosts
2、需要輸入開發服務器的center服務端口,默認是80
3、需要輸入開發服務器的web服務端口,默認是80
4、確認開發服務器是否使用https,默認爲 否(N)
其中開發服務器我們使用 px.o2oa.net, 其他選項都保持默認即可。
看到以下界面,說明component創建完成。
然後選擇“vue3”,會自動創建基於vue3的component。在安裝完部分依賴之後,會詢問幾個問題:
1、需要輸入開發服務器的hosts
2、需要輸入開發服務器的center服務端口,默認是80
3、需要輸入開發服務器的web服務端口,默認是80
4、確認開發服務器是否使用https,默認爲 否(N)
其中開發服務器我們使用 px.o2oa.net, 其他選項都保持默認即可。
看到以下界面,說明component創建完成。
此時就可以在目錄列表中看到“x_component_custom_workplace”這個目錄了。
展開目錄後,就可以看到一個標準的vue應用的文件結構。其中有o2.config.js文件內容如下:
module.exports = {
"server": {
"host": "develop.o2oa.net",
"port": "80",
"httpPort": "80",
"https": false
}
}
這裏的內容就是在創建component時詢問的四個問題答案,它主要用於在啓動本地開發服務器時,確定要連接的後端服務地址。我們可以隨時修改這個配置,來連接不同的O2OA開發服務器。
在看到public/$Main/和public/lp/目錄,這和原生component的$Main目錄和lp目錄內容完全一致。
運行組件
vue類型的component,是可以運行在本地開發服務器上的,所以我們只需要在組件目錄下運行npm run serve即可
cd x_component_custom_workplace
npm run serve
運行後,會啓動本地開發服務器,並自動打開瀏覽器,訪問。
添加路由插件
我們還可以爲vue component添加vue router插件,這和所有vue應用一樣,可以通過vue ui添加,也可以使用命令:vue add router.
如果有源碼有未提交的commit,會提示,建議先提交後再安裝插件。這裏我們選擇y,繼續安裝。
安裝一些依賴包後,會詢問是否使用histroy模式,此處我們選擇 n ,使用 hash 模式。
接着安裝完成,我們可以看到src目錄下,增加了router/index.js和view/目錄下的兩個視圖。
重新啓動本地開發服務器:npm run serve,可以在瀏覽器中看到效果:
修改組件
此時再修改組件內容,如修改src/components/O2Task.vue文件,中的create()方法,將獲取5個待辦,改獲取10個待辦:
...
<script>
...
created(){
//此處將第二個參數從5改爲10
o2.Actions.load("x_processplatform_assemble_surface").TaskAction.V2ListPaging(1, 10, null).then((json)=>{
this.$data.taskList = json.data;
});
}
...
</script>
...
保存文件後,本地服務器會自動編譯,瀏覽器會自動體現修改的內容。
這樣我們就可以方便的開發component組件了。
集成自定義應用
部署組件
此時組件都是在本地服務器運行,要部署到服務器的方法和native類型的組件是一樣的。在確保存在了gulpconfig.js文件,並正確配置後,在o2web/目錄下運行gulp命令:
gulp x_component_custom_workplace --ev dev
就可以將組件部署到服務器了。
可以和native組件一樣的方式,訪問custom.workplace組件了。
React類型的component
O2OA支持使用React創建component組件,其方法與Vue3的組件非常類似。
創建組件
我們再次打開終端,到o2web/source目錄,運行創建component命令,創建一個名爲“custom.liveplace”的component。
o2-cmpt create custom.liveplace
然後選擇“react”,就會自動創建基於React的component。在安裝完部分依賴之後,同樣會詢問幾個問題:
1、需要輸入開發服務器的hosts
2、需要輸入開發服務器的center服務端口,默認是80
3、需要輸入開發服務器的web服務端口,默認是80
4、確認開發服務器是否使用https,默認爲 否(N)
其中開發服務器我們使用 px.o2oa.net, 其他選項都保持默認即可。
看到以下界面,說明component創建完成。(React應用創建過程較長,需要耐心等待一下)
此時就可以在目錄列表中看到“x_component_custom_liveplace”這個目錄了。
展開目錄後,就可以看到一個標準的React應用的文件結構。其中有o2.config.js文件內容如下:
module.exports = {
"server": {
"host": "develop.o2oa.net",
"port": "80",
"httpPort": "80",
"https": false
}
}
這裏的內容就是在創建component時詢問的四個問題答案,它主要用於在啓動本地開發服務器時,確定要連接的後端服務地址。我們可以隨時修改這個配置,來連接不同的O2OA開發服務器。
再看到public/$Main/和public/lp/目錄,這和原生component的$Main目錄和lp目錄內容完全一致。
運行組件
React類型的component,是可以運行在本地開發服務器上的,所以我們只需要在組件目錄下運行npm run start即可
cd x_component_custom_liveplace
npm run start
運行後,會啓動本地開發服務器,並自動打開瀏覽器,訪問。
此時可以修改組件源碼,這裏我們先修改一下歡迎信息。
修改中文語言信息:打開組件源碼目錄的public/lp/zh-cn.js,修改一下welcome:
MWF.xApplication.custom.liveplace.LP = {
"title": "custom.liveplace",
"welcome": "歡迎使用O2OA和React!",
"taskListTitle": "此處列出您的5個最新待辦",
"taskTitle": "標題",
"taskProcess": "流程",
"taskTime": "到達時間",
"openCalendar": "打開日程管理",
"openOrganization": "打開組織管理",
"startProcess": "啓動流程",
"createDocument": "創建信息",
"openInBrowser": "在新瀏覽器窗口中打開"
}
刷新瀏覽器窗口後,就可以看到:
我們可以切換語言信息,來看一下,點擊右上角的個人頭像,點擊“個人設置”,將“語言設置”改爲“English”,“保存個人信息”後,刷新頁面。
就可以看到,組件的語言信息,已經切換過來了:
接着就需要根據需求的內容,進行組件開發了。
部署組件
部署React類型的組件,也非常容易。和上述兩種模式是一樣的。
在確保存在了gulpconfig.js文件,並正確配置後,在o2web/目錄下運行gulp命令:
gulp x_component_custom_liveplace --ev dev
就可以將組件部署到服務器了。
可以和native組件一樣的方式,訪問custom.liveplace組件了。
總結
本文我們講述瞭如何對O2OA進行前端的二次開發,介紹了創建O2OA component的方式,包括原生方式、基於Vue3的方式和基於React的方式。各種O2OA component腳手架工具幫助開發者更方便地搭建了O2OA組件的開發框架。無論那種方式,都完整的保留了O2OA平臺的所有可用API,認證體系,多語言特性、後端服務方法轉換等特性,使開發者可以更好地專注於需要實現的業務。