官方walkthough是把OData model配置在manifest.json,通过底层框架加载配置文件实例化模型,我不喜欢这种风格,手写代码实现整个过程,容易理解。
1.通过fiori-tools-proxy提供的proxy-middleware解决跨域
ui5.yaml配置文件如下
Authorization后面用账号:密码转成base64的编码,如果不配置,会弹出账号密码框手动输入账号密码。
# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
specVersion: "4.0"
metadata:
name: demo01
type: application
server:
customMiddleware:
- name: fiori-tools-proxy
afterMiddleware: compression
configuration:
ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted
ui5:
path:
- /resources
- /test-resources
url: https://ui5.sap.com
backend:
- path: /sap
url: http://你的sap ip:port
headers:
Authorization: "Basic 账号:密码转base64的编码"
- name: fiori-tools-appreload
afterMiddleware: compression
configuration:
port: 35729
path: webapp
delay: 300
- name: fiori-tools-preview
afterMiddleware: fiori-tools-appreload
configuration:
component: demo01
ui5Theme: sap_horizon
2.后端OData服务信息

STOCK_TYPSET_GET_ENTITY 方法代码
METHOD stock_typset_get_entity.
**TRY.
*CALL METHOD SUPER->STOCK_TYPSET_GET_ENTITY
* EXPORTING
* IV_ENTITY_NAME =
* IV_ENTITY_SET_NAME =
* IV_SOURCE_NAME =
* IT_KEY_TAB =
** io_request_object =
** io_tech_request_context =
* IT_NAVIGATION_PATH =
** IMPORTING
** er_entity =
** es_response_context =
* .
** CATCH /iwbep/cx_mgw_busi_exception .
** CATCH /iwbep/cx_mgw_tech_exception .
**ENDTRY.
DATA: lo_mon_stock TYPE REF TO /scwm/cl_mon_stock,
lt_huident_r TYPE /scwm/tt_huident_r,
ls_huident_r TYPE /scwm/s_huident_r,
lt_stock_mon TYPE /scwm/tt_stock_mon,
ev_returncode TYPE xfeld.
DATA: ls_data TYPE zss_stock.
READ TABLE it_key_tab INTO DATA(ls_key) WITH KEY name = 'Huident'.
IF sy-subrc = 0.
ls_data-huident = ls_key-value.
CALL FUNCTION 'CONVERSION_EXIT_HUID_INPUT'
EXPORTING
input = ls_data-huident
IMPORTING
output = ls_data-huident.
CREATE OBJECT lo_mon_stock
EXPORTING
iv_lgnum = '1100'.
ls_huident_r-low = ls_data-huident.
ls_huident_r-sign = 'I'.
ls_huident_r-option = 'EQ'.
APPEND ls_huident_r TO lt_huident_r.
CLEAR: ls_huident_r.
CALL METHOD lo_mon_stock->get_physical_stock
EXPORTING
iv_drill_down = abap_false
it_huident_r = lt_huident_r
IMPORTING
et_stock_mon = lt_stock_mon
ev_error = ev_returncode.
READ TABLE lt_stock_mon INTO DATA(ls_mon) INDEX 1.
IF sy-subrc = 0.
MOVE-CORRESPONDING ls_mon TO ls_data.
ENDIF.
er_entity = ls_data.
ENDIF.
ENDMETHOD.
STOCK_TYPSET_GET_ENTITYSET方法代码
METHOD stock_typset_get_entityset.
DATA: lo_mon_stock TYPE REF TO /scwm/cl_mon_stock,
lt_options TYPE /iwbep/t_cod_select_options,
lt_huident_r TYPE /scwm/tt_huident_r,
ls_huident_r TYPE /scwm/s_huident_r,
lt_stock_mon TYPE /scwm/tt_stock_mon,
lt_out TYPE TABLE OF zss_stock,
ls_out TYPE zss_stock,
ev_returncode TYPE xfeld,
lv_charg TYPE mchb-charg.
DATA lv_hu TYPE /scwm/de_huident.
LOOP AT it_filter_select_options INTO DATA(ls_select_options) WHERE property = 'Huident'.
lt_options = ls_select_options-select_options.
CLEAR: ls_select_options.
ENDLOOP.
MOVE-CORRESPONDING lt_options TO lt_huident_r.
LOOP AT lt_huident_r ASSIGNING FIELD-SYMBOL(<fs_hu>).
IF strlen( <fs_hu>-low ) = 12. "处理单元 12位数长度
CALL FUNCTION 'CONVERSION_EXIT_HUID_INPUT'
EXPORTING
input = <fs_hu>-low
IMPORTING
output = <fs_hu>-low.
lv_hu = <fs_hu>-low.
ELSE.
lv_charg = <fs_hu>-low.
ENDIF.
ENDLOOP.
IF lt_huident_r[] IS NOT INITIAL.
IF lv_charg IS INITIAL. "查询条件是处理单元
CREATE OBJECT lo_mon_stock
EXPORTING
iv_lgnum = '1100'.
CALL METHOD lo_mon_stock->get_physical_stock
EXPORTING
iv_drill_down = abap_false
it_huident_r = lt_huident_r
IMPORTING
et_stock_mon = lt_stock_mon
ev_error = ev_returncode.
IF lt_stock_mon IS NOT INITIAL.
LOOP AT lt_stock_mon INTO DATA(ls_mon).
MOVE-CORRESPONDING ls_mon TO ls_out.
WRITE ls_mon-vfdat TO ls_out-vfdat.
ls_out-cat_text = ''.
CASE ls_mon-cat+0(1).
WHEN '0'.
ls_out-cat_text = '非限制'.
WHEN '1'.
ls_out-cat_text = '质检中'.
WHEN '2'.
ls_out-cat_text = '已冻结'.
ENDCASE.
APPEND ls_out TO lt_out.
CLEAR: ls_out,
ls_mon.
ENDLOOP.
ELSE. "无库存的HU,且之前做过入库和出库,通过HU取表仓库任务表的物料和批次
SELECT SINGLE charg INTO lv_charg
FROM /scwm/ordim_c
WHERE vlenr = lv_hu
AND charg <> ''.
ENDIF.
ENDIF.
IF lv_charg IS NOT INITIAL. "查询条件是批次
SELECT a~matnr,b~maktx,a~charg,werks,lgort,
a~clabs,a~cinsm,a~cspem
INTO TABLE @DATA(lt_mchb)
FROM mchb AS a
LEFT JOIN makt AS b
ON a~matnr = b~matnr
WHERE charg = @lv_charg
AND b~spras = @sy-langu
AND ( clabs > 0 OR cinsm > 0 OR cspem > 0 ).
LOOP AT lt_mchb INTO DATA(ls_mchb).
ls_out-huident = lv_hu.
ls_out-matnr = ls_mchb-matnr.
ls_out-maktx = ls_mchb-maktx.
ls_out-charg = ls_mchb-charg.
IF ls_mchb-clabs IS NOT INITIAL.
ls_out-quan = ls_mchb-clabs.
ls_out-cat_text = '非限制'.
ELSEIF ls_mchb-cinsm IS NOT INITIAL.
ls_out-quan = ls_mchb-cinsm.
ls_out-cat_text = '质检中'.
ELSEIF ls_mchb-cspem IS NOT INITIAL.
ls_out-quan = ls_mchb-cspem.
ls_out-cat_text = '已冻结'.
ENDIF.
SELECT SINGLE meins INTO ls_out-meins
FROM mara
WHERE matnr = ls_mchb-matnr.
SELECT SINGLE vfdat INTO @DATA(lv_vfdat)
FROM mch1
WHERE matnr = @ls_mchb-matnr
AND charg = @ls_mchb-charg.
WRITE lv_vfdat TO ls_out-vfdat.
* APPEND ls_out TO lt_out.
COLLECT ls_out INTO lt_out.
CLEAR: ls_out,
ls_mchb.
ENDLOOP.
ENDIF.
ENDIF.
LOOP AT lt_out ASSIGNING FIELD-SYMBOL(<fs_out>).
SELECT SINGLE ddtext INTO <fs_out>-zjy_text
FROM qave AS a
INNER JOIN qals AS b ON a~prueflos = b~prueflos
INNER JOIN dd07t AS c ON a~vbewertung = c~domvalue_l
WHERE b~matnr = <fs_out>-matnr
AND b~charg = <fs_out>-charg
AND c~ddlanguage = sy-langu
AND c~domname = 'QBEWERTUNG'.
IF sy-subrc NE 0.
<fs_out>-zjy_text = '没有评估'.
ENDIF.
ENDLOOP.
et_entityset = lt_out.
ENDMETHOD.
3.前端处理
因为用代理解决跨域,这里 sServiceUrl = "/sap/opu/odata/sap/ZPDA_STOCK_SRV/"得用接口的相对路径,不能用绝对路径。
3.1通过key
| 特性 | 方式一 (By Key) |
|---|---|
| URL 格式 | /Stock_typSet('400011073537') |
| 后端行为 | 调用 GET_ENTITY (单条读取) |
| 返回结构 | oData 是对象 {...} |
| 适用场景 | 已知唯一主键,获取详情 |
javascript
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/odata/v2/ODataModel",
"sap/m/MessageToast"
], (
Controller, ODataModel, MessageToast
) => {
"use strict";
return Controller.extend("demo01.controller.View1", {
/**
* @override
* @returns {void|undefined}
*/
onInit: function () {
var sServiceUrl = "/sap/opu/odata/sap/ZPDA_STOCK_SRV/";
var oModel = new ODataModel(sServiceUrl, {
useBatch: true,
json: true
});
this.getView().setModel(oModel);
},
onButtonQueryPress: function () {
var oModel = this.getView().getModel();
var sHuident = "400011073537";
var sPath = "/Stock_typSet('" + sHuident + "')";
oModel.read(sPath, {
success: function (oData, response) {
this.getView().bindElement(sPath);
MessageToast.show("查询成功")
}.bind(this),
error: function (oError) {
MessageToast.show("查询失败")
}.bind(this)
});
}
});
});
xml视图
javascript
<mvc:View
controllerName="demo01.controller.View1"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:f="sap.ui.layout.form"
displayBlock="true"
height="100%"
busyIndicatorDelay="0">
<VBox class="sapUiSmallMargin">
<Button id="idqueryButton"
text="查询" press="onButtonQueryPress" />
</VBox>
<VBox class="sapUiSmallMargin">
<f:SimpleForm
layout="ResponsiveGridLayout"
title="库存信息"
labelSpanXL="4"
labelSpanL="3"
labelSpanM="4"
labelSpanS="12"
adjustLabelSpan="false"
emptySpanXL="0"
emptySpanL="4"
emptySpanM="0"
emptySpanS="0"
columnsXL="2"
columnsL="1"
columnsM="1"
singleContainerFullSize="false">
<f:content>
<Label text="HU" />
<Text text="{Huident}" />
<Label text="物料编码" />
<Text text="{Matnr}" />
<Label text="物料描述" />
<Text text="{Maktx}" />
<Label text="批次" />
<Text text="{Charg}" />
<Label text="数量" />
<Text text="{Quan}" />
<Label text="单位" />
<Text text="{Meins}" />
<Label text="有效期" />
<Text text="{Vfdat}" />
</f:content>
</f:SimpleForm>
</VBox>
</mvc:View>
效果

3.2通过filter
| 特性 | 方式二 (By Filter) |
|---|---|
| URL 格式 | /Stock_typSet?$filter=Huident eq '400011073537' |
| 后端行为 | 调用 GET_ENTITYSET (集合查询) |
| 返回结构 | oData.results 是数组 [...] |
| 适用场景 | 搜索、条件查询或主键不唯一 |
javascript
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/odata/v2/ODataModel",
"sap/ui/model/Filter",
"sap/ui/model/FilterOperator"
], (Controller, ODataModel,Filter, FilterOperator)=> {
"use strict";
return Controller.extend("demo01.controller.View1", {
/**
* @override
* @returns {void|undefined}
*/
onInit: function() {
var sServiceUrl = "/sap/opu/odata/sap/ZPDA_STOCK_SRV/";
var oModel = new ODataModel(sServiceUrl, {
useBatch: true,
json: true
});
this.getView().setModel(oModel);
},
onButtonQueryPress: function() {
var oModel = this.getView().getModel();
var sHuident = "400011073537";
// 创建过滤器
var oFilter = new Filter("Huident", FilterOperator.EQ, sHuident);
oModel.read("/Stock_typSet", {
filters: [oFilter], // 传入过滤器数组
success: function(oData) {
// 注意:使用 Filter 返回的是数组,oData.results 包含结果
console.log("查询结果列表:", oData.results);
},
error: function(oError) {
console.error("读取失败:", oError);
}
});
}
});
});