HAP發(fā)貨單管理Demo


title: HAP發(fā)貨單管理Demo
categories: 后端
tags:

  • hap

整體效果

前端效果

發(fā)貨退貨單據(jù)界面:

前端效果模板1.png

頭行查詢出現(xiàn)的屬性:頭:1.單號(hào) 2.發(fā)貨時(shí)間 3.單據(jù)類型 4.倉(cāng)庫(kù) 5.接收倉(cāng)庫(kù) 6.發(fā)貨單狀態(tài) 7.司機(jī)姓名 8.司機(jī)電話 9.車牌號(hào)碼 10.運(yùn)費(fèi) 11.備注 12.創(chuàng)建人

單號(hào):DOC_NUMBER,必輸,編號(hào)規(guī)則:D+yyyyMMdd+(1-1000隨機(jī)整數(shù),不夠4位用0補(bǔ)齊) 
發(fā)貨時(shí)間:大于當(dāng)前日期,必輸
單據(jù)類型:DOC_TYPE,ISSUED/RETURN,下拉列表
倉(cāng)庫(kù):ORGANIZATION_ID,LOV,必輸,從xxfnd_organization_access_b表中獲取,根據(jù)當(dāng)前角色來篩選組織 e.param['roleId'] =("${Session.roleId}");
接收倉(cāng)庫(kù):TO_ORGANIZATION_ID,LOV,當(dāng)DOC_TYPE=ISSUED時(shí),必輸,不可等于來源倉(cāng)庫(kù),否則灰掉不可錄入
發(fā)貨狀態(tài):顯示LOOKUP的MEANING                   
運(yùn)費(fèi):只錄入數(shù)字,ISSUED類型時(shí)必輸

行:1.行號(hào) 2.物料編碼 3.是否批次控制 4.物料說明 5.批次 6.行數(shù)量 7.待發(fā)貨數(shù)量 8.已發(fā)貨數(shù)量

物料編碼:LOV,字段對(duì)應(yīng)為INVENTORY_ITEM_ID,根據(jù)頭上的ORGANIZATION_ID作為篩選條件                          
是否批次控制:只讀,CHECKBOX,由"物料編碼LOV"帶出,物料TABLE:xxinv_material_item                 
物料說明:只讀,由"物料編碼LOV"帶出
批次:IF(LOT_CONTROL=Y)要錄入, 否則不用錄入
行數(shù)量:"PENDING"時(shí)必輸,其他狀態(tài)只讀顯示
待發(fā)貨數(shù)量:"PENDING,ISSUED"時(shí)只讀蝎亚,“COMFIRMED”默認(rèn)(行數(shù)量-已發(fā)貨數(shù)量)
已發(fā)貨數(shù)量(ISSUED_QTY):只讀根據(jù)xxinv_material_txns的SOURCE_TABLE,SOURCE_KEY1來匯總行的已發(fā)貨數(shù)量

單據(jù)查詢界面:

搜索條件:1.單號(hào) 2.倉(cāng)庫(kù) 3.單據(jù)狀態(tài) 4.物料編碼 5.是否存在未發(fā)貨行 6.單據(jù)類型

查詢結(jié)果:1.單據(jù)號(hào) 2.單據(jù)類型 3.單據(jù)狀態(tài) 4.倉(cāng)庫(kù) 5.目標(biāo)倉(cāng)庫(kù) 6.司機(jī) 7.創(chuàng)建人 (編輯與查看按鈕)

前端效果模板2.png

前端界面對(duì)應(yīng)邏輯

頭行界面邏輯:

頭行界面邏輯.png

單據(jù)查詢界面邏輯:

單據(jù)查詢界面邏輯.png

編碼描述與lov值準(zhǔn)備:

lov與編碼描述準(zhǔn)備.png

開發(fā)

數(shù)據(jù)庫(kù)準(zhǔn)備

創(chuàng)建數(shù)據(jù)庫(kù)

創(chuàng)建數(shù)據(jù)庫(kù)hap4_demo,把權(quán)限賦給hap_dev用戶
因?yàn)槲覀円呀?jīng)有了hap_dev用戶炫狱,這里就只是創(chuàng)建數(shù)據(jù)庫(kù)和刷新權(quán)限了魂务。

CREATE SCHEMA hap4_ship_order DEFAULT CHARACTER SET utf8mb4;
GRANT ALL PRIVILEGES ON hap4_ship_order.* TO hap_dev@'%';
FLUSH PRIVILEGES;

修改pom文件中的依賴與前端config.js

修改pom文件中的依賴

<dependency>
            <groupId>io.choerodon</groupId>
            <artifactId>hap-core</artifactId>
        </dependency>
        <dependency>
            <groupId>io.choerodon</groupId>
            <artifactId>hap-security-standard</artifactId>
        </dependency>

修改config.js

 modules: [
    '../target/generate-react/choerodon-fnd-util',
    '../target/generate-react/hap-core',
  ],

初始化數(shù)據(jù)庫(kù)表

修改initdatabse.sh腳本中的數(shù)據(jù)庫(kù)名稱和target.jar的名稱與項(xiàng)目名稱一致
再執(zhí)行該腳本sh init-database.sh

修改xlsx文件

將grid的demo路徑修改為項(xiàng)目路徑。就是將hap-demo替換為這里的ship-order又谋。修改這里是因?yàn)榍岸寺窂降膯栴}橘荠,路由的js中有${match.url}匹配的就是當(dāng)前項(xiàng)目名礁遣,xlsx中寫死了hap-demo雪侥,需要手動(dòng)修改碗殷。

打包并運(yùn)行

執(zhí)行如下命令腳本

mvn clean package
npm install --registry https://nexus.choerodon.com.cn/repository/choerodon-npm/
npm run build
mvn spring-boot:run

所需表初始化

添加groovy建表的腳本,這里給出一個(gè)實(shí)例:物料表速缨。

package script.db

databaseChangeLog(logicalFilePath: "hap4-ship-order.groovy") {

    changeSet(author: "lth", id: "2019-08-26-hap4_ship_order") {
        if (helper.dbType().isSupportSequence()) {
            createSequence(sequenceName: 'XXINV_MATERIAL_ITEM_S', startValue: "100")
        }
        createTable(tableName: "XXINV_MATERIAL_ITEM", remarks: "物料信息表") {
            if (helper.dbType().isSupportAutoIncrement()) {
                column(name: "ITEM_ID", type: "BIGINT", autoIncrement: "true", startWith: "10001", remarks: "PK") {
                    constraints(nullable: "false", primaryKey: "true", primaryKeyName: "XXINV_MATERIAL_ITEM_PK")
                }
            } else {
                column(name: "ITEM_ID", type: "BIGINT", remarks: "PK") {
                    constraints(nullable: "false", primaryKey: "true", primaryKeyName: "XXINV_MATERIAL_ITEM_PK")
                }
            }
            column(name: "INVENTORY_ITEM_ID", type: "BIGINT(20)", remarks: "ERP物料ID") {
                constraints(nullable: "false")
            }
            column(name: "ITEM_NUMBER", type: "VARCHAR(60)", remarks: "物料編碼") {
                constraints(nullable: "false")
            }
            column(name: "ORGANIZATION_ID", type: "BIGINT(20)", remarks: "庫(kù)存組織ID") {
                constraints(nullable: "false")
            }
            column(name: "DESCRIPTION", type: "VARCHAR(240)", remarks: "物料說明")
            column(name: "LOT_CONTROL", type: "VARCHAR(10)", remarks: "是否批次控制")
            column(name: "UOM_CODE", type: "VARCHAR(30)", remarks: "單位") {
                constraints(nullable: "false")
            }
            column(name: "ITEM_STATUS", type: "VARCHAR(30)", remarks: "物料狀態(tài) ACTIVE/INACTIVE") {
                constraints(nullable: "false")
            }

            //必輸字段
            column(name: "OBJECT_VERSION_NUMBER", type: "BIGINT", defaultValue: "1")
            column(name: "REQUEST_ID", type: "bigint", defaultValue: "-1")
            column(name: "PROGRAM_APPLICATION_ID", type: "BIGINT(11)")
            column(name: "PROGRAM_ID", type: "bigint", defaultValue: "-1")
            column(name: "PROGRAM_UPDATE_DATE", type: "DATE")
            column(name: "CREATED_BY", type: "bigint", defaultValue: "-1")
            column(name: "CREATION_DATE", type: "datetime", defaultValueComputed: "CURRENT_TIMESTAMP")
            column(name: "LAST_UPDATED_BY", type: "bigint", defaultValue: "-1")
            column(name: "LAST_UPDATE_DATE", type: "datetime", defaultValueComputed: "CURRENT_TIMESTAMP")
            column(name: "LAST_UPDATE_LOGIN", type: "bigint", defaultValue: "-1")

            column(name: "ATTRIBUTE_CATEGORY", type: "varchar(30)")
            column(name: "ATTRIBUTE1", type: "varchar(240)")
            column(name: "ATTRIBUTE2", type: "varchar(240)")
            column(name: "ATTRIBUTE3", type: "varchar(240)")
            column(name: "ATTRIBUTE4", type: "varchar(240)")
            column(name: "ATTRIBUTE5", type: "varchar(240)")
            column(name: "ATTRIBUTE6", type: "varchar(240)")
            column(name: "ATTRIBUTE7", type: "varchar(240)")
            column(name: "ATTRIBUTE8", type: "varchar(240)")
            column(name: "ATTRIBUTE9", type: "varchar(240)")
            column(name: "ATTRIBUTE10", type: "varchar(240)")
            column(name: "ATTRIBUTE11", type: "varchar(240)")
            column(name: "ATTRIBUTE12", type: "varchar(240)")
            column(name: "ATTRIBUTE13", type: "varchar(240)")
            column(name: "ATTRIBUTE14", type: "varchar(240)")
            column(name: "ATTRIBUTE15", type: "varchar(240)")
        }
        //判斷是不是postgresql數(shù)據(jù)庫(kù)锌妻,并且添加唯一約束。我們是mysql數(shù)據(jù)庫(kù)旬牲,
        if (!helper.isPostgresql()) {
            addUniqueConstraint(columnNames: "INVENTORY_ITEM_ID,ORGANIZATION_ID", tableName: "XXINV_MATERIAL_ITEM", constraintName: "XXINV_MATERIAL_ITEM_U1")
        } else {
            addUniqueConstraint(columnNames: "INVENTORY_ITEM_ID", tableName: "XXINV_MATERIAL_ITEM", constraintName: "FXXINV_MATERIAL_ITEM_U1")
        }
    }
}

順便提一句:創(chuàng)建索引的方法如下:

createIndex(tableName: "XXINV_SHIPED_DOC_LINES", indexName: "XXINV_SHIPED_DOC_LINES_N1") {column(name: "INVENTORY_ITEM_ID", type: "BIGINT(20)");column(name: "ORGANIZATION_ID",type: "BIGINT(20)")}

建好groovy腳本之后仿粹,如果之前運(yùn)行過initdatabase腳本,需要?jiǎng)h除數(shù)據(jù)庫(kù)表databasechangelog中之前的操作記錄引谜,否則可能會(huì)腳本運(yùn)行之后沒有插入數(shù)據(jù)庫(kù)牍陌。

后端開發(fā)

建立對(duì)應(yīng)的mapper dto service 包,然后創(chuàng)建實(shí)體類 mapper類 service類员咽。這些上一篇的demo開發(fā)中有解釋實(shí)體類中注解的使用和這些類或者接口應(yīng)該繼承或者實(shí)現(xiàn)什么類或者接口,這里就不再贅述了贮预。

代碼維護(hù)狀態(tài)創(chuàng)建和lov的創(chuàng)建

代碼維護(hù)創(chuàng)建:在后臺(tái)的代碼維護(hù)頁(yè)面創(chuàng)建相應(yīng)的Lookup Type贝室。

lov與編碼描述準(zhǔn)備.png

lov創(chuàng)建

首先我們知道,在后端定義lov時(shí)會(huì)指定一個(gè)sql.id,這個(gè)東西指的就是我們mapper接口名+對(duì)應(yīng)方法装蓬。當(dāng)我們?cè)诤笈_(tái)調(diào)用值集框時(shí)怖糊,就會(huì)自動(dòng)創(chuàng)建一個(gè)mapper對(duì)象。
不過僅僅這樣還不夠峡迷,mapper中方法的定義很有講究银伟。

我這里拿物料來舉例,這個(gè)接口里面有兩個(gè)lov的查詢绘搞。發(fā)現(xiàn)有沒有特別之處彤避?最特殊的就是其中的參數(shù),傳遞的是一個(gè)完整的物料對(duì)象夯辖。這個(gè)對(duì)象中有我們的篩選條件琉预。這里就是尤其要注意的地方。

public interface MaterialItemMapper extends Mapper<MaterialItem> {

    /**
     * 通過倉(cāng)庫(kù)id模糊查詢出倉(cāng)庫(kù)(這里沒有倉(cāng)庫(kù)表)
     * @param materialItem
     * @return
     */
    List<MaterialItem> selectOrganizationLov(MaterialItem materialItem);

    /**
     * 通過頭的倉(cāng)庫(kù)Id(不用手動(dòng)輸入)找對(duì)應(yīng)的物料蒿褂,描述作為再篩選的條件(需要手動(dòng)輸入)
     * @param materialItem
     * @return
     */
    List<MaterialItem> selectMaterialItemLov(MaterialItem materialItem);
    
}

現(xiàn)在在后臺(tái)創(chuàng)建對(duì)應(yīng)的描述:注意我在圖中的解釋圆米,這里就不再拿出來敘述了,在圖上解釋更為直觀啄栓。

lov的定義.png

物料對(duì)應(yīng)的mapper.xml文件:上面的參數(shù)是對(duì)象娄帖,在xml中的寫法又與以往的寫法要變化一些。

  1. ResultMap中需要繼承io.choerodon.mybatis.mapper.StdMapper.STD昙楚。
  2. 寫的查詢方法標(biāo)簽中需要指定resultMap近速。不能用resultType來代替resultMap的定義,而且還要指定parameterType來指定方法傳來的參數(shù)桂肌。
  3. select的參數(shù)需要指定数焊,不能用*來代替。
  4. 如果只有一個(gè)if標(biāo)簽崎场,也需要用<\where>來包裹佩耳。后臺(tái)值集查詢時(shí),會(huì)先執(zhí)行一個(gè)查詢總數(shù)的sql谭跨,如果只用where會(huì)使動(dòng)態(tài)生成的sql多一個(gè)where干厚,會(huì)失敗。
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="hap.demo.core.order.mapper.MaterialItemMapper">


    <resultMap id="baseMaterial" type="hap.demo.core.order.dto.MaterialItem" extends="io.choerodon.mybatis.mapper.StdMapper.STD">
        <id property="itemId" column="ITEM_ID" jdbcType="DECIMAL"/>
        <result property="inventoryItemId" column="INVENTORY_ITEM_ID" jdbcType="DECIMAL"/>
        <result property="itemNumber" column="ITEM_NUMBER" jdbcType="VARCHAR"/>
        <result property="organizationId" column="ORGANIZATION_ID" jdbcType="DECIMAL"/>
        <result property="description" column="DESCRIPTION" jdbcType="VARCHAR"/>
        <result property="lotControl" column="LOT_CONTROL" jdbcType="VARCHAR"/>
        <result property="uomCode" column="UOM_CODE" jdbcType="VARCHAR"/>
        <result property="itemStatus" column="ITEM_STATUS" jdbcType="VARCHAR"/>
    </resultMap>

    <select id="selectOrganizationLov" resultMap="baseMaterial" parameterType="hap.demo.core.order.dto.MaterialItem">
        SELECT distinct item.ORGANIZATION_ID FROM
        xxinv_material_item item
        <where>
            <if test="organizationId != null">
                <bind name="pattern" value="'%' + organizationId + '%'"/>
                item.ORGANIZATION_ID LIKE #{pattern}
            </if>
        </where>
    </select>

    <select id="selectMaterialItemLov" resultMap="baseMaterial" parameterType="hap.demo.core.order.dto.MaterialItem">
        SELECT item.INVENTORY_ITEM_ID,item.ITEM_NUMBER,item.DESCRIPTION,item.ORGANIZATION_ID
        FROM xxinv_material_item item
        <where>
            <if test="organizationId != null">
                item.ORGANIZATION_ID = #{organizationId}
            </if>
            <if test="description != null">
                <bind name="pattern" value="'%' + description + '%'"/>
                and item.DESCRIPTION LIKE #{pattern}
            </if>
        </where>
    </select>
</mapper>

訂單頭和行查詢 (增刪改也補(bǔ)充上了)

頭:創(chuàng)建VO類螃宙,整理sql語(yǔ)句

先寫出條件查詢訂單頭蛮瞄,因?yàn)橛胁皇穷^表中的列作為條件,所以我使用了一個(gè)OrderHeaderVO來包裹條件字段和結(jié)果字段谆扎。注意其中的shippedTime屬性挂捅,它來自ShipDoc類。①這個(gè)屬性在name為OrderHeader的DataSet中堂湖,對(duì)應(yīng)字段的type是datetime類型闲先。②shippedTime的java類型是java.util.Date状土。③OrderHeaderVO的resultMapper中shippedTime對(duì)應(yīng)的jdbctype為TIMESTAPE,對(duì)應(yīng)的javatype是java.util.Date伺糠。還有這里繼承了BaseDTO是因?yàn)榍岸说脑鰟h改查操作會(huì)傳一個(gè)status值蒙谓,只有繼承了BaseDTO之后,在寫service的實(shí)現(xiàn)類并重寫方法時(shí)训桶,才能拿到狀態(tài)值累驮,進(jìn)行對(duì)應(yīng)的操作。

這里需要注意數(shù)據(jù)中的字段是datetime類型的舵揭,講道理對(duì)應(yīng)的java中類型為TIMESTAPE谤专,那么jdbctype是它可以理解;那為啥實(shí)體類的字段不使用TIMESTAPE而使用Date呢琉朽?通過實(shí)踐毒租,如果使用TIMESTAPE,前端的DataSet轉(zhuǎn)化為OrderHeaderVO就會(huì)報(bào)錯(cuò)箱叁,不能轉(zhuǎn)化成TIMESTAPE墅垮;如果使用@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")能夠轉(zhuǎn)化成對(duì)象了。不過我看BaseDTO的源碼中耕漱,它的創(chuàng)建與更新時(shí)間屬性都是java.util.Date類型算色,數(shù)據(jù)庫(kù)中也是datetime類型,在mapper.xml中用的是jdbctype="TIMESTAPE"螟够;這就省略了使用注解灾梦。這里兩種方式應(yīng)該都是能行的。我當(dāng)時(shí)時(shí)間查詢之后沒有在前端顯示出來是因?yàn)槲姨孛磗elect語(yǔ)句的時(shí)間字段沒有對(duì)應(yīng)上resultMap中的列妓笙,真是難受若河。

public class OrderHeaderVO extends BaseDTO {

    private Long shipDocId;
    //結(jié)果字段

    private String docNumber;

    private String docType;

    private String shippmentStatus;

    private Long organizationId;

    private Long toOrganizationId;

    private String driverName;

    private String driverPhone;

    private Double freight;

    private String memo;

    private Date shippedTime;

    private Long createdBy;


    //條件字段

    private Long inventoryItemId;

    private String issuedFlag;
    
    private List<OrderLineVO> lines;
    //省略getter和setter
}

下面給出基本的連接,沒有添加條件寞宫。這條sql我改了三次萧福,第一次select的值只寫了頭界面展示的;第二次由于行上需要展示頭界面未包含的辈赋,就添加了隱藏的創(chuàng)建時(shí)間等字段鲫忍;第三次由于是左連接,如果一個(gè)頭對(duì)應(yīng)多個(gè)行钥屈,那么頭就會(huì)顯示多次悟民,為了不重復(fù)顯示頭就添加了DISTINCT。

SELECT DISTINCT
    doc.SHIP_DOC_ID,
    doc.DOC_NUMBER,
    doc.DOC_TYPE,
    doc.SHIPPMENT_STATUS,
    doc.ORGANIZATION_ID,
    doc.TO_ORGANIZATION_ID,
    doc.DRIVER_NAME,
    doc.DRIVER_PHONE,
    doc.FREIGHT,
    doc.MEMO,
    doc.CREATED_BY,
    doc.SHIPPED_TIME
FROM
    xxinv_shiped_doc doc
LEFT JOIN xxinv_shiped_doc_lines line ON (
    doc.SHIP_DOC_ID = line.SHIP_DOC_ID
)
LEFT JOIN xxinv_material_item item ON (
    line.INVENTORY_ITEM_ID = item.INVENTORY_ITEM_ID
    AND line.ORGANIZATION_ID = item.ORGANIZATION_ID
)

對(duì)應(yīng)的mybatis中的語(yǔ)句:

<resultMap id="baseOrderHeader" type="hap.demo.core.order.dto.vo.OrderHeaderVO">
        <result property="shipDocId" column="SHIP_DOC_ID" jdbcType="DECIMAL"/>
        <result property="createdBy" column="CREATED_BY" jdbcType="DECIMAL"/>
        <result property="docNumber" column="DOC_NUMBER" jdbcType="VARCHAR"/>
        <result property="docType" column="DOC_TYPE" jdbcType="VARCHAR"/>
        <result property="driverName" column="DRIVER_NAME" jdbcType="VARCHAR"/>
        <result property="driverPhone" column="DRIVER_PHONE" jdbcType="VARCHAR"/>
        <result property="inventoryItemId" column="INVENTORY_ITEM_ID" jdbcType="DECIMAL"/>
        <result property="issuedFlag" column="ISSUED_FLAG" jdbcType="VARCHAR"/>
        <result property="organizationId" column="ORGANIZATION_ID" jdbcType="DECIMAL"/>
        <result property="shippmentStatus" column="SHIPPMENT_STATUS" jdbcType="VARCHAR"/>
        <result property="toOrganizationId" column="TO_ORGANIZATION_ID" jdbcType="DECIMAL"/>
        <result property="freight" column="FREIGHT" jdbcType="DECIMAL"/>
        <result property="memo" column="MEMO" jdbcType="VARCHAR"/>
        <result property="shippedTime" column="SHIPPED_TIME" javaType="java.util.Date" jdbcType="TIMESTAMP"/>
        <collection property="lines" javaType="ArrayList" column="shipDocId" ofType="hap.demo.core.order.dto.vo.OrderLineVO"
                    select="hap.demo.core.order.mapper.ShipDocLinesMapper.selectOrderLine"/>
    </resultMap>
     <select id="selectOrderHeader" resultMap="baseOrderHeader" >
        SELECT DISTINCT
            doc.SHIP_DOC_ID,doc.DOC_NUMBER,doc.DOC_TYPE,doc.SHIPPMENT_STATUS,doc.ORGANIZATION_ID,
        doc.TO_ORGANIZATION_ID,doc.DRIVER_NAME,doc.DRIVER_PHONE,doc.FREIGHT,doc.MEMO,doc.CREATED_BY,doc.SHIPPED_TIME
        FROM
            xxinv_shiped_doc doc
        LEFT JOIN xxinv_shiped_doc_lines line ON (
            doc.SHIP_DOC_ID = line.SHIP_DOC_ID
        )
        LEFT JOIN xxinv_material_item item ON (
            line.INVENTORY_ITEM_ID = item.INVENTORY_ITEM_ID
            AND line.ORGANIZATION_ID = item.ORGANIZATION_ID
        )
        <where>
            <if test="orderHeaderVO.docNumber != null">
                doc.DOC_NUMBER =#{orderHeaderVO.docNumber}
            </if>
            <if test="orderHeaderVO.docType != null">
                AND doc.DOC_TYPE = #{orderHeaderVO.docType}
            </if>
            <if test="orderHeaderVO.shippmentStatus != null">
                AND doc.shippment_status =#{orderHeaderVO.shippmentStatus}
            </if>
            <if test="orderHeaderVO.organizationId != null">
                AND doc.organization_id =#{orderHeaderVO.organizationId}
            </if>
            <if test="orderHeaderVO.inventoryItemId != null">
                AND item.INVENTORY_ITEM_ID =#{orderHeaderVO.inventoryItemId}
            </if>
            <if test="orderHeaderVO.issuedFlag != null">
                AND line.ISSUED_FLAG  =#{orderHeaderVO.issuedFlag}
            </if>
        </where>
    </select>

行:對(duì)應(yīng)的VO類和sql語(yǔ)句

創(chuàng)建行OrderLineVO類篷就,主要就是包含在前端顯示的字段射亏,或者是需要行實(shí)體類需要用到的字段。 上面頭的實(shí)體中之所以要加行的list,是因?yàn)檠挥荆笮陆^同時(shí)插入行 或者有頭但 更新行的時(shí)候银锻,能夠從重寫的mutations方法中的headerList參數(shù)中永品,header對(duì)象中能拿到對(duì)應(yīng)的行l(wèi)ist做鹰,從而拿到行數(shù)據(jù)。
下面加了一個(gè)@JsonIgnoreProperties(ignoreUnknown = true)注解是因?yàn)榍岸说男蠨ataSet轉(zhuǎn)化為行實(shí)體的時(shí)候會(huì)把當(dāng)中定義的lov的對(duì)象也傳進(jìn)來鼎姐。這個(gè)注解能讓不是實(shí)體中的字段被忽略钾麸。

@JsonIgnoreProperties(ignoreUnknown = true)
public class OrderLineVO extends BaseDTO {

    private Long lineNum;

    private Long inventoryItemId;

    private String itemNumber;
    //物料表中

    private String lotControl;

    private String description;

    //行表

    private String lotNumber;

    private Double lineQty;

    private Double issueReqQty;

    //已發(fā)貨數(shù)量

    private Double shippedQty;
    //省略getter和setter
}

前端行對(duì)應(yīng)的mybatis中的sql語(yǔ)句

<resultMap id="baseOrderLineVO" type="hap.demo.core.order.dto.vo.OrderLineVO">
        <result property="description" column="DESCRIPTION" jdbcType="VARCHAR"/>
        <result property="lineNum" column="LINE_NUM" jdbcType="DECIMAL"/>
        <result property="inventoryItemId" column="INVENTORY_ITEM_ID" jdbcType="DECIMAL"/>
        <result property="itemNumber" column="ITEM_NUMBER" jdbcType="VARCHAR"/>
        <result property="lotControl" column="LOT_CONTROL" jdbcType="VARCHAR"/>
        <result property="lotNumber" column="LOT_NUMBER" jdbcType="VARCHAR"/>
        <result property="lineQty" column="LINE_QTY" jdbcType="DECIMAL"/>
        <result property="issueReqQty" column="ISSUE_REQ_QTY" jdbcType="DECIMAL"/>
    </resultMap>
    <select id="selectOrderLine" resultMap="baseOrderLineVO">
        SELECT
            line.LINE_NUM,
            item.ITEM_NUMBER,
            item.INVENTORY_ITEM_ID,
            item.LOT_CONTROL,
            item.DESCRIPTION,
            line.LOT_NUMBER,
            line.LINE_QTY,
            line.ISSUE_REQ_QTY
        FROM
            xxinv_shiped_doc_lines line
        LEFT JOIN xxinv_material_item item ON (
            line.INVENTORY_ITEM_ID = item.INVENTORY_ITEM_ID
            AND line.ORGANIZATION_ID = item.ORGANIZATION_ID
        )
        WHERE line.SHIP_DOC_ID=#{shipDocId}
    </select>

創(chuàng)建對(duì)應(yīng)的Service和Impl類

Service類不說了,Impl類說一下:這個(gè)OrderHeaderVO沒有對(duì)應(yīng)的數(shù)據(jù)庫(kù)表炕桨,那么就不用繼承BaseServiceImpl了饭尝,Service類也不用繼承BaseService類。不過還是需要實(shí)現(xiàn)IDatasetService接口的献宫,這樣前端的dataset的請(qǐng)求才能有對(duì)應(yīng)的操作钥平。
queries方法中的參數(shù)body中包裹的就是dataset.js中的query fields屬性;前端編輯了哪些查詢條件姊途,把他的name與value作為body的k-v涉瘾。例如前端,我前端有一個(gè)如下查詢字段的代碼捷兰,那么我如果在前端進(jìn)行了物料lov的選擇立叛,隨便選一個(gè)物料,點(diǎn)擊查詢贡茅;這個(gè)接收到的body中會(huì)包含了什么呢秘蛇?看下面的圖就知道了,會(huì)把這兩個(gè)屬性查到的值,和它的name作為鍵值對(duì)傳入body顶考。知道這個(gè)了赁还,我后面通過shopDocId查行就能通過前端拿到shopDocId了。

{ name: 'materialItem4', type: 'object',textField: 'itemNumber', label: '物料編碼', lovCode: 'XXINV_ITEMS' },
        { name: 'inventoryItemId', type: 'number', label: 'ERP物料ID', bind: 'materialItem4.inventoryItemId' },
queries中的body.png

下面是初步的查詢的Service驹沿,并沒有測(cè)試是否能增刪改艘策。這里面有很多錯(cuò)誤。

@Service
@Transactional(rollbackFor = Exception.class)
@Dataset("OrderHeader")
public class OrdreHeaderServiceImpl implements OrderHeaderService, IDatasetService<OrderHeaderVO> {

    @Autowired
    private ShipDocMapper shipDocMapper;
    @Autowired
    private ShipDocLinesMapper shipDocLinesMapper;

    @Override
    public List<OrderHeaderVO> listOrderHeader(OrderHeaderVO orderHeaderVO) {
        return shipDocMapper.selectOrderHeader(orderHeaderVO);
    }

    @Override
    public List<?> queries(Map<String, Object> body, int page, int pageSize, String sortname, boolean isDesc) {
        try {
            OrderHeaderVO orderHeaderVO = new OrderHeaderVO();
            BeanUtils.populate(orderHeaderVO, body);
            PageHelper.startPage(page, pageSize);
            return shipDocMapper.selectOrderHeader(orderHeaderVO);
        } catch (Exception e) {
            throw new DatasetException("dataset.error", e);
        }
    }

    @Override
    public List<OrderHeaderVO> mutations(List<OrderHeaderVO> list) {
        for (OrderHeaderVO header : list) {
            switch (header.get__status()) {
                case ADD:
                    insertOrderHeader(header);
                    break;
                case DELETE:
                    deleteOrderHeader(header);
                    break;
                case UPDATE:
                    updateOrderHeader(header);
                    break;
                default:
                    break;
            }
        }
        return list;
    }


    private ShipDoc headerToShipDoc(OrderHeaderVO header) {
        return new ShipDoc(header.getOrganizationId(),
                header.getDocNumber(),
                header.getToOrganizationId(),
                header.getShippedTime(),
                header.getDocType(),
                header.getShippmentStatus(),
                header.getCreatedBy(),
                header.getDriverName(),
                header.getDriverPhone(),
                header.getFreight(),
                header.getMemo());


    }

    private ShipDocLines orderLineToShipDocLines(OrderHeaderVO header,OrderLineVO orderLine) {
        return new ShipDocLines(header.getShipDocId(),
                orderLine.getLineNum(),
                orderLine.getInventoryItemId(),
                header.getOrganizationId(),
                orderLine.getIssueReqQty(),
                orderLine.getLineQty(),
                orderLine.getLotNumber(),
                header.getIssuedFlag());
    }

    private void insertOrderHeader(OrderHeaderVO header) {
        List<OrderLineVO> orderLines = shipDocLinesMapper.selectOrderLine(header.getShipDocId());
        ShipDoc shipDoc = headerToShipDoc(header);
        shipDocMapper.insert(shipDoc);
        if (orderLines.size() != 0) {
            for (OrderLineVO line:orderLines
            ) {
                ShipDocLines shipDocLine = orderLineToShipDocLines(header, line);
                shipDocLinesMapper.insert(shipDocLine);
            }
        }
    }

    private void deleteOrderHeader(OrderHeaderVO header) {
        List<OrderLineVO> orderLines = shipDocLinesMapper.selectOrderLine(header.getShipDocId());
        ShipDoc shipDoc = headerToShipDoc(header);
        shipDocMapper.delete(shipDoc);
        if (orderLines.size() != 0) {
            for (OrderLineVO line:orderLines
            ) {
                ShipDocLines shipDocLine = orderLineToShipDocLines(header, line);
                shipDocLinesMapper.delete(shipDocLine);
            }
        }
    }

    private void updateOrderHeader(OrderHeaderVO header) {
        List<OrderLineVO> orderLines = shipDocLinesMapper.selectOrderLine(header.getShipDocId());
        ShipDoc shipDoc = headerToShipDoc(header);
        shipDocMapper.updateByPrimaryKey(shipDoc);
        if (orderLines.size() != 0) {
            for (OrderLineVO line:orderLines
            ) {
                ShipDocLines shipDocLine = orderLineToShipDocLines(header, line);
                shipDocLinesMapper.updateByPrimaryKey(shipDocLine);
            }
        }
    }
}

下面給出比較完善的頭的實(shí)現(xiàn)類:基本實(shí)現(xiàn)了單次插入頭和行甚负,或者在頭的基礎(chǔ)上修改或者插入行柬焕,也實(shí)現(xiàn)了頭行一起刪除。

@Service
@Transactional(rollbackFor = Exception.class)
@Dataset("OrderHeader")
public class OrdreHeaderServiceImpl implements OrderHeaderService, IDatasetService<OrderHeaderVO> {

    @Autowired
    private ShipDocMapper shipDocMapper;
    @Autowired
    private ShipDocLinesMapper shipDocLinesMapper;

    @Override
    public List<OrderHeaderVO> listOrderHeader(OrderHeaderVO orderHeaderVO) {
        return shipDocMapper.selectOrderHeader(orderHeaderVO);
    }

    @Override
    public List<?> queries(Map<String, Object> body, int page, int pageSize, String sortname, boolean isDesc) {
        try {
            OrderHeaderVO orderHeaderVO = new OrderHeaderVO();
            BeanUtils.populate(orderHeaderVO, body);
            PageHelper.startPage(page, pageSize);
            return shipDocMapper.selectOrderHeader(orderHeaderVO);
        } catch (Exception e) {
            throw new DatasetException("dataset.error", e);
        }
    }

    @Override
    public List<OrderHeaderVO> mutations(List<OrderHeaderVO> list) {
        for (OrderHeaderVO header : list) {
            switch (header.get__status()) {
                case ADD:
                    insertOrderHeader(header);
                    break;
                case DELETE:
                    deleteOrderHeader(header);
                    break;
                case UPDATE:
                    updateOrderHeader(header);
                    break;
                default:
                    break;
            }
        }
        return list;
    }

    /**
     * 將前端的header轉(zhuǎn)化為訂單頭
     *
     * @param header
     * @return
     */
    private ShipDoc headerToShipDoc(OrderHeaderVO header) {
        ShipDoc frontShipDoc = new ShipDoc();
        frontShipDoc.setOrganizationId(header.getOrganizationId());
        frontShipDoc.setDocNumber(header.getDocNumber());
        frontShipDoc.setToOrganizationId(header.getToOrganizationId());
        frontShipDoc.setShippedTime(header.getShippedTime());
        frontShipDoc.setDocType(header.getDocType());
        frontShipDoc.setShippmentStatus(header.getShippmentStatus());
        frontShipDoc.setCreatedBy(header.getCreatedBy());
        frontShipDoc.setDriverName(header.getDriverName());
        frontShipDoc.setDriverPhone(header.getDriverPhone());
        frontShipDoc.setFreight(header.getFreight());
        frontShipDoc.setMemo(header.getMemo());

        return frontShipDoc;
    }

    /**
     * 將前端的line轉(zhuǎn)化為訂單行
     *
     * @param header
     * @param orderLine
     * @return
     */
    private ShipDocLines orderLineToShipDocLines(OrderHeaderVO header, OrderLineVO orderLine) {
        ShipDocLines frontShipDocLine = new ShipDocLines();

        frontShipDocLine.setShipDocId(header.getShipDocId());
        frontShipDocLine.setLineNum(orderLine.getLineNum());
        frontShipDocLine.setInventoryItemId(orderLine.getInventoryItemId());
        frontShipDocLine.setOrganizationId(header.getOrganizationId());
        frontShipDocLine.setIssueReqQty(orderLine.getIssueReqQty());
        frontShipDocLine.setLineQty(orderLine.getLineQty());
        frontShipDocLine.setLotNumber(orderLine.getLotNumber());
        frontShipDocLine.setIssuedFlag(header.getIssuedFlag());

        return frontShipDocLine;
    }

    private void insertOrderHeader(OrderHeaderVO header) {
        List<OrderLineVO> orderLines = header.getLines();
        ShipDoc shipDoc = headerToShipDoc(header);
        shipDocMapper.insert(shipDoc);
        if (orderLines.size() != 0) {
            Long i =Long.valueOf("1") ;
            for (OrderLineVO line : orderLines
            ) {
                line.setLineNum(i);
                Long docId = shipDocMapper.selectShipDocId(shipDoc.getOrganizationId(), shipDoc.getDocNumber());
                header.setShipDocId(docId);
                ShipDocLines shipDocLine = orderLineToShipDocLines(header, line);
                shipDocLinesMapper.insert(shipDocLine);
                i++;
            }
        }
    }

    private void deleteOrderHeader(OrderHeaderVO header) {
        List<OrderLineVO> orderLines = shipDocLinesMapper.selectOrderLine(header.getShipDocId());
        ShipDoc shipDoc = headerToShipDoc(header);
        shipDocMapper.delete(shipDoc);
        if (orderLines.size() != 0) {
            for (OrderLineVO line : orderLines
            ) {
                ShipDocLines shipDocLine = orderLineToShipDocLines(header, line);
                shipDocLinesMapper.delete(shipDocLine);
            }
        }
    }

    /**
     * @param shipDoc  新
     * @param shipDoc1 原始
     * @return
     */
    private ShipDoc mergeShipDoc(ShipDoc shipDoc, ShipDoc shipDoc1) {
        if (shipDoc.getShippedTime() != null && !shipDoc.getShippedTime().equals(shipDoc1.getShippedTime())) {
            shipDoc1.setShippedTime(shipDoc.getShippedTime());
        }
        if (shipDoc.getDocType() != null && !shipDoc.getDocType().equals(shipDoc1.getDocType())) {
            shipDoc1.setDocType(shipDoc.getDocType());
        }
        if (shipDoc.getOrganizationId() != null && !shipDoc.getOrganizationId().equals(shipDoc1.getOrganizationId())) {
            shipDoc1.setOrganizationId(shipDoc.getOrganizationId());
        }
        if (shipDoc.getToOrganizationId() != null && !shipDoc.getToOrganizationId().equals(shipDoc1.getToOrganizationId())) {
            shipDoc1.setToOrganizationId(shipDoc.getToOrganizationId());
        }
        if (shipDoc.getDriverName() != null && !shipDoc.getDriverName().equals(shipDoc1.getDriverName())) {
            shipDoc1.setDriverName(shipDoc.getDriverName());
        }
        if (shipDoc.getDriverPhone() != null && !shipDoc.getDriverPhone().equals(shipDoc1.getDriverPhone())) {
            shipDoc1.setDriverPhone(shipDoc.getDriverPhone());
        }
        if (shipDoc.getFreight() != null && !shipDoc.getFreight().equals(shipDoc1.getFreight())) {
            shipDoc1.setFreight(shipDoc.getFreight());
        }
        if (shipDoc.getMemo() != null && !shipDoc.getMemo().equals(shipDoc1.getMemo())) {
            shipDoc1.setMemo(shipDoc.getMemo());
        }

        return shipDoc1;
    }

    /**
     * @param shipDocLines  新
     * @param shipDocLines1 數(shù)據(jù)庫(kù)中原數(shù)據(jù)
     * @return
     */
    private ShipDocLines mergeShipDocLines(ShipDocLines shipDocLines, ShipDocLines shipDocLines1) {
        if (shipDocLines.getInventoryItemId() != null && !shipDocLines.getInventoryItemId().equals(shipDocLines1.getInventoryItemId())) {
            shipDocLines1.setInventoryItemId(shipDocLines.getInventoryItemId());
        }
        if (shipDocLines.getLotNumber() != null && !shipDocLines.getLotNumber().equals(shipDocLines1.getLotNumber())) {
            shipDocLines1.setLotNumber(shipDocLines.getLotNumber());
        }
        if (shipDocLines.getLineQty() != null && !shipDocLines.getLineQty().equals(shipDocLines1.getLineQty())) {
            shipDocLines1.setLineQty(shipDocLines.getLineQty());
        }
        if (shipDocLines.getIssueReqQty() != null && !shipDocLines.getIssueReqQty().equals(shipDocLines1.getIssueReqQty())) {
            shipDocLines1.setIssueReqQty(shipDocLines.getIssueReqQty());
        }

        return shipDocLines1;
    }

    private void updateOrderHeader(OrderHeaderVO header) {
        List<OrderLineVO> orderLines = header.getLines();

        //shipDoc是新修改數(shù)據(jù)
        ShipDoc shipDoc = headerToShipDoc(header);
        //shipDoc1是原數(shù)據(jù)
        ShipDoc shipDoc1 = shipDocMapper.selectByPrimaryKey(header.getShipDocId());
        ShipDoc mergeShipDoc1 = mergeShipDoc(shipDoc, shipDoc1);


        shipDocMapper.updateByPrimaryKey(mergeShipDoc1);
        //已經(jīng)存在行增加或修改的情況下
        if (orderLines.size() != 0) {
            for (OrderLineVO line : orderLines
            ) {
                //轉(zhuǎn)化為行梭域,再查出原來的行斑举,合并,更新
                ShipDocLines newShipDocLine = orderLineToShipDocLines(header, line);
                Long docLineId = shipDocLinesMapper.selectShipDocLineId(newShipDocLine.getOrganizationId(),
                        newShipDocLine.getInventoryItemId(),
                        newShipDocLine.getShipDocId());
                if (null != docLineId) {
                    ShipDocLines shipDocLine = shipDocLinesMapper.selectByPrimaryKey(docLineId);
                    ShipDocLines mergeShipDocLine = mergeShipDocLines(newShipDocLine, shipDocLine);
                    //在同倉(cāng)庫(kù)和同物料的情況下判斷是不是新建的行病涨,如果是則拋出異常
                        if (shipDocLine.getLineNum().equals(newShipDocLine.getLineNum())) {
                            shipDocLinesMapper.updateByPrimaryKey(mergeShipDocLine);
                        }else {
                            throw new DatasetException("所屬倉(cāng)庫(kù)與物料已經(jīng)存在",newShipDocLine.getLineNum());
                        }

                } else {
                    Long lineNum = shipDocLinesMapper.selectShipDocLineNum(newShipDocLine.getShipDocId());
                    if (lineNum != null) {
                        Long i=lineNum+1;
                        newShipDocLine.setLineNum(i);
                        shipDocLinesMapper.insert(newShipDocLine);
                    }else {
                        newShipDocLine.setLineNum(Long.valueOf("1"));
                        shipDocLinesMapper.insert(newShipDocLine);
                    }

                }

            }
        }
    }
}

在行的編輯頁(yè)面要實(shí)現(xiàn)單個(gè)刪除行:需要自己重寫刪除方法

@Service
@Transactional(rollbackFor = Exception.class)
@Dataset("OrderLine")
public class OrderLineServiceImpl  implements OrderLineService, IDatasetService<OrderLineVO> {

    @Autowired
    private ShipDocLinesMapper shipDocLinesMapper;

    @Autowired
    private ShipDocMapper shipDocMapper;

    private Long id;

    @Override
    public List<?> queries(Map<String, Object> body, int page, int pageSize, String sortname, boolean isDesc) {
        Object shipDocId =  body.get("shipDocId");
        id =Long.valueOf(shipDocId.toString());
        return shipDocLinesMapper.selectOrderLine(id);
    }

    @Override
    public List<OrderLineVO> mutations(List<OrderLineVO> list) {
        for (OrderLineVO line : list) {
            switch (line.get__status()) {
                case DELETE:
                    deleteOrderLine(line);
                    break;
                default:
                    break;
            }
        }
        return list;
    }

    private void deleteOrderLine(OrderLineVO line) {
        ShipDoc shipDoc = shipDocMapper.selectByPrimaryKey(id);
        Long id1 = shipDocLinesMapper.selectShipDocLineId(shipDoc.getOrganizationId(), line.getInventoryItemId(), id);
        if (id1 != null) {
            shipDocLinesMapper.deleteByPrimaryKey(id1);
        }
    }
}

創(chuàng)建OrderHeader的DataSet.js和OrderLine的DataSet.js

注意要對(duì)其中fields和queries fields屬性中的lov進(jìn)行數(shù)據(jù)綁定(bind)富玷,不然在行的頁(yè)面不知道加載什么數(shù)據(jù)。注意在這里fields中的兩個(gè)lov,他們都綁定了organizationId赎懦;但是我在頭對(duì)應(yīng)的react組件中用來展示的是綁定的數(shù)據(jù)雀鹃,就沒有用這個(gè)lov對(duì)象。但是在行的DataSet中励两,也有一個(gè)lov黎茎,只是因?yàn)檫@里不光展示數(shù)據(jù),還要能通過lov編輯數(shù)據(jù)当悔,所以就設(shè)置與前面的lov的name相同的行name傅瞻。

function generateCode() {
    let date = new Date();
    let number = Math.round((Math.random()*1000));
    var month = ("0" + (date.getMonth() + 1)).slice(-2);
    while(number.toString().length<4){
        number ="0"+number;
    }
    return "D"+date.getFullYear()+""+month+""+date.getDate()+""+number;
}


export default {
    //主鍵字段名,一般用作級(jí)聯(lián)行表的查詢字段
    primaryKey: 'shipDocId',
    autoQuery: true,
    pageSize: 20,
    //對(duì)應(yīng)后臺(tái)ds的name盲憎,自動(dòng)生成約定的submitUrl, queryUrl, tlsUrl
    name: 'OrderHeader',
    //與后端對(duì)應(yīng)的列的描述
    fields: [
        {name: 'docNumber', type: 'string', label: '單據(jù)號(hào)', defaultValue: generateCode()},
        {name: 'docType', type: 'string', label: '單據(jù)類型', lookupCode: 'XXFND_DOC_TYPE'},
        {name: 'shippmentStatus', type: 'string', label: '單據(jù)狀態(tài)', lookupCode: 'XXFND_SHIPPMENT_STATUS'},
        {name: 'materialItem1', type: 'object', textField:'organizationId', label: '倉(cāng)庫(kù)ID',lovCode: 'XXINV_ISSUE_ORGANIZATION'},
        { name: 'organizationId', type: 'number', label: '倉(cāng)庫(kù)ID', bind: 'materialItem1.organizationId' },
        {name: 'materialItem2', type: 'object',textField:'organizationId', label: '目標(biāo)倉(cāng)庫(kù)ID',lovCode: 'XXINV_ISSUE_ORGANIZATION'},
        { name: 'toOrganizationId', type: 'number', label: '倉(cāng)庫(kù)ID', bind: 'materialItem2.organizationId' },
        {name: 'driverName', type: 'string', label: '司機(jī)姓名'},
        {name: 'driverPhone', type: 'string', label: '司機(jī)電話'},
        {name: 'createdBy', type: 'string', label: '創(chuàng)建人number'},
        {name: 'freight', type: 'string', label: '運(yùn)費(fèi)'},
        {name: 'memo', type: 'string', label: '備注'},
        {name: 'shippedTime', type: 'datetime', label: '發(fā)貨時(shí)間'},
    ],
    //查詢字段嗅骄,自動(dòng)生成查詢組件
    queryFields: [
        { name: 'shipDoc', type: 'object', textField: 'docNumber', label: '單據(jù)號(hào)', lovCode: 'XXINV_SHIP_DOC' },
        { name: 'docNumber', type: 'string', label: '單據(jù)號(hào)', bind: 'shipDoc.docNumber' },
        { name: 'materialItem3', type: 'object',textField:'organizationId', label: '倉(cāng)庫(kù)ID', lovCode: 'XXINV_ISSUE_ORGANIZATION' },
        { name: 'organizationId', type: 'number', label: '倉(cāng)庫(kù)ID', bind: 'materialItem3.organizationId' },
        { name: 'shippmentStatus', type: 'string', label: '單據(jù)狀態(tài)', lookupCode: 'XXFND_SHIPPMENT_STATUS'},
        { name: 'materialItem4', type: 'object',textField: 'itemNumber', label: '物料編碼', lovCode: 'XXINV_ITEMS' },
        { name: 'inventoryItemId', type: 'number', label: 'ERP物料ID', bind: 'materialItem4.inventoryItemId' },
        { name: 'issuedFlag', type: 'string', label: '行發(fā)貨狀態(tài)', lookupCode: 'XXFND_SHIPLINE_ISSUED_FLAG' },
        { name: 'docType', type: 'string', label: '單據(jù)類型', lookupCode: 'XXFND_DOC_TYPE' },
    ],
};

行的字段顯示就比較簡(jiǎn)單了,不過也是需要數(shù)據(jù)綁定的:

export default {
    name: 'OrderLine',
    fields: [
        { name: 'lineNum', type: 'string', label: '行號(hào)'},
        { name: 'materialItem', type: 'object',textField: 'itemNumber', required: true , label: '物料', lovCode: 'XXINV_ITEMS'},
        { name: 'inventoryItemId', type: 'number', label: 'ERP物料ID', bind: 'materialItem.inventoryItemId' },
        { name: 'itemNumber',type: 'string', label: '物料編碼', bind: 'materialItem.itemNumber'},
        { name: 'lotControl', type: 'string', label: '批次控制',},
        { name: 'description', type: 'string', label: '物料描述'},
        { name: 'lotNumber', type: 'string', label: '批次' },
        { name: 'lineQty', type: 'number', label: '行數(shù)量'},
        { name: 'issueReqQty', type: 'number', label: '待發(fā)貨數(shù)量'},
        { name: 'shippedQty', type: 'number', label: '已發(fā)貨數(shù)量' },
    ],
};

開發(fā)頭和行react組件

先開發(fā)頭的饼疙,其中我把編輯和查看作為參數(shù)寫死(0和1)傳入

import React from 'react';
import {Button, IntlField, Modal, Table, Tooltip} from 'choerodon-ui/pro';
import OrderLineModal from './OrderLineModal';

const { Column } = Table;
const modalKey = Modal.key();

export default ({ headerDS, lineDS }) => {
    let isCancel;
    let created;

    /**
     * 編輯修改
     * 數(shù)據(jù)校驗(yàn)成功時(shí)保存
     */
    async function handleOnOkOrderLineModal() {
        isCancel = false;
        if (await headerDS.current.validate()) {
            await headerDS.submit();
        } else {
            return false;
        }
    }

    function handleOnCancelOrderLineModal() {
        isCancel = true;
    }

    /**
     * 關(guān)閉編彈窗.
     *
     */
    function handleOnCloseOrderLineModal() {
        if (isCancel) {
            // 新建時(shí)取消溺森,移除dataSet記錄
            if (created) {
                headerDS.remove(created);
            } else {
                // 修改時(shí)取消 重置當(dāng)前記錄數(shù)據(jù)
                headerDS.current.reset();
                lineDS.reset();
            }
        }
        // 重置新建記錄標(biāo)記
        created = null;
    }

    /**
     * 打開彈窗.
     * @param shipDocId 頭Id
     * @param enabled
     */
    function openOrderLineModal(shipDocId,enabled) {
        if (!shipDocId) {
            created = headerDS.create();
        }
        // 如果是編輯狀態(tài) 單號(hào)不可編輯
        const isEditDisabled = !!shipDocId;
        // 如果為啟用狀態(tài) 只可以進(jìn)行查看 不能編輯數(shù)據(jù)
        const isEnableDisabled = (isEditDisabled) && (enabled === 1);
        // 如果為啟用狀態(tài) 編碼規(guī)則行,不可被選中
        lineDS.selection = isEnableDisabled ? false : 'multiple';
        Modal.open({
            //唯一鍵窑眯, 當(dāng)destroyOnClose為false時(shí)屏积,必須指定key。
            // 為了避免與其他modal的key重復(fù)伸但,可通過Modal.key()來獲取唯一key肾请。
            key: modalKey,
            //標(biāo)題
            title: shipDocId ? '編輯' : '添加',
            //抽屜模式
            drawer: true,
            //關(guān)閉時(shí)是否銷毀
            destroyOnClose: true,
            //同時(shí)顯示ok和cancel按鈕,false的時(shí)候只顯示ok按鈕
            okCancel: !isEnableDisabled,
            //確認(rèn)按鈕文字
            okText: !isEnableDisabled ? '保存' : '關(guān)閉',
            //點(diǎn)擊確定回調(diào)更胖,返回false Promise.resolve(false)或
            // Promise.reject()不會(huì)關(guān)閉铛铁, 其他自動(dòng)關(guān)閉
            onOk: !isEnableDisabled ? handleOnOkOrderLineModal : handleOnCancelOrderLineModal,
            //點(diǎn)擊取消回調(diào),返回false Promise.resolve(false)或
            // Promise.reject()不會(huì)關(guān)閉却妨, 其他自動(dòng)關(guān)閉
            onCancel: handleOnCancelOrderLineModal,
            //關(guān)閉后回調(diào)
            afterClose: handleOnCloseOrderLineModal,
            children: (
                <OrderLineModal headerDS={headerDS} lineDS={lineDS} isEditDisabled={isEditDisabled} isEnableDisabled={isEnableDisabled} />
            ),
            style: {
                width: 1100,
            },
        });
    }

    const addBtn = (
        <Button
            icon="playlist_add"
            funcType="flat"
            color="blue"
            onClick={() => openOrderLineModal(null, null)}
        >
            {'添加'}
        </Button>
    );

    const excelBtn = (
        <Button
            icon="format_align_justify"
            funcType="raised"
            color="blue"
        >
            {'Excel導(dǎo)出'}
        </Button>
    );
    /**
     * 渲染表格內(nèi)容.
     */
    return (
        <Table buttons={[excelBtn,'delete',addBtn]} dataSet={headerDS} queryFieldsLimit={8}>
            <Column name="docNumber"  />
            <Column name="docType"  />
            <Column name="shippmentStatus"  />
            <Column name="organizationId" />
            <Column name="toOrganizationId" />
            <Column name="driverName" />
            <Column name="driverPhone" />
            <Column name="createdBy" />
            <Column header={'編輯'} align="center" width={120}
                    renderer={({ record})=>{
                        return (
                            <Button
                                funcType="flat"
                                icon={'mode_edit'}
                                onClick={() => openOrderLineModal(record.get('shipDocId'),0)}
                             />

                        );
                    }}/>
            <Column
                header={'查看'}
                align="center"
                width={120}
                renderer={({ record}) => {
                    return (
                            <Button
                                funcType="flat"
                                icon={'visibility'}
                                onClick={() => openOrderLineModal(record.get('shipDocId'),1)}
                            />
                    );
                }}
            />
        </Table>
    );

};

再開發(fā)行的react組件饵逐。行中form標(biāo)簽中的就是頭傳過來的字段,其中的時(shí)間 代碼維護(hù) lov的標(biāo)簽需要使用對(duì)應(yīng)的彪标。 注意名稱必須對(duì)應(yīng)倍权。時(shí)間空間有個(gè)import moment from 'moment';

import React from 'react';
import {
    Button,
    CheckBox,
    DatePicker,
    DateTimePicker,
    Form,
    Lov,
    NumberField,
    Select,
    Table,
    TextField
} from 'choerodon-ui/pro';
import moment from 'moment';

const { Column } = Table;

export default ({ headerDS, lineDS, isEditDisabled, isEnableDisabled }) => {


    let btnGroup = [];
    if (!isEnableDisabled) {
        btnGroup = ['add', 'delete'];
    }


    return (
        <div>
            <Form
                columns={3}
                abelWidth={100}

            >
                <TextField name="docNumber" label={'單號(hào)'} dataSet={headerDS} required disabled={isEditDisabled}/>
                <DateTimePicker name="shippedTime" label={'發(fā)貨時(shí)間'} dataSet={headerDS}  disabled={isEnableDisabled} min={moment()}/>
                <Select name="docType" label={'單據(jù)類型'} dataSet={headerDS} required disabled={isEnableDisabled}/>
                <Lov  name="materialItem1" label={'倉(cāng)庫(kù)'} dataSet={headerDS} required disabled={isEnableDisabled} />
                <Lov  name="materialItem2" label={'接收倉(cāng)庫(kù)'} dataSet={headerDS}  disabled={isEnableDisabled} />
                <Select name="shippmentStatus" label={'發(fā)貨單狀態(tài)'} dataSet={headerDS} required  disabled={isEditDisabled} />
                <TextField name="driverName" label={'司機(jī)姓名'} dataSet={headerDS}  disabled={isEnableDisabled} />
                <TextField name="driverPhone" label={'司機(jī)電話'} dataSet={headerDS}  disabled={isEnableDisabled} />
                <TextField name="freight" label={'運(yùn)費(fèi)'} dataSet={headerDS}  disabled={isEnableDisabled} />
                <TextField name="memo" label={'備注'} dataSet={headerDS}  disabled={isEnableDisabled} />
                <TextField name="createdBy" label={'創(chuàng)建人'} dataSet={headerDS}  disabled={isEditDisabled} />
            </Form>
            <Table
                buttons={btnGroup}
                dataSet={lineDS}
                header={'行'}
            >
                <Column name="lineNum"  label={'行號(hào)'} />
                <Column name="materialItem" label={'物料編碼'} editor={<Lov />}/>
                <Column name="lotControl"  label={'批次控制'} />
                <Column name="description"  label={'物料描述'} />
                <Column name="lotNumber"  label={'批次'} editor={<NumberField/>}/>
                <Column name="lineQty"  label={'行數(shù)量'} editor={<NumberField/>}/>
                <Column name="issueReqQty"  label={'待發(fā)貨數(shù)量'} editor={<NumberField/>}/>
                <Column name="shippedQty"  label={'已發(fā)貨數(shù)量'} editor={<NumberField/>}/>
            </Table>
            <div>
                <Button
                    icon="format_align_justify"
                    funcType="raised"
                    color="blue"
                >
                    {'打印'}
                </Button>
                <Button
                    icon="format_align_justify"
                    funcType="raised"
                    color="blue"
                >
                    {'單據(jù)確認(rèn)'}
                </Button>
                <Button
                    icon="format_align_justify"
                    funcType="raised"
                    color="blue"
                >
                    {'確認(rèn)發(fā)貨'}
                </Button>
            </div>
        </div>
    );
};

編輯xlsx,添加路由

這里的頭與行的順序最好不要打亂捞烟,我之前亂了一行薄声,初始化表,插入不了/ship-order/order下面的數(shù)據(jù)

xlsx添加服務(wù).png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末题画,一起剝皮案震驚了整個(gè)濱河市默辨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌苍息,老刑警劉巖缩幸,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壹置,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡表谊,警方通過查閱死者的電腦和手機(jī)钞护,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爆办,“玉大人难咕,你說我怎么就攤上這事⊙罕疲” “怎么了步藕?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)挑格。 經(jīng)常有香客問我,道長(zhǎng)沾歪,這世上最難降的妖魔是什么漂彤? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮灾搏,結(jié)果婚禮上挫望,老公的妹妹穿的比我還像新娘。我一直安慰自己狂窑,他們只是感情好媳板,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泉哈,像睡著了一般蛉幸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丛晦,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天奕纫,我揣著相機(jī)與錄音,去河邊找鬼烫沙。 笑死匹层,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锌蓄。 我是一名探鬼主播升筏,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼瘸爽!你這毒婦竟也來了您访?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蝶糯,失蹤者是張志新(化名)和其女友劉穎洋只,沒想到半個(gè)月后辆沦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡识虚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年肢扯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片担锤。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蔚晨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肛循,到底是詐尸還是另有隱情铭腕,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布多糠,位于F島的核電站累舷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏夹孔。R本人自食惡果不足惜被盈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搭伤。 院中可真熱鬧只怎,春花似錦、人聲如沸怜俐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拍鲤。三九已至贴谎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間殿漠,已是汗流浹背赴精。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绞幌,地道東北人蕾哟。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像莲蜘,于是被迫代替她去往敵國(guó)和親谭确。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 【日精進(jìn)第33天】 一票渠、【學(xué)~勤學(xué)】 ①日常課誦 誦讀《道德經(jīng)》第7章和注解一遍逐哈,《京瓷哲學(xué)》第七條问顷,《定位》第四...
    julia2000閱讀 233評(píng)論 0 1
  • 文|素言錦心 昨天周日送孩子上課,距離下課還有兩小時(shí)算途,回家太耽誤時(shí)間嘴瓤,還沒忙完手頭的事情就要?jiǎng)由碓偃ソ雍⒆永啵谑呛?..
    素言錦心閱讀 1,104評(píng)論 3 2
  • 今天學(xué)習(xí)了bootstrap,預(yù)計(jì)再學(xué)習(xí)一天
    1eb034fb5715閱讀 77評(píng)論 0 0
  • 早戀胧奔,從這個(gè)詞語(yǔ)誕生的那一天龙填,就注定它是個(gè)貶義詞岩遗。因?yàn)樗巧弦惠厡?duì)下一輩青春期青澀的初戀的鄙視宿礁。 早戀梆靖,字面意思過...
    劉苔米閱讀 274評(píng)論 0 1