版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
SAPERP:SAPUI5用戶界面設(shè)計(jì)與開(kāi)發(fā)1SAPUI5簡(jiǎn)介1.1SAPUI5概述SAPUI5,或稱為SAPFiori,是一個(gè)基于HTML5的用戶界面開(kāi)發(fā)框架,由SAP公司開(kāi)發(fā)。它提供了一套豐富的UI控件庫(kù),允許開(kāi)發(fā)者創(chuàng)建美觀、響應(yīng)式且功能強(qiáng)大的企業(yè)級(jí)應(yīng)用。SAPUI5的核心優(yōu)勢(shì)在于其與SAP后端系統(tǒng)的無(wú)縫集成,使得開(kāi)發(fā)者能夠輕松地訪問(wèn)SAP的業(yè)務(wù)數(shù)據(jù)和服務(wù)。1.1.1SAPUI5的架構(gòu)SAPUI5采用MVC(Model-View-Controller)架構(gòu)模式,這有助于開(kāi)發(fā)者將數(shù)據(jù)模型、用戶界面和業(yè)務(wù)邏輯分離,從而提高代碼的可維護(hù)性和可擴(kuò)展性。1.1.2SAPUI5的開(kāi)發(fā)工具SAPUI5的開(kāi)發(fā)通常使用SAPWebIDE或者任何支持HTML5和JavaScript的編輯器。此外,SAP提供了SAPUI5SDK,它包含了所有必要的庫(kù)和工具,便于開(kāi)發(fā)者在本地環(huán)境中進(jìn)行開(kāi)發(fā)和測(cè)試。1.2SAPUI5與SAP系統(tǒng)的集成SAPUI5的一大特色是它能夠與SAP后端系統(tǒng)緊密集成,如SAPS/4HANA、SAPNetWeaver等。這種集成是通過(guò)OData協(xié)議實(shí)現(xiàn)的,OData是一種用于訪問(wèn)和操作數(shù)據(jù)的開(kāi)放協(xié)議,支持RESTful風(fēng)格的接口。1.2.1OData服務(wù)的使用在SAPUI5中,開(kāi)發(fā)者可以通過(guò)創(chuàng)建OData模型來(lái)訪問(wèn)SAP后端的數(shù)據(jù)。以下是一個(gè)簡(jiǎn)單的示例,展示如何在SAPUI5應(yīng)用中使用OData模型://在組件的manifest.json文件中定義OData服務(wù)
{
"sap.ui5":{
"models":{
"i18n":{
"type":"sap.ui.model.resource.ResourceModel",
"settings":{
"bundleName":"com.myapp.i18n.messages"
}
},
"mainModel":{
"type":"sap.ui.model.odata.ODataModel",
"uri":"/sap/opu/odata/sap/API_SRV_SRV/"
}
}
}
}在控制器中使用模型:sap.ui.controller("com.myapp.view.Main",{
onInit:function(){
varoModel=newsap.ui.model.odata.ODataModel("/sap/opu/odata/sap/API_SRV_SRV/",true);
this.getView().setModel(oModel);
},
onAfterRendering:function(){
varoModel=this.getView().getModel();
oModel.read("/Products",{
success:function(oData,oResponse){
console.log(oData);
},
error:function(oError){
console.error(oError);
}
});
}
});1.2.2集成示例假設(shè)我們有一個(gè)SAP后端系統(tǒng),其中包含一個(gè)名為/Products的OData服務(wù),該服務(wù)返回一個(gè)產(chǎn)品列表。我們可以使用SAPUI5的List控件來(lái)顯示這些產(chǎn)品:<mvc:View
controllerName="com.myapp.view.Main"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
>
<Listid="productList"items="{/Products}">
<items>
<StandardListItemtitle="{ProductName}"description="{ProductDescription}"/>
</items>
</List>
</mvc:View>1.3SAPUI5開(kāi)發(fā)環(huán)境搭建搭建SAPUI5的開(kāi)發(fā)環(huán)境需要幾個(gè)關(guān)鍵步驟,包括安裝必要的軟件和配置項(xiàng)目結(jié)構(gòu)。1.3.1安裝SAPWebIDESAPWebIDE是SAPUI5開(kāi)發(fā)的首選工具,它提供了豐富的功能,如代碼編輯、調(diào)試、預(yù)覽和部署。要安裝SAPWebIDE,可以訪問(wèn)SAP的官方網(wǎng)站下載并安裝。1.3.2創(chuàng)建SAPUI5項(xiàng)目在SAPWebIDE中,可以通過(guò)以下步驟創(chuàng)建一個(gè)新的SAPUI5項(xiàng)目:打開(kāi)SAPWebIDE。選擇File>New>Project。在NewProject對(duì)話框中,選擇SAPUI5Application。輸入項(xiàng)目名稱和描述,然后點(diǎn)擊Finish。1.3.3配置SAPUI5應(yīng)用創(chuàng)建項(xiàng)目后,需要在manifest.json文件中配置應(yīng)用的元數(shù)據(jù)和依賴。例如,定義應(yīng)用的名稱、版本、依賴的庫(kù)和模型:{
"sap.app":{
"id":"com.myapp",
"type":"application",
"applicationVersion":{
"version":"1.0.0"
}
},
"sap.ui5":{
"rootView":{
"viewName":"com.myapp.view.Main",
"type":"XML",
"async":true
},
"dependencies":{
"libs":{
"sap.m":{},
"sap.ui.core":{}
}
},
"models":{
"i18n":{
"type":"sap.ui.model.resource.ResourceModel",
"settings":{
"bundleName":"com.myapp.i18n.messages"
}
}
}
}
}1.3.4運(yùn)行SAPUI5應(yīng)用在SAPWebIDE中,可以通過(guò)點(diǎn)擊Run按鈕來(lái)預(yù)覽和測(cè)試SAPUI5應(yīng)用。此外,也可以使用SAPUI5DevelopmentServer來(lái)運(yùn)行應(yīng)用,這有助于在本地環(huán)境中進(jìn)行調(diào)試和測(cè)試。通過(guò)遵循上述步驟,開(kāi)發(fā)者可以快速搭建SAPUI5的開(kāi)發(fā)環(huán)境,并開(kāi)始創(chuàng)建和測(cè)試企業(yè)級(jí)應(yīng)用。SAPUI5的強(qiáng)大功能和與SAP后端系統(tǒng)的緊密集成,使其成為開(kāi)發(fā)SAP相關(guān)應(yīng)用的理想選擇。2SAPUI5基礎(chǔ)2.1SAPUI5項(xiàng)目創(chuàng)建在開(kāi)始SAPUI5項(xiàng)目開(kāi)發(fā)之前,首先需要?jiǎng)?chuàng)建一個(gè)項(xiàng)目框架。SAPUI5項(xiàng)目通?;贛VC(Model-View-Controller)架構(gòu)模式構(gòu)建,這有助于分離數(shù)據(jù)模型、視圖和控制器,使得代碼更加模塊化和易于維護(hù)。2.1.1創(chuàng)建項(xiàng)目使用SAPWebIDE或VSCode創(chuàng)建項(xiàng)目:打開(kāi)SAPWebIDE或VSCode。選擇“新建項(xiàng)目”。選擇SAPUI5作為項(xiàng)目類型。配置項(xiàng)目:設(shè)置項(xiàng)目名稱。選擇項(xiàng)目模板,例如“SAPUI5應(yīng)用”。確定項(xiàng)目位置。項(xiàng)目結(jié)構(gòu):webapp:包含SAPUI5應(yīng)用的所有文件。manifest.json:定義應(yīng)用的元數(shù)據(jù)和配置。index.html:應(yīng)用的入口文件。Component.js:定義應(yīng)用的組件和初始化邏輯。view:存放視圖文件。controller:存放控制器文件。model:存放數(shù)據(jù)模型文件。2.1.2示例代碼//Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/Device",
"myapp/model/models"
],function(UIComponent,Device,models){
"usestrict";
returnUIComponent.extend("myapp.Component",{
metadata:{
manifest:"json"
},
init:function(){
UICtotype.init.apply(this,arguments);
this.setModel(models.createDeviceModel(),"device");
}
});
});2.2SAPUI5控件基礎(chǔ)SAPUI5提供了豐富的UI控件,用于構(gòu)建響應(yīng)式和用戶友好的界面。這些控件遵循SAPFiori設(shè)計(jì)原則,確保一致性和易用性。2.2.1常用控件Button:用于觸發(fā)事件或動(dòng)作。Label:顯示靜態(tài)文本。Input:用戶輸入數(shù)據(jù)。Table:顯示和操作數(shù)據(jù)集。List:顯示數(shù)據(jù)列表。Panel:包含其他控件的容器。2.2.2示例代碼<!--index.html-->
<!DOCTYPEhtml>
<html>
<head>
<title>SAPUI5控件示例</title>
<scriptid="sap-ui-bootstrap"
src="/1.97.2/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-async="true"
data-sap-ui-oninit="myapp.init"
data-sap-ui-compatversion="edge"
data-sap-ui-resourceroots='{"myapp":"./"}'>
</script>
</head>
<bodyclass="sapUiBody"role="application">
<divid="content"></div>
</body>
</html>//myapp.init.js
sap.ui.getCore().attachInit(function(){
sap.ui.xmlView({
viewName:"myapp.view.App",
type:"XML"
}).placeAt("content");
});<!--view/App.view.xml-->
<mvc:View
controllerName="myapp.controller.App"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:html="/1999/xhtml"
xmlns:local="myapp"
xmlns:custom="myapp.custom"
xmlns:library="myapp.library"
xmlns:common="mon"
xmlns:chart="sap.viz.ui5.controls"
xmlns:form="sap.ui.layout.form"
xmlns:table="sap.ui.table"
xmlns:semantic="sap.m.semantic"
xmlns:semantic2="sap.m.semantic2"
xmlns:semantic3="sap.m.semantic3"
xmlns:semantic4="sap.m.semantic4"
xmlns:semantic5="sap.m.semantic5"
xmlns:semantic6="sap.m.semantic6"
xmlns:semantic7="sap.m.semantic7"
xmlns:semantic8="sap.m.semantic8"
xmlns:semantic9="sap.m.semantic9"
xmlns:semantic10="sap.m.semantic10"
xmlns:semantic11="sap.m.semantic11"
xmlns:semantic12="sap.m.semantic12"
xmlns:semantic13="sap.m.semantic13"
xmlns:semantic14="sap.m.semantic14"
xmlns:semantic15="sap.m.semantic15"
xmlns:semantic16="sap.m.semantic16"
xmlns:semantic17="sap.m.semantic17"
xmlns:semantic18="sap.m.semantic18"
xmlns:semantic19="sap.m.semantic19"
xmlns:semantic20="sap.m.semantic20"
xmlns:semantic21="sap.m.semantic21"
xmlns:semantic22="sap.m.semantic22"
xmlns:semantic23="sap.m.semantic23"
xmlns:semantic24="sap.m.semantic24"
xmlns:semantic25="sap.m.semantic25"
xmlns:semantic26="sap.m.semantic26"
xmlns:semantic27="sap.m.semantic27"
xmlns:semantic28="sap.m.semantic28"
xmlns:semantic29="sap.m.semantic29"
xmlns:semantic30="sap.m.semantic30"
xmlns:semantic31="sap.m.semantic31"
xmlns:semantic32="sap.m.semantic32"
xmlns:semantic33="sap.m.semantic33"
xmlns:semantic34="sap.m.semantic34"
xmlns:semantic35="sap.m.semantic35"
xmlns:semantic36="sap.m.semantic36"
xmlns:semantic37="sap.m.semantic37"
xmlns:semantic38="sap.m.semantic38"
xmlns:semantic39="sap.m.semantic39"
xmlns:semantic40="sap.m.semantic40"
xmlns:semantic41="sap.m.semantic41"
xmlns:semantic42="sap.m.semantic42"
xmlns:semantic43="sap.m.semantic43"
xmlns:semantic44="sap.m.semantic44"
xmlns:semantic45="sap.m.semantic45"
xmlns:semantic46="sap.m.semantic46"
xmlns:semantic47="sap.m.semantic47"
xmlns:semantic48="sap.m.semantic48"
xmlns:semantic49="sap.m.semantic49"
xmlns:semantic50="sap.m.semantic50"
xmlns:semantic51="sap.m.semantic51"
xmlns:semantic52="sap.m.semantic52"
xmlns:semantic53="sap.m.semantic53"
xmlns:semantic54="sap.m.semantic54"
xmlns:semantic55="sap.m.semantic55"
xmlns:semantic56="sap.m.semantic56"
xmlns:semantic57="sap.m.semantic57"
xmlns:semantic58="sap.m.semantic58"
xmlns:semantic59="sap.m.semantic59"
xmlns:semantic60="sap.m.semantic60"
xmlns:semantic61="sap.m.semantic61"
xmlns:semantic62="sap.m.semantic62"
xmlns:semantic63="sap.m.semantic63"
xmlns:semantic64="sap.m.semantic64"
xmlns:semantic65="sap.m.semantic65"
xmlns:semantic66="sap.m.semantic66"
xmlns:semantic67="sap.m.semantic67"
xmlns:semantic68="sap.m.semantic68"
xmlns:semantic69="sap.m.semantic69"
xmlns:semantic70="sap.m.semantic70"
xmlns:semantic71="sap.m.semantic71"
xmlns:semantic72="sap.m.semantic72"
xmlns:semantic73="sap.m.semantic73"
xmlns:semantic74="sap.m.semantic74"
xmlns:semantic75="sap.m.semantic75"
xmlns:semantic76="sap.m.semantic76"
xmlns:semantic77="sap.m.semantic77"
xmlns:semantic78="sap.m.semantic78"
xmlns:semantic79="sap.m.semantic79"
xmlns:semantic80="sap.m.semantic80"
xmlns:semantic81="sap.m.semantic81"
xmlns:semantic82="sap.m.semantic82"
xmlns:semantic83="sap.m.semantic83"
xmlns:semantic84="sap.m.semantic84"
xmlns:semantic85="sap.m.semantic85"
xmlns:semantic86="sap.m.semantic86"
xmlns:semantic87="sap.m.semantic87"
xmlns:semantic88="sap.m.semantic88"
xmlns:semantic89="sap.m.semantic89"
xmlns:semantic90="sap.m.semantic90"
xmlns:semantic91="sap.m.semantic91"
xmlns:semantic92="sap.m.semantic92"
xmlns:semantic93="sap.m.semantic93"
xmlns:semantic94="sap.m.semantic94"
xmlns:semantic95="sap.m.semantic95"
xmlns:semantic96="sap.m.semantic96"
xmlns:semantic97="sap.m.semantic97"
xmlns:semantic98="sap.m.semantic98"
xmlns:semantic99="sap.m.semantic99"
xmlns:semantic100="sap.m.semantic100"
xmlns:semantic101="sap.m.semantic101"
xmlns:semantic102="sap.m.semantic102"
xmlns:semantic103="sap.m.semantic103"
xmlns:semantic104="sap.m.semantic104"
xmlns:semantic105="sap.m.semantic105"
xmlns:semantic106="sap.m.semantic106"
xmlns:semantic107="sap.m.semantic107"
xmlns:semantic108="sap.m.semantic108"
xmlns:semantic109="sap.m.semantic109"
xmlns:semantic110="sap.m.semantic110"
xmlns:semantic111="sap.m.semantic111"
xmlns:semantic112="sap.m.semantic112"
xmlns:semantic113="sap.m.semantic113"
xmlns:semantic114="sap.m.semantic114"
xmlns:semantic115="sap.m.semantic115"
xmlns:semantic116="sap.m.semantic116"
xmlns:semantic117="sap.m.semantic117"
xmlns:semantic118="sap.m.semantic118"
xmlns:semantic119="sap.m.semantic119"
xmlns:semantic120="sap.m.semantic120"
xmlns:semantic121="sap.m.semantic121"
xmlns:semantic122="sap.m.semantic122"
xmlns:semantic123="sap.m.semantic123"
xmlns:semantic124="sap.m.semantic124"
xmlns:semantic125="sap.m.semantic125"
xmlns:semantic126="sap.m.semantic126"
xmlns:semantic127="sap.m.semantic127"
xmlns:semantic128="sap.m.semantic128"
xmlns:semantic129="sap.m.semantic129"
xmlns:semantic130="sap.m.semantic130"
xmlns:semantic131="sap.m.semantic131"
xmlns:semantic132="sap.m.semantic132"
xmlns:semantic133="sap.m.semantic133"
xmlns:semantic134="sap.m.semantic134"
xmlns:semantic135="sap.m.semantic135"
xmlns:semantic136="sap.m.semantic136"
xmlns:semantic137="sap.m.semantic137"
xmlns:semantic138="sap.m.semantic138"
xmlns:semantic139="sap.m.semantic139"
xmlns:semantic140="sap.m.semantic140"
xmlns:semantic141="sap.m.semantic141"
xmlns:semantic142="sap.m.semantic142"
xmlns:semantic143="sap.m.semantic143"
xmlns:semantic144="sap.m.semantic144"
xmlns:semantic145="sap.m.semantic145"
xmlns:semantic146="sap.m.semantic146"
xmlns:semantic147="sap.m.semantic147"
xmlns:semantic148="sap.m.semantic148"
xmlns:semantic149="sap.m.semantic149"
xmlns:semantic150="sap.m.semantic150"
xmlns:semantic151="sap.m.semantic151"
xmlns:semantic152="sap.m.semantic152"
xmlns:semantic153="sap.m.semantic153"
xmlns:semantic154="sap.m.semantic154"
xmlns:semantic155="sap.m.semantic155"
xmlns:semantic156="sap.m.semantic156"
xmlns:semantic157="sap.m.semantic157"
xmlns:semantic158="sap.m.semantic158"
xmlns:semantic159="sap.m.semantic159"
xmlns:semantic160="sap.m.semantic160"
xmlns:semantic161="sap.m.semantic161"
xmlns:semantic162="sap.m.semantic162"
xmlns:semantic163="sap.m.semantic163"
xmlns:semantic164="sap.m.semantic164"
xmlns:semantic165="sap.m.semantic165"
xmlns:semantic166="sap.m.semantic166"
xmlns:semantic167="sap.m.semantic167"
xmlns:semantic168="sap.m.semantic168"
xmlns:semantic169="sap.m.semantic169"
xmlns:semantic170="sap.m.semantic170"
xmlns:semantic171="sap.m.semantic171"
xmlns:semantic172="sap.m.semantic172"
xmlns:semantic173="sap.m.semantic173"
xmlns:semantic174="sap.m.semantic174"
xmlns:semantic175="sap.m.semantic175"
xmlns:semantic176="sap.m.semantic176"
xmlns:semantic177="sap.m.semantic177"
xmlns:semantic178="sap.m.semantic178"
xmlns:semantic179="sap.m.semantic179"
xmlns:semantic180="sap.m.semantic180"
xmlns:semantic181="sap.m.semantic181"
xmlns:semantic182="sap.m.semantic182"
xmlns:semantic183="sap.m.semantic183"
xmlns:semantic184="sap.m.semantic184"
xmlns:semantic185="sap.m.semantic185"
xmlns:semantic186="sap.m.semantic186"
xmlns:semantic187="sap.m.semantic187"
xmlns:semantic188="sap.m.semantic188"
xmlns:semantic189="sap.m.semantic189"
xmlns:semantic190="sap.m.semantic190"
xmlns:semantic191="sap.m.semantic191"
xmlns:semantic192="sap.m.semantic192"
xmlns:semantic193="sap.m.semantic193"
xmlns:semantic194="sap.m.semantic194"
xmlns:semantic195="sap.m.semantic195"
xmlns:semantic196="sap.m.semantic196"
xmlns:semantic197="sap.m.semantic197"
xmlns:semantic198="sap.m.semantic198"
xmlns:semantic199="sap.m.semantic199"
xmlns:semantic200="sap.m.semantic200"
>
<Pagetitle="SAPUI5控件示例">
<content>
<Buttontext="點(diǎn)擊我"press="onButtonPress"/>
<Labeltext="用戶名"/>
<Inputvalue="JohnDoe"/>
</content>
</Page>
</mvc:View>2.2.3代碼解釋在上述代碼中,我們創(chuàng)建了一個(gè)簡(jiǎn)單的SAPUI5應(yīng)用,包含一個(gè)頁(yè)面(Page),頁(yè)面中有一個(gè)按鈕(Button)和一個(gè)輸入框(Input)。當(dāng)按鈕被點(diǎn)擊時(shí),會(huì)觸發(fā)onButtonPress事件,該事件在控制器中定義。2.3SAPUI5布局與樣式SAPUI5提供了多種布局控件,用于組織和呈現(xiàn)UI元素。同時(shí),SAPUI5支持CSS樣式,允許開(kāi)發(fā)者自定義應(yīng)用的外觀。2.3.1布局控件FlexBox:基于Flexbox布局模型。Grid:基于網(wǎng)格布局模型。VBox/HBox:垂直或水平布局容器。Form:用于創(chuàng)建表單布局。2.3.2示例代碼<!--view/App.view.xml-->
<mvc:View
controllerName="myapp.controller.App"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
>
<Gridcolumns="2">
<items>
<Column>
<Labeltext="用戶名"/>
<Inputvalue="JohnDoe"/>
</Column>
<Column>
<Labeltext="密碼"/>
<Inputtype="password"value="secret"/>
</Column>
</items>
</Grid>
</mvc:View>2.3.3CSS樣式在SAPUI5中,可以通過(guò)在webapp目錄下創(chuàng)建css文件夾并添加.css文件來(lái)應(yīng)用自定義樣式。/*css/styles.css*/
.sapMInput{
width:100%;
}2.3.4代碼解釋在布局示例中,我們使用了Grid控件來(lái)創(chuàng)建一個(gè)兩列的布局,其中包含用戶名和密碼的輸入框。通過(guò)在Grid中定義Column3數(shù)據(jù)綁定與模型3.1SAPUI5數(shù)據(jù)綁定概念在SAPUI5中,數(shù)據(jù)綁定是一種核心機(jī)制,用于在視圖(View)和模型(Model)之間建立動(dòng)態(tài)連接。這意味著視圖中的控件可以自動(dòng)從模型中讀取數(shù)據(jù),或者將用戶輸入的數(shù)據(jù)寫(xiě)回到模型中,而無(wú)需編寫(xiě)額外的代碼來(lái)處理數(shù)據(jù)的讀取和寫(xiě)入。數(shù)據(jù)綁定使得界面能夠?qū)崟r(shí)反映數(shù)據(jù)的變化,提高了應(yīng)用程序的響應(yīng)性和用戶體驗(yàn)。3.1.1原理數(shù)據(jù)綁定基于觀察者模式(ObserverPattern),模型中的數(shù)據(jù)變化會(huì)自動(dòng)通知到所有綁定到該數(shù)據(jù)的視圖控件,從而觸發(fā)視圖的更新。SAPUI5支持多種數(shù)據(jù)綁定類型,包括:?jiǎn)蜗蚪壎ǎ簲?shù)據(jù)從模型流向視圖,但不能從視圖寫(xiě)回模型。雙向綁定:數(shù)據(jù)可以在模型和視圖之間雙向流動(dòng),適用于表單輸入等場(chǎng)景。相對(duì)綁定:數(shù)據(jù)綁定基于模型中的相對(duì)路徑,允許在模型數(shù)據(jù)結(jié)構(gòu)中進(jìn)行導(dǎo)航。3.1.2示例假設(shè)我們有一個(gè)模型,其中包含員工信息,我們想要在視圖中顯示員工的名字。以下是一個(gè)使用SAPUI5數(shù)據(jù)綁定的簡(jiǎn)單示例://在Controller中定義模型
sap.ui.getCore().setModel(newsap.ui.model.json.JSONModel({
employee:{
name:"張三",
position:"軟件工程師"
}
}),"employeeModel");
//在視圖中綁定數(shù)據(jù)
<mvc:Viewxmlns:mvc="sap.ui.core.mvc"controllerName="com.example.controller.EmployeeController">
<Labeltext="員工姓名:"/>
<Inputvalue="{/employee/name}"/>
</mvc:View>在這個(gè)例子中,/employee/name是模型中的路徑,指向員工的名字。Input控件通過(guò)value屬性綁定到這個(gè)路徑,因此會(huì)顯示模型中員工的名字。3.2使用OData模型OData(OpenDataProtocol)模型是SAPUI5中用于與后端OData服務(wù)進(jìn)行通信的模型。它支持從后端服務(wù)讀取、更新、創(chuàng)建和刪除數(shù)據(jù)。OData模型能夠自動(dòng)處理數(shù)據(jù)的分頁(yè)、排序、過(guò)濾和擴(kuò)展,使得前端開(kāi)發(fā)人員可以更專注于UI邏輯,而無(wú)需關(guān)心后端數(shù)據(jù)的復(fù)雜處理。3.2.1原理OData模型通過(guò)元數(shù)據(jù)(Metadata)來(lái)了解后端服務(wù)的數(shù)據(jù)結(jié)構(gòu)。元數(shù)據(jù)描述了服務(wù)中可用的實(shí)體集、屬性、操作等信息。SAPUI5的OData模型會(huì)自動(dòng)下載元數(shù)據(jù),并根據(jù)元數(shù)據(jù)構(gòu)建模型,使得前端可以使用模型API來(lái)操作數(shù)據(jù)。3.2.2示例以下是一個(gè)使用OData模型從后端服務(wù)讀取數(shù)據(jù)的示例://在Controller中定義OData模型
sap.ui.getCore().setModel(newsap.ui.model.odata.ODataModel("/V4/SAPUI5DemoEPMMockData/",true),"odataModel");
//在視圖中綁定數(shù)據(jù)
<mvc:Viewxmlns:mvc="sap.ui.core.mvc"controllerName="com.example.controller.ODataController">
<Listitems="{odataModel>/Products}">
<items>
<StandardListItemtitle="{odataModel>Name}"description="{odataModel>Price}"/>
</items>
</List>
</mvc:View>在這個(gè)例子中,List控件綁定到odataModel模型的/Products路徑,StandardListItem的title和description屬性分別綁定到Name和Price屬性。當(dāng)模型從后端服務(wù)加載數(shù)據(jù)后,List控件會(huì)自動(dòng)顯示所有產(chǎn)品的名稱和價(jià)格。3.3模型與視圖的綁定模型與視圖的綁定是SAPUI5中數(shù)據(jù)綁定的核心。通過(guò)綁定,視圖中的控件可以訪問(wèn)模型中的數(shù)據(jù),而無(wú)需顯式地在代碼中進(jìn)行數(shù)據(jù)讀取和賦值。這種機(jī)制簡(jiǎn)化了UI開(kāi)發(fā),提高了代碼的可維護(hù)性和可讀性。3.3.1原理模型與視圖的綁定通過(guò)在視圖中使用大括號(hào){}來(lái)指定模型路徑。當(dāng)模型數(shù)據(jù)發(fā)生變化時(shí),所有綁定到該數(shù)據(jù)的控件都會(huì)自動(dòng)更新。此外,SAPUI5還提供了綁定上下文(BindingContext)的概念,允許在視圖中使用相對(duì)路徑來(lái)訪問(wèn)模型數(shù)據(jù)。3.3.2示例假設(shè)我們有一個(gè)包含多個(gè)員工的模型,我們想要在視圖中顯示所有員工的信息。以下是一個(gè)使用模型與視圖綁定的示例://在Controller中定義模型
sap.ui.getCore().setModel(newsap.ui.model.json.JSONModel({
employees:[
{name:"張三",position:"軟件工程師"},
{name:"李四",position:"產(chǎn)品經(jīng)理"}
]
}),"employeeModel");
//在視圖中綁定數(shù)據(jù)
<mvc:Viewxmlns:mvc="sap.ui.core.mvc"controllerName="com.example.controller.EmployeeListController">
<Listitems="{employeeModel>/employees}">
<items>
<StandardListItemtitle="{employeeModel>name}"description="{employeeModel>position}"/>
</items>
</List>
</mvc:View>在這個(gè)例子中,List控件綁定到employeeModel模型的/employees路徑,StandardListItem的title和description屬性分別綁定到name和position屬性。當(dāng)模型數(shù)據(jù)發(fā)生變化時(shí),List控件會(huì)自動(dòng)更新顯示的員工信息。通過(guò)以上示例,我們可以看到SAPUI5的數(shù)據(jù)綁定機(jī)制如何簡(jiǎn)化了UI開(kāi)發(fā),使得開(kāi)發(fā)人員可以更專注于業(yè)務(wù)邏輯和用戶體驗(yàn),而無(wú)需過(guò)多關(guān)注數(shù)據(jù)的讀寫(xiě)操作。4視圖與控制器4.1視圖的創(chuàng)建與理解在SAPUI5中,視圖(View)是用戶界面的構(gòu)建塊,它定義了應(yīng)用程序的布局和外觀。視圖可以使用XML視圖或JS視圖創(chuàng)建,兩者都允許你定義UI元素,但以不同的方式處理數(shù)據(jù)綁定和事件處理。4.1.1XML視圖XML視圖使用XML語(yǔ)法來(lái)定義UI元素。下面是一個(gè)簡(jiǎn)單的XML視圖示例:<mvc:View
controllerName="com.myapp.controller.Main"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:core="sap.ui.core"
displayBlock="true"
xmlns:l="sap.ui.layout"
>
<Pagetitle="我的應(yīng)用">
<content>
<Labeltext="歡迎使用SAPUI5!"/>
<Buttontext="點(diǎn)擊我"press="onButtonPress"/>
</content>
</Page>
</mvc:View>在這個(gè)例子中,我們定義了一個(gè)頁(yè)面(Page),包含一個(gè)標(biāo)簽(Label)和一個(gè)按鈕(Button)。controllerName屬性指定了與視圖關(guān)聯(lián)的控制器。4.1.2JS視圖JS視圖使用JavaScript語(yǔ)法來(lái)定義UI元素。下面是一個(gè)等效的JS視圖示例:sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/Page",
"sap/m/Label",
"sap/m/Button"
],function(Controller,Page,Label,Button){
"usestrict";
returnController.extend("com.myapp.view.Main",{
createContent:function(oController){
returnnewPage({
title:"我的應(yīng)用",
content:[
newLabel({
text:"歡迎使用SAPUI5!"
}),
newButton({
text:"點(diǎn)擊我",
press:oController.onButtonPress
})
]
});
}
});
});在這個(gè)JS視圖中,我們使用Controller.extend來(lái)創(chuàng)建一個(gè)視圖,并在createContent方法中定義UI元素。4.2控制器的作用與實(shí)現(xiàn)控制器(Controller)在SAPUI5中負(fù)責(zé)處理視圖中的事件和數(shù)據(jù)綁定。它是一個(gè)JavaScript類,可以訪問(wèn)視圖和模型。4.2.1控制器的實(shí)現(xiàn)下面是一個(gè)簡(jiǎn)單的控制器示例,它處理按鈕的點(diǎn)擊事件:sap.ui.define([
"sap/ui/core/mvc/Controller"
],function(Controller){
"usestrict";
returnController.extend("com.myapp.controller.Main",{
onButtonPress:function(oEvent){
varoSource=oEvent.getSource();
varoView=this.getView();
varoModel=oView.getModel();
//從模型中獲取數(shù)據(jù)
varsMessage=oModel.getProperty("/message");
//更新UI
oView.byId("myLabel").setText(sMessage);
}
});
});在這個(gè)例子中,onButtonPress方法被調(diào)用來(lái)處理按鈕的點(diǎn)擊事件。它首先獲取事件的源對(duì)象,然后獲取與視圖關(guān)聯(lián)的模型。最后,它從模型中讀取數(shù)據(jù),并更新UI中的標(biāo)簽。4.3視圖與控制器的交互視圖和控制器之間的交互主要通過(guò)事件和數(shù)據(jù)綁定來(lái)實(shí)現(xiàn)??刂破骺梢员O(jiān)聽(tīng)視圖中的事件,并在事件觸發(fā)時(shí)執(zhí)行相應(yīng)的處理函數(shù)。同時(shí),控制器也可以通過(guò)數(shù)據(jù)綁定來(lái)更新視圖中的UI元素。4.3.1事件處理在XML視圖中,事件處理函數(shù)可以通過(guò)press等屬性直接綁定到控制器的方法上。例如:<Buttontext="點(diǎn)擊我"press="onButtonPress"/>在JS視圖中,事件處理函數(shù)通常在控制器中定義,并通過(guò)attachEvent方法綁定到UI元素上。4.3.2數(shù)據(jù)綁定數(shù)據(jù)綁定允許UI元素顯示模型中的數(shù)據(jù),并在數(shù)據(jù)改變時(shí)自動(dòng)更新UI。下面是一個(gè)數(shù)據(jù)綁定的示例:<mvc:View
controllerName="com.myapp.controller.Main"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:core="sap.ui.core"
>
<Pagetitle="我的應(yīng)用">
<content>
<Labeltext="{/message}"/>
</content>
</Page>
</mvc:View>在這個(gè)例子中,Label元素的text屬性被綁定到模型中的/message路徑。當(dāng)模型中的數(shù)據(jù)改變時(shí),標(biāo)簽的文本也會(huì)自動(dòng)更新。4.3.3控制器中的數(shù)據(jù)綁定控制器可以使用bindElement或bindProperty方法來(lái)更新UI元素的數(shù)據(jù)綁定。例如:onButtonPress:function(oEvent){
varoView=this.getView();
varoModel=oView.getModel();
//更新模型中的數(shù)據(jù)
oModel.setProperty("/message","數(shù)據(jù)已更新");
//更新UI中的數(shù)據(jù)綁定
oView.byId("myLabel").bindProperty("text","/message");
}在這個(gè)例子中,控制器首先更新模型中的數(shù)據(jù),然后更新UI元素的數(shù)據(jù)綁定,以確保UI顯示最新的數(shù)據(jù)。通過(guò)以上示例,我們可以看到SAPUI5中視圖與控制器的交互機(jī)制,以及如何使用數(shù)據(jù)綁定和事件處理來(lái)構(gòu)建動(dòng)態(tài)的用戶界面。5路由與導(dǎo)航5.1SAPUI5路由機(jī)制在SAPUI5應(yīng)用中,路由(Route)和導(dǎo)航(Navigation)是實(shí)現(xiàn)應(yīng)用內(nèi)不同視圖(View)之間跳轉(zhuǎn)的關(guān)鍵技術(shù)。路由機(jī)制允許開(kāi)發(fā)者定義應(yīng)用的URL結(jié)構(gòu),并根據(jù)不同的URL參數(shù)加載相應(yīng)的視圖。這種機(jī)制不僅增強(qiáng)了應(yīng)用的可訪問(wèn)性和可維護(hù)性,還使得應(yīng)用能夠更好地適應(yīng)不同的用戶需求和使用場(chǎng)景。5.1.1原理SAPUI5的路由機(jī)制基于sap.m.routing.Router類,它負(fù)責(zé)監(jiān)聽(tīng)URL的變化,并根據(jù)預(yù)定義的路由規(guī)則加載相應(yīng)的視圖。路由規(guī)則通常在應(yīng)用的manifest.json文件中定義,或者在應(yīng)用啟動(dòng)時(shí)動(dòng)態(tài)配置。每個(gè)路由規(guī)則包含一個(gè)模式(pattern),用于匹配URL的一部分,以及一個(gè)目標(biāo)(target),用于指定當(dāng)模式匹配成功時(shí)應(yīng)加載的視圖。5.1.2實(shí)現(xiàn)步驟定義路由規(guī)則:在應(yīng)用的manifest.json文件中定義路由規(guī)則,或者在代碼中使用Router的initialize方法動(dòng)態(tài)配置。創(chuàng)建視圖:為每個(gè)路由規(guī)則創(chuàng)建一個(gè)視圖,通常使用XML視圖或JS視圖。配置目標(biāo):在路由規(guī)則中配置目標(biāo),指定視圖的ID和控制器的名稱。初始化路由器:在應(yīng)用啟動(dòng)時(shí)初始化路由器,開(kāi)始監(jiān)聽(tīng)URL變化。導(dǎo)航到視圖:使用Router的navTo方法導(dǎo)航到指定的視圖。5.2實(shí)現(xiàn)頁(yè)面導(dǎo)航頁(yè)面導(dǎo)航是SAPUI5應(yīng)用中常見(jiàn)的功能,它允許用戶在不同的視圖之間跳轉(zhuǎn)。導(dǎo)航可以通過(guò)按鈕、鏈接或菜單項(xiàng)觸發(fā),當(dāng)用戶點(diǎn)擊這些元素時(shí),應(yīng)用會(huì)根據(jù)預(yù)定義的路由規(guī)則加載相應(yīng)的視圖。5.2.1示例代碼//在控制文件中初始化路由器
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/Router"
],function(Controller,Router){
"usestrict";
returnController.extend("com.example.app.controller.Main",{
onInit:function(){
varoRouter=newRouter({
routes:[
{
pattern:"",
name:"home",
target:"homeView"
},
{
pattern:"detail/{id}",
name:"detail",
target:"detailView"
}
],
targets:{
homeView:{
viewName:"Home",
viewLevel:1
},
detailView:{
viewName:"Detail",
viewLevel:2
}
}
});
oRouter.initialize();
},
onNavigateToDetail:function(oEvent){
varsId=oEvent.getParameter("arguments").id;
this.getRouter().navTo("detail",{id:sId});
}
});
});5.2.2代碼解釋在上述代碼中,我們首先定義了兩個(gè)路由規(guī)則:一個(gè)用于主頁(yè)(home),另一個(gè)用于詳情頁(yè)(detail)。主頁(yè)的路由規(guī)則沒(méi)有模式,這意味著當(dāng)URL不包含任何特定參數(shù)時(shí),將加載主頁(yè)視圖。詳情頁(yè)的路由規(guī)則包含一個(gè)模式"detail/{id}",其中{id}是一個(gè)動(dòng)態(tài)參數(shù),用于從URL中提取特定的ID值。在onNavigateToDetail方法中,我們使用getRouter().navTo方法導(dǎo)航到詳情頁(yè)視圖。navTo方法接受兩個(gè)參數(shù):第一個(gè)參數(shù)是路由規(guī)則的名稱,第二個(gè)參數(shù)是一個(gè)對(duì)象,用于傳遞動(dòng)態(tài)參數(shù)。5.3路由參數(shù)與傳遞在SAPUI5中,路由參數(shù)允許應(yīng)用根據(jù)URL中的信息動(dòng)態(tài)加載數(shù)據(jù)或配置視圖。這在處理具有動(dòng)態(tài)內(nèi)容的頁(yè)面時(shí)非常有用,例如,當(dāng)用戶從主頁(yè)導(dǎo)航到一個(gè)特定產(chǎn)品的詳情頁(yè)時(shí),應(yīng)用可以根據(jù)URL中的產(chǎn)品ID加載相應(yīng)的產(chǎn)品信息。5.3.1示例代碼//控制器中處理路由參數(shù)
sap.ui.define([
"sap/ui/core/mvc/Controller"
],function(Controller){
"usestrict";
returnController.extend("com.example.app.controller.Detail",{
onInit:function(){
varoRouter=this.getOwnerComponent().getRouter();
oRouter.getRoute("detail").attachPatternMatched(this._onObjectMatched,this);
},
_onObjectMatched:function(oEvent){
varoArguments=oEvent.getParameter("arguments");
varsId=oArguments.id;
//假設(shè)有一個(gè)產(chǎn)品數(shù)據(jù)模型
varoModel=this.getView().getModel("productModel");
oModel.read("/products('"+sId+"')",{
success:function(oData){
this.getView().bindElement(oData);
}.bind(this),
error:function(oError){
console.error("Errorloadingproductdata:",oError);
}.bind(this)
});
}
});
});5.3.2代碼解釋在Detail控制器的onInit方法中,我們監(jiān)聽(tīng)了detail路由規(guī)則的PatternMatched事件。當(dāng)用戶導(dǎo)航到詳情頁(yè)時(shí),這個(gè)事件會(huì)被觸發(fā),我們可以在事件處理函數(shù)_onObjectMatched中獲取路由參數(shù)。在_onObjectMatched方法中,我們從事件參數(shù)中提取id值,并使用這個(gè)值從產(chǎn)品數(shù)據(jù)模型中加載特定的產(chǎn)品信息。bindElement方法用于將加載的數(shù)據(jù)綁定到視圖,從而動(dòng)態(tài)更新視圖的內(nèi)容。通過(guò)這種方式,SAPUI5應(yīng)用能夠根據(jù)URL中的參數(shù)動(dòng)態(tài)加載和顯示數(shù)據(jù),提供更加個(gè)性化和響應(yīng)式的用戶體驗(yàn)。6SAPUI5應(yīng)用程序開(kāi)發(fā)6.1應(yīng)用程序架構(gòu)設(shè)計(jì)在設(shè)計(jì)SAPUI5應(yīng)用程序架構(gòu)時(shí),重要的是要遵循模塊化和組件化的原則,以確保代碼的可維護(hù)性和可擴(kuò)展性。SAPUI5采用MVC(Model-View-Controller)架構(gòu)模式,將應(yīng)用程序分為三個(gè)主要部分:Model:負(fù)責(zé)數(shù)據(jù)管理,與后端服務(wù)通信,以及數(shù)據(jù)的本地存儲(chǔ)。View:負(fù)責(zé)用戶界面的呈現(xiàn),使用XML或JS視圖來(lái)定義UI。Controller:負(fù)責(zé)處理用戶交互,控制數(shù)據(jù)流,并更新視圖。6.1.1示例:定義一個(gè)簡(jiǎn)單的Model//在Component.js中定義模型
sap.ui.define([
"sap/ui/core/UIComponent"
],function(UIComponent){
"usestrict";
returnUIComponent.extend("com.example.MyApp.Component",{
metadata:{
models:{
appModel:{
type:"sap.ui.model.json.JSONModel",
uri:"path/to/your/service"
}
}
},
init:function(){
UICtotype.init.apply(this,arguments);
this.setModel(newsap.ui.model.json.JSONModel(),"appModel");
}
});
});6.2模塊化與組件化SAPUI5支持模塊化開(kāi)發(fā),允許將應(yīng)用程序分解為多個(gè)可重用的組件。每個(gè)組件可以有自己的視圖、控制器和模型,這有助于保持代碼的清晰和組織性。6.2.1示例:創(chuàng)建一個(gè)組件//Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/Device",
"com/example/MyApp/model/models"
],function(UIComponent,Device,models){
"usestrict";
returnUIComponent.extend("com.example.MyApp.Component",{
metadata:{
//定義組件的元數(shù)據(jù)
//...
},
init:function(){
UICtotype.init.apply(this,arguments);
this.setModel(models.createAppModel(),"appModel");
//初始化其他組件邏輯
//...
}
});
});6.2.2示例:定義一個(gè)組件的視圖<!--View.xml-->
<mvc:View
controllerName="com.example.MyApp.view.MyView"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:local="com.example.MyApp.view"
>
<App>
<pages>
<Pagetitle="{i18n>title}">
<content>
<Listid="idList"items="{/items}">
<core:items>
<StandardListItemtitle="{name}"description="{description}"/>
</core:items>
</List>
</content>
</Page>
</pages>
</App>
</mvc:View>6.3應(yīng)用程序性能優(yōu)化性能優(yōu)化是SAPUI5應(yīng)用程序開(kāi)發(fā)中的關(guān)鍵環(huán)節(jié)。以下是一些提高性能的策略:延遲加載:只在需要時(shí)加載視圖和控制器,而不是在應(yīng)用程序啟動(dòng)時(shí)加載所有內(nèi)容。數(shù)據(jù)綁定優(yōu)化:使用change事件而不是liveUpdate,以減少不必要的數(shù)據(jù)刷新。資源壓縮:使用壓縮工具如UglifyJS或Terser來(lái)壓縮JS和CSS文件,減少加載時(shí)間。6.3.1示例:實(shí)現(xiàn)延遲加載//Controller.js
sap.ui.define([
"sap/m/StandardListItem",
"sap/ui/model/json/JSONModel"
],function(StandardListItem,JSONModel){
"usestrict";
returnsap.ui.core.mvc.Controller.extend("com.example.MyApp.view.MyView",{
onInit:function(){
varoModel=newJSONModel();
oModel.loadData("path/to/data.json",null,false);
this.getView().setModel(oModel);
}
});
});在這個(gè)例子中,StandardListItem和JSONModel模塊只在控制器初始化時(shí)加載,實(shí)現(xiàn)了延遲加載,從而提高了應(yīng)用程序的啟動(dòng)速度。6.3.2示例:數(shù)據(jù)綁定優(yōu)化<!--View.xml-->
<mvc:View
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:core="sap.ui.core"
>
<Listid="idList"items="{path:'/items',change:'onListChange'}">
<!--使用change事件來(lái)優(yōu)化數(shù)據(jù)綁定-->
</List>
</mvc:View>在這個(gè)視圖中,我們使用change事件來(lái)優(yōu)化數(shù)據(jù)綁定,這意味著只
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度房地產(chǎn)開(kāi)發(fā)精美合同協(xié)議范本(品質(zhì)保障版)3篇
- 2024版幼兒娛樂(lè)場(chǎng)所承包合同條款匯編版
- 二零二五版租賃住房合同糾紛調(diào)解規(guī)范3篇
- 2024版汽車租賃委托付款協(xié)議書(shū)
- 2025年度版權(quán)監(jiān)測(cè)合同標(biāo)的:盜版監(jiān)測(cè)與維權(quán)3篇
- 二零二五版勞動(dòng)合同主體變更與員工培訓(xùn)補(bǔ)貼協(xié)議3篇
- 2024年科技成果轉(zhuǎn)化與合作合同
- 二零二五年度跨境電商金融合同履行與跨境支付服務(wù)3篇
- 二零二五年度生態(tài)環(huán)保庫(kù)房租賃合同3篇
- 二零二五年度房地產(chǎn)項(xiàng)目招投標(biāo)及合同簽訂協(xié)議3篇
- 餐飲行業(yè)智慧餐廳管理系統(tǒng)方案
- 2025年度生物醫(yī)藥技術(shù)研發(fā)與許可協(xié)議3篇
- 電廠檢修安全培訓(xùn)課件
- 殯葬改革課件
- 2024企業(yè)答謝晚宴會(huì)務(wù)合同3篇
- 雙方個(gè)人協(xié)議書(shū)模板
- 車站安全管理研究報(bào)告
- 瑪米亞RB67中文說(shuō)明書(shū)
- 五年級(jí)數(shù)學(xué)(小數(shù)四則混合運(yùn)算)計(jì)算題專項(xiàng)練習(xí)及答案
- 2024年鋼鐵貿(mào)易行業(yè)前景分析:鋼鐵貿(mào)易行業(yè)發(fā)展趨勢(shì)推動(dòng)行業(yè)可持續(xù)發(fā)展
- 節(jié)前物業(yè)安全培訓(xùn)
評(píng)論
0/150
提交評(píng)論