1. 배열함수

때로는 SUM만 가지고 해결될 수 없는 조건이 들어가 있는 합계를 원할때가 있다.

이럴때 배열함수라는 것을 사용하는데 한 함수에서 여러 범위를 지정하고 조건을 달아놓으면

해당 조건에 해당하는 건만 필터링되어 결과물을 얻을 수 있다.


2. 예시

 : 아래 테이블에서 원하는 두가지 조건에 해당하는 건들의 출장비 합을 구해보도록 하겠다.

  

 이름 

 성별 

출장비

 김모 

  

 30,000

 박모 

  

 80,000

 김모 

  

 60,000

 강모 

  

 70,000

 황모 

  

 80,000

 김모 

  

 90,000


 - 이름이 "김모"씨고 성별이 "여"인 사람의 출장비 합계를 구하라.

1) 먼저 임의의 비어있는 컬럼에 SUM함수를 사용하여 SUM함수 내에서 조건을 단다.   

=SUM((B4:B9="김모")*(C4:C9="여")*D4:D9) 

   - B4:B9 : 이름 전체범위

   - B4:B9="김모" : 이름이 "김모"

   - C4:C9 : 성별 전체범위

   - C4:C9="여" : 성별이 "여"인 조건

   - (B4:B9="김모") * (C4:C9="여") : 가운데 "*"로 조건을 연결시킨다.(계속연결가능)

   - 마지막에는 실제 합계를 구하고자 하는 출장비 전체범위를 "*"로 연결한다

   - 여기까지는 일반적인 사항임.

   

2) 배열함수적용   

=SUM((B4:B9="김모")*(C4:C9="여")*D4:D9) 

   - 여기까지 입력 후 수식 제일 오른쪽에 커서를 두고 CTRL + SHIFT + ENTER 키를 같이 눌러본다. 

     그러면 아래와 같이 수식에 중괄호가 덫씌워져 표시된다.    

={SUM((B4:B9="김모")*(C4:C9="여")*D4:D9)}

   - 그러면 아래와 같은 원하는 결과가 나올것이다.



만족하셨나요? ~~~~~~~



1. Kanboard 란?

  : KANBAN이란 도요다 자동차의 현장에서 작업대상들을 각각 적은 마분지 카드를 보드에 붙여넣은 다음 

    작업대상, 작업준비중, 진행중, 완료 이런식(단계는 넣을수도 있고 뺄수도 있음)으로 구분하여 생산진행과정을 

    관리 방식에서 유래되었다. (각 단계가 완료되면 그 다음단계에 마분지를 옮겨붙이는 방식)

    기것을 기반으로 만들어진 프로그램이 Kanboard다.

    요즘 유행하는 Scrum이 여기서부터 시작된 듯...


2. 프로그램 설명

 - 사이트 : https://kanboard.org/

 - 라이센스 : MIT (기업에서도 무료로 사용할 수 있는 라이센스, 수정가능)

 - 소스 다운로드 : https://github.com/kanboard/kanboard/releases

 - 언어 : PHP

 - 기타 : 한글지원

 - Kanban board 형태로 보기

   : 마우스로 해당항목을 옮기면 진행상태가 바로 업데이트된다. 

  - 목록으로 보기



  - Gantt Chart 추가가능

  : 차트 內에서 기간을 움직이면 자동으로 날짜가 수정됨.


3. 설치

   1) 파일 다운로드

      : https://github.com/kanboard/kanboard/releases 에서 최신버전을 다운로드 받아(zip) 압축을 푼다.

      - 압축을 풀면 아래와 같은 폴더구조가 나온다.


   2) 일반적인 저렴한(월 1,000원정도) 웹호스팅 서버(Apache서버+PHP가 설치된)에 FTP로 접속해서 

       Apache서버 root 폴더인 public_html 폴더 아래에 압축을 푼 파일을 넣는다.

   


4. 본인이 가입한 웹호스팅 url로 접속해본다.

   : 그런다음 기본적인 셋팅을 따라하면 됨.


만족하셨나요? ~~~~~~~



앞에 "제품정보"에서 "원산지"항목을 DB공통코드와 연동하여 콤보박스로 만드는 방법에 대해 설명하겠음.


1. 공토통코드관리에 원산지 추가

   - 아래 이미지와 같이 한국,미국,일본 원산지 항목을 추가한다.

   - 분류코드 : ORIGIN

   - 분류명 : 원산지

   - 코드는 각각 : KR, US, JP 로 입력

   - 코드값에 각각 : 한국,미국,일본 을 입력

   - 정렬 : 원하는 정렬순으로 숫자를 입력한다.

   - 사용여부 : 모두체크


2. javascript 수정

 - 앞에서 만든 webapp/assets/js/view/shopmng/product.js 를 수정한다.

var fnObj = {};

var ACTIONS = axboot.actionExtend(fnObj, {

    PAGE_SEARCH: function (caller, act, data) {

        axboot.ajax({

            type: "GET",

            url: ["product"],

            data: caller.searchView.getData(),

            callback: function (res) {

                caller.gridView01.setData(res);

            }

        });

        return false;

    },

    PAGE_SAVE: function (caller, act, data) {

        var saveList = [].concat(caller.gridView01.getData("modified"));

        saveList = saveList.concat(caller.gridView01.getData("deleted"));


        axboot.ajax({

            type: "PUT",

            url: ["product"],

            data: JSON.stringify(saveList),

            callback: function (res) {

                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

                axToast.push(LANG("onsave"));

            }

        });

    },

    ITEM_ADD: function (caller, act, data) {

        caller.gridView01.addRow();

    },

    ITEM_DEL: function (caller, act, data) {

        caller.gridView01.delRow("selected");

    }

});


var CODE = {};  //추가

// fnObj 기본 함수 스타트와 리사이즈

fnObj.pageStart = function () {

    var _this = this;  


    axboot

        .call({

            type: "GET", url: "/api/v1/commonCodes", data: {groupCd: "ORIGIN"},

            callback: function (res) {

                var originList = [];

                res.list.forEach(function (n) {

                originList.push({

                                //여기서 CD, NM의 문자를 사용한 이유는 아래 gridView의 원산지의

                                // optionValue와 optionText를 CD와 NM으로 사용했기 때문이다.

                CD: n.code, NM: n.name + "(" + n.code + ")"                        

                    });

                });

                this.originList = originList;

                //alert(JSON.stringify(this.originList));

            }

        })

        .done(function () {

            CODE = this; // this는 call을 통해 수집된 데이터들.

//위쪽 추가

            _this.pageButtonView.initView();  // this --> _this 로 변경

            _this.searchView.initView();  // this --> _this 로 변경

            _this.gridView01.initView();  // this --> _this 로 변경

            ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

        });  //추가

};


fnObj.pageResize = function () {


};



fnObj.pageButtonView = axboot.viewExtend({

    initView: function () {

        axboot.buttonClick(this, "data-page-btn", {

            "search": function () {

                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

            },

            "save": function () {

                ACTIONS.dispatch(ACTIONS.PAGE_SAVE);

            }

        });

    }

});


//== view 시작

/**

 * searchView

 */

fnObj.searchView = axboot.viewExtend(axboot.searchView, {

    initView: function () {

        this.target = $(document["searchView0"]);

        this.target.attr("onsubmit", "return ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);");

        this.filter = $("#filter");

    },

    getData: function () {

        return {

            pageNumber: this.pageNumber,

            pageSize: this.pageSize,

            filter: this.filter.val()

        }

    }

});



/**

 * gridView

 */

fnObj.gridView01 = axboot.viewExtend(axboot.gridView, {

    initView: function () {

        var _this = this;

        this.originList = CODE.originList; //추가

        

        this.target = axboot.gridBuilder({

            showRowSelector: true,

            frozenColumnIndex: 0,

            sortable: true,

            multipleSelect: true,

            target: $('[data-ax5grid="grid-view-01"]'), 

            columns: [

            {key: "prdtCd", label: "제품코드", width: 100, align: "center", editor: "text"},

                {key: "prdtNm", label: "제품명", width: 200, align: "center", editor: "text"},

                {

                    key: "origin", label: "원산지", width: 100,align: "center", editor: {

                    type: "select", config: {

                        columnKeys: {

                            optionValue: "CD", optionText: "NM"

                        },

                        options: this.originList //추가

                        /*

                            // this.originList 에 아래와 같이 셋팅되어있다. 만약 연동하지 않고 하드코딩으로 

                            // 처리하고 싶으면 this.originList 자리에 아래와 같은 형태로만 입력시켜주면 된다.

                        [

                        {"CD":"KR","NM":"한국(KR)"},

                        {"CD":"US","NM":"미국(US)"},

                        {"CD":"JP","NM":"일본(JP)"}

                        ]

                        */

                        

                    }

                  }

                },

                {key: "purchasePrice", label: "매입가격", width: 150, align: "right", editor: "number"},

                {key: "salesPrice", label: "판매가격", width: 150, align: "right", editor: "number"}          

            ],

            body: {

                onClick: function () {

                    this.self.select(this.dindex, {selectedClear: true});

                }

            }

        });


        axboot.buttonClick(this, "data-grid-view-01-btn", {

            "add": function () {

                ACTIONS.dispatch(ACTIONS.ITEM_ADD);

            },

            "delete": function () {

                ACTIONS.dispatch(ACTIONS.ITEM_DEL);

            }

        });

    },

    getData: function (_type) {

        var list = [];

        var _list = this.target.getList(_type);


        if (_type == "modified" || _type == "deleted") {

            list = ax5.util.filter(_list, function () {

                return this.prdtCd;

            });

        } else {

            list = _list;

        }

        return list;

    },

    addRow: function () {

        this.target.addRow({__created__: true}, "last");

    }

}); 


3. 화면확인 

  - 콤보박스가 잘 나오는 것을 볼 수 있다.



만족하셨나요? ~~~~~~~맘에 드셨다면 아래 꾹~~~


◆ 정상적인 CRUD를 위한 JAVA소스 수정


1. 먼저 maven 의 generate-sources 를 해준다.    


           그러면 아래와 같이 target하위에 소스가 생긴다.


2.  JAVA 소스 수정

 - com/dasdes/shopmng/contrillers/ProductController.java : 조회조건 받을수 있도록 수정 

 - com/dasdes/shopmng/domain/prdt/Product.java           : 필수값체크

 - com/dasdes/shopmng/domain/BaseService.java             : Entity 와 연결되는 QProduct.java(위 generate에서 생성됨) 선언

 - com/dasdes/shopmng/domain/prdt/ProductService.java  : 조회조건 filter추가 및 list 리턴객체 교체


  1) ProductController.java 수정

package com.dasdes.shopmng.controllers;


import com.chequer.axboot.core.api.response.Responses;

import com.chequer.axboot.core.controllers.BaseController;

import com.chequer.axboot.core.parameter.RequestParams;

import org.springframework.stereotype.Controller;

import com.chequer.axboot.core.api.response.ApiResponse;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import com.dasdes.shopmng.domain.prdt.Product;

import com.dasdes.shopmng.domain.prdt.ProductService;

import com.wordnik.swagger.annotations.ApiImplicitParam;  //추가

import com.wordnik.swagger.annotations.ApiImplicitParams; //추가


import javax.inject.Inject;

import java.util.List;


@Controller

@RequestMapping(value = "/api/v1/product")

public class ProductController extends BaseController {


    @Inject

    private ProductService productService;


    @RequestMapping(method = RequestMethod.GET, produces = APPLICATION_JSON)

    @ApiImplicitParams({

            @ApiImplicitParam(name = "prdtCd", value = "제품코드", dataType = "String", paramType = "query"),

            @ApiImplicitParam(name = "prdtNm", value = "제품명", dataType = "String", paramType = "query"),

            @ApiImplicitParam(name = "filter", value = "검색어", dataType = "String", paramType = "query")

    })    

    public Responses.ListResponse list(RequestParams<Product> requestParams) {

        List<Product> list = productService.gets(requestParams);

        return Responses.ListResponse.of(list);

    }


    @RequestMapping(method = {RequestMethod.PUT}, produces = APPLICATION_JSON)

    public ApiResponse save(@RequestBody List<Product> request) {

        productService.savePrdt(request);

        return ok();

    }

}


  2) Product.java 수정

package com.dasdes.shopmng.domain.prdt;


import com.chequer.axboot.core.annotations.ColumnPosition;

import com.dasdes.shopmng.domain.SimpleJpaModel;

import lombok.*;

import org.apache.ibatis.type.Alias;

import org.hibernate.annotations.DynamicInsert;

import org.hibernate.annotations.DynamicUpdate;

import com.chequer.axboot.core.annotations.Comment;

import javax.persistence.*;


import javax.validation.constraints.NotNull;



@Setter

@Getter

@DynamicInsert

@DynamicUpdate

@Entity

@EqualsAndHashCode(callSuper = true)

@Table(name = "prdt_base")

@Comment(value = "")

@Alias("product")

public class Product extends SimpleJpaModel<String> {


@Id

@Column(name = "prdt_cd", length = 50, nullable = false)

@NotNull(message = "제품코드를 입력하세요")   //pk이므로 not null 체크 추가

@Comment(value = "제품코드")

private String prdtCd;


@Column(name = "prdt_nm", length = 50, nullable = false)

@Comment(value = "제품명")

private String prdtNm;


@Column(name = "origin", length = 50, nullable = false)

@Comment(value = "원산지")

private String origin;


@Column(name = "purchase_price", precision = 10, nullable = false)

@Comment(value = "매입가격")

private Integer purchasePrice;


@Column(name = "sales_price", precision = 10, nullable = false)

@Comment(value = "판매가격")

private Integer salesPrice;



    @Override

    public String getId() {

        return prdtCd;

    }


3) BaseService.java 수정

package com.dasdes.shopmng.domain;


import com.dasdes.shopmng.domain.code.QCommonCode;

import com.dasdes.shopmng.domain.file.QCommonFile;

import com.dasdes.shopmng.domain.program.QProgram;

import com.dasdes.shopmng.domain.program.menu.QMenu;

import com.dasdes.shopmng.domain.user.QUser;

import com.dasdes.shopmng.domain.user.auth.QUserAuth;

import com.dasdes.shopmng.domain.user.auth.menu.QAuthGroupMenu;

import com.dasdes.shopmng.domain.user.role.QUserRole;

import com.chequer.axboot.core.domain.base.AXBootBaseService;

import com.chequer.axboot.core.domain.base.AXBootJPAQueryDSLRepository;

import com.dasdes.shopmng.domain.prdt.QProduct;  //추가


import java.io.Serializable;



public class BaseService<T, ID extends Serializable> extends AXBootBaseService<T, ID> {


    protected QUserRole qUserRole = QUserRole.userRole;

    protected QAuthGroupMenu qAuthGroupMenu = QAuthGroupMenu.authGroupMenu;

    protected QCommonCode qCommonCode = QCommonCode.commonCode;

    protected QUser qUser = QUser.user;

    protected QProgram qProgram = QProgram.program;

    protected QUserAuth qUserAuth = QUserAuth.userAuth;

    protected QMenu qMenu = QMenu.menu;

    protected QCommonFile qCommonFile = QCommonFile.commonFile;

    protected QProduct qProduct = QProduct.product; //추가


    protected AXBootJPAQueryDSLRepository<T, ID> repository;


    public BaseService() {

        super();

    }


    public BaseService(AXBootJPAQueryDSLRepository<T, ID> repository) {

        super(repository);

        this.repository = repository;

    }

}


4) ProductService.java 수정

package com.dasdes.shopmng.domain.prdt;


import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional; //추가


import com.dasdes.shopmng.domain.BaseService;

import com.querydsl.core.BooleanBuilder;  //추가


import javax.inject.Inject;

import com.chequer.axboot.core.parameter.RequestParams;

import java.util.List;


@Service

public class ProductService extends BaseService<Product, String> {

    private ProductRepository productRepository;


    @Inject

    public ProductService(ProductRepository productRepository) {

        super(productRepository);

        this.productRepository = productRepository;

    }


    public List<Product> gets(RequestParams<Product> requestParams) {

    String prdtCd=requestParams.getString("prdtCd", "");

    String prdtNm=requestParams.getString("prdtNm", "");

    String filter = requestParams.getString("filter");

   

    BooleanBuilder builder = new BooleanBuilder();



        if (isNotEmpty(prdtCd)) {

            builder.and(qProduct.prdtCd.eq(prdtCd));

        }


        if (isNotEmpty(prdtNm)) {

            builder.and(qProduct.prdtNm.eq(prdtNm));

        }


        List<Product> prdtList = select().from(qProduct).where(builder).orderBy(qProduct.prdtCd.asc(), qProduct.prdtNm.asc()).fetch();


        if (isNotEmpty(filter)) {

        prdtList = filter(prdtList, filter);

        }

   

        return prdtList;

    }

    

    //저장

    @Transactional

    public void savePrdt(List<Product> product) {

        save(product);

    }

}


3. 서버구동 및 CRUD 테스트

1) 저장/수정테스트 : 정상작동 OK


2) 검색필터 테스트 : 필터링 잘 됨. OK



Axboot 신규화면 만들기 4 - Detail 내역 컬럼 콤보박스 만들기(DB연동) 에서 계속........


만족하셨나요? ~~~~~~~

◆  화면소스 작성

   1. 기존에 만들어져있는 화면들을 보고 구성하고자 하는 형태를 미리 본다.

       - 왼쪽의 메뉴를 눌러 화면형태를 보니 공통코드관리화면이 제품코드관리하는데 적합한 화면일 것 같다.

    


   2. 기본적으로 axboot 화면의 jsp 위치는 webapp > jsp 하위에 있고, js 는 webapp > assets > js 하위에 있다.

       시스템공통관련 소스는 그 하위의 system이라는 폴더에 따로 모여있다.

   3. 일단 우리가 만드는 화면소스들은 한폴더에서 관리하기 위해 아래와 같이 shopnmg 라는 폴더를 생성한다. 

      (본인이 원하는 위치로 해도 됨)

       - js 용 폴더 생성 : webapp > assets > js > view > shopmng

 - jsp용 폴더 생성 : webapp > jsp > shopmng 

   4. 그런다음 공통코드관리의 화면소스를 복사하여 이름을 바꾸자.

   이름은 product.js, product.jsp 로 만들도록 하겠다.

     

   5. 소스수정

       1) webapp하위의 axboot.config.js 에 js의 호출할 약식이름과 full명의 mapping설정

    js에서 product로 호출하면 controller에 "/api/v1/product" 로 호출할 수 있도록 내용을 추가한다.


   6. product.js 파일 수정 : url 경로와 gridView를 제품테이블 기준으로 변경한다.

       : 화면속성은  http://localhost:8080/modelExtractor/db 로 접속시 나오는 필드명을 사용하면 된다.

var fnObj = {};

var ACTIONS = axboot.actionExtend(fnObj, {

    PAGE_SEARCH: function (caller, act, data) {

        axboot.ajax({

            type: "GET",

            url: ["product"], 

            data: caller.searchView.getData(),

            callback: function (res) {

                caller.gridView01.setData(res);

            }

        });

        return false;

    },

    PAGE_SAVE: function (caller, act, data) {

        var saveList = [].concat(caller.gridView01.getData("modified"));

        saveList = saveList.concat(caller.gridView01.getData("deleted"));


        axboot.ajax({

            type: "PUT",

            url: ["product"],

            data: JSON.stringify(saveList),

            callback: function (res) {

                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

                axToast.push(LANG("onsave"));

            }

        });

    },

    ITEM_ADD: function (caller, act, data) {

        caller.gridView01.addRow();

    },

    ITEM_DEL: function (caller, act, data) {

        caller.gridView01.delRow("selected");

    }

});


// fnObj 기본 함수 스타트와 리사이즈

fnObj.pageStart = function () {

    this.pageButtonView.initView();

    this.searchView.initView();

    this.gridView01.initView();


    ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

};


fnObj.pageResize = function () {


};


fnObj.pageButtonView = axboot.viewExtend({

    initView: function () {

        axboot.buttonClick(this, "data-page-btn", {

            "search": function () {

                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

            },

            "save": function () {

                ACTIONS.dispatch(ACTIONS.PAGE_SAVE);

            }

        });

    }

});


//== view 시작

/**

 * searchView

 */

fnObj.searchView = axboot.viewExtend(axboot.searchView, {

    initView: function () {

        this.target = $(document["searchView0"]);

        this.target.attr("onsubmit", "return ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);");

        this.filter = $("#filter");

    },

    getData: function () {

        return {

            pageNumber: this.pageNumber,

            pageSize: this.pageSize,

            filter: this.filter.val()

        }

    }

});



/**

 * gridView

 */

fnObj.gridView01 = axboot.viewExtend(axboot.gridView, {

    initView: function () {

        var _this = this;

        this.target = axboot.gridBuilder({

            showRowSelector: true,

            frozenColumnIndex: 0,

            sortable: true,

            multipleSelect: true,

            target: $('[data-ax5grid="grid-view-01"]'),

            columns: [

                {key: "prdtCd", label: "제품코드", width: 100, align: "center", editor: "text"},

                {key: "prdtNm", label: "제품명", width: 200, align: "center", editor: "text"},

                {key: "origin", label: "원산지", width: 100, align: "center", editor: "text"},

                {key: "purchasePrice", label: "매입가격", width: 150, align: "right", editor: "number"},

                {key: "salesPrice", label: "판매가격", width: 150, align: "right", editor: "number"}                

            ],

            body: {

                onClick: function () {

                    this.self.select(this.dindex, {selectedClear: true});

                }

            }

        });


        axboot.buttonClick(this, "data-grid-view-01-btn", {

            "add": function () {

                ACTIONS.dispatch(ACTIONS.ITEM_ADD);

            },

            "delete": function () {

                ACTIONS.dispatch(ACTIONS.ITEM_DEL);

            }

        });

    },

    getData: function (_type) {

        var list = [];

        var _list = this.target.getList(_type);


        if (_type == "modified" || _type == "deleted") {

            list = ax5.util.filter(_list, function () {

                return this.prdtCd;  //이부분을 키값으로 수정해줘야 변경된 값을 인식해서 서버로 날려줄 수 있다.

            });

        } else {

            list = _list;

        }

        return list;

    },

    addRow: function () {

        this.target.addRow({__created__: true, posUseYn: "N", useYn: "Y"}, "last");

    }

});


7. product.jsp 수정 : js 가 있는 위치를 변경해준다.

<%@ page contentType="text/html; charset=UTF-8" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="ax" tagdir="/WEB-INF/tags" %>


<ax:set key="system-common-code-version" value="1.0.0"/>

<ax:set key="title" value="${pageName}"/>

<ax:set key="page_desc" value="${pageRemark}"/>

<ax:set key="page_auto_height" value="true"/>


<ax:layout name="base">

    <jsp:attribute name="script">

        <ax:script-lang key="ax.script" var="LANG" />

        <ax:script-lang key="ax.admin" var="COL" />

        <script type="text/javascript" src="<c:url value='/assets/js/view/shopmng/product.js' />"></script>

    </jsp:attribute>

    <jsp:body>


        <ax:page-buttons></ax:page-buttons>


        <div role="page-header">

            <ax:form name="searchView0">

                <ax:tbl clazz="ax-search-tbl" minWidth="500px">

                    <ax:tr>

                        <ax:td label='ax.admin.search' width="300px">

                            <ax:input type="text" name="filter" id="filter" clazz="form-control" placeholder="ax.admin.input.search"/>

                        </ax:td>

                    </ax:tr>

                </ax:tbl>

            </ax:form>

            <div class="H10"></div>

        </div>


        <ax:split-layout name="ax1" orientation="horizontal">

            <ax:split-panel width="*" style="">


                <!-- 목록 -->

                <div class="ax-button-group" data-fit-height-aside="grid-view-01">

                    <div class="left">

                        <h2>

                            <i class="cqc-list"></i>

                            <!--<ax:lang id="ax.admin.commoncode.title"/>-->

                            제품코드 관리

                        </h2>

                    </div>

                    <div class="right">

                        <button type="button" class="btn btn-default" data-grid-view-01-btn="add"><i class="cqc-circle-with-plus"></i> <ax:lang id="ax.admin.add"/></button>

                        <button type="button" class="btn btn-default" data-grid-view-01-btn="delete"><i class="cqc-circle-with-minus"></i> <ax:lang id="ax.admin.delete"/></button>

                    </div>

                </div>

                <div data-ax5grid="grid-view-01" data-fit-height-content="grid-view-01" style="height: 300px;"></div>


            </ax:split-panel>

        </ax:split-layout>



    </jsp:body>

</ax:layout> 

8. JAVA 소스 수정

 - com/dasdes/shopmng/contrillers/ProductController.java : 조회조건 받을수 있도록 수정 

 - com/dasdes/shopmng/domain/prdt/Product.java           : 필수값체크

 - com/dasdes/shopmng/domain/prdt/ProductService.java  : 조회조건 filter추가 및 list 리턴객체 교체


ProductController.java

package com.dasdes.shopmng.controllers;


import com.chequer.axboot.core.api.response.Responses;

import com.chequer.axboot.core.controllers.BaseController;

import com.chequer.axboot.core.parameter.RequestParams;

import org.springframework.stereotype.Controller;

import com.chequer.axboot.core.api.response.ApiResponse;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import com.dasdes.shopmng.domain.prdt.Product;

import com.dasdes.shopmng.domain.prdt.ProductService;


import javax.inject.Inject;

import java.util.List;


@Controller

@RequestMapping(value = "/api/v1/product")

public class ProductController extends BaseController {


    @Inject

    private ProductService productService;


    @RequestMapping(method = RequestMethod.GET, produces = APPLICATION_JSON)

    public Responses.ListResponse list(RequestParams<Product> requestParams) {

        List<Product> list = productService.gets(requestParams);

        return Responses.ListResponse.of(list);

    }


    @RequestMapping(method = {RequestMethod.PUT}, produces = APPLICATION_JSON)

    public ApiResponse save(@RequestBody List<Product> request) {

        productService.save(request);

        return ok();

    }

}


4. 화면소스 작성

   1) 기존에 만들어져있는 화면들을 보고 구성하고자 하는 형태를 미리 본다.

       - 왼쪽의 메뉴를 눌러 화면형태를 보니 공통코드관리화면이 제품코드관리하는데 적합한 화면일 것 같다.

    


   2) 기본적으로 axboot 화면의 jsp 위치는 webapp > jsp 하위에 있고, js 는 webapp > assets > js 하위에 있다.

       시스템공통관련 소스는 그 하위의 system이라는 폴더에 따로 모여있다.

   3) 일단 우리가 만드는 화면소스들은 한폴더에서 관리하기 위해 아래와 같이 shopnmg 라는 폴더를 생성한다. 

      (본인이 원하는 위치로 해도 됨)

       - js 용 폴더 생성 : webapp > assets > js > view > shopmng

 - jsp용 폴더 생성 : webapp > jsp > shopmng 

   4) 그런다음 공통코드관리의 화면소스를 복사하여 이름을 바꾸자.

   이름은 product.js, product.jsp 로 만들도록 하겠다.

     

   5) 소스수정

       a) axboot.config.js 에 js의 호출할 약식이름과 full명의 mapping설정

    js에서 product로 호출하면 controller에 "/api/v1/product" 로 호출할 수 있도록 내용을 추가한다.


     6) product.js 파일 수정 : url 경로와 gridView를 제품테이블 기준으로 변경한다.

       : 화면속성은  http://localhost:8080/modelExtractor/db 로 접속시 나오는 필드명을 사용하면 된다.

var fnObj = {};

var ACTIONS = axboot.actionExtend(fnObj, {

    PAGE_SEARCH: function (caller, act, data) {

        axboot.ajax({

            type: "GET",

            url: ["product"], 

            data: caller.searchView.getData(),

            callback: function (res) {

                caller.gridView01.setData(res);

            }

        });

        return false;

    },

    PAGE_SAVE: function (caller, act, data) {

        var saveList = [].concat(caller.gridView01.getData("modified"));

        saveList = saveList.concat(caller.gridView01.getData("deleted"));


        axboot.ajax({

            type: "PUT",

            url: ["product"],

            data: JSON.stringify(saveList),

            callback: function (res) {

                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

                axToast.push(LANG("onsave"));

            }

        });

    },

    ITEM_ADD: function (caller, act, data) {

        caller.gridView01.addRow();

    },

    ITEM_DEL: function (caller, act, data) {

        caller.gridView01.delRow("selected");

    }

});


// fnObj 기본 함수 스타트와 리사이즈

fnObj.pageStart = function () {

    this.pageButtonView.initView();

    this.searchView.initView();

    this.gridView01.initView();


    ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

};


fnObj.pageResize = function () {


};


fnObj.pageButtonView = axboot.viewExtend({

    initView: function () {

        axboot.buttonClick(this, "data-page-btn", {

            "search": function () {

                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

            },

            "save": function () {

                ACTIONS.dispatch(ACTIONS.PAGE_SAVE);

            }

        });

    }

});


//== view 시작

/**

 * searchView

 */

fnObj.searchView = axboot.viewExtend(axboot.searchView, {

    initView: function () {

        this.target = $(document["searchView0"]);

        this.target.attr("onsubmit", "return ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);");

        this.filter = $("#filter");

    },

    getData: function () {

        return {

            pageNumber: this.pageNumber,

            pageSize: this.pageSize,

            filter: this.filter.val()

        }

    }

});



/**

 * gridView

 */

fnObj.gridView01 = axboot.viewExtend(axboot.gridView, {

    initView: function () {

        var _this = this;

        this.target = axboot.gridBuilder({

            showRowSelector: true,

            frozenColumnIndex: 0,

            sortable: true,

            multipleSelect: true,

            target: $('[data-ax5grid="grid-view-01"]'),

            columns: [

                {key: "prdtCd", label: "제품코드", width: 100, align: "center", editor: {type: "text", disabled: "notCreated"}},

                {key: "prdtNm", label: "제품명", width: 200, align: "center", editor: "text"},

                {key: "origin", label: "원산지", width: 100, align: "center", editor: "text"},

                {key: "purchasePrice", label: "매입가격", width: 150, align: "right", editor: "number"},

                {key: "salesPrice", label: "판매가격", width: 150, align: "right", editor: "number"}                

            ],

            body: {

                onClick: function () {

                    this.self.select(this.dindex, {selectedClear: true});

                }

            }

        });


        axboot.buttonClick(this, "data-grid-view-01-btn", {

            "add": function () {

                ACTIONS.dispatch(ACTIONS.ITEM_ADD);

            },

            "delete": function () {

                ACTIONS.dispatch(ACTIONS.ITEM_DEL);

            }

        });

    },

    getData: function (_type) {

        var list = [];

        var _list = this.target.getList(_type);


        if (_type == "modified" || _type == "deleted") {

            list = ax5.util.filter(_list, function () {

                return this.groupCd && this.code;

            });

        } else {

            list = _list;

        }

        return list;

    },

    addRow: function () {

        this.target.addRow({__created__: true, posUseYn: "N", useYn: "Y"}, "last");

    }

});


7) product.jsp 수정 : js 가 있는 위치를 변경해준다.

<%@ page contentType="text/html; charset=UTF-8" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="ax" tagdir="/WEB-INF/tags" %>


<ax:set key="system-common-code-version" value="1.0.0"/>

<ax:set key="title" value="${pageName}"/>

<ax:set key="page_desc" value="${pageRemark}"/>

<ax:set key="page_auto_height" value="true"/>


<ax:layout name="base">

    <jsp:attribute name="script">

        <ax:script-lang key="ax.script" var="LANG" />

        <ax:script-lang key="ax.admin" var="COL" />

        <script type="text/javascript" src="<c:url value='/assets/js/view/shopmng/product.js' />"></script>

    </jsp:attribute>

    <jsp:body>


        <ax:page-buttons></ax:page-buttons>


        <div role="page-header">

            <ax:form name="searchView0">

                <ax:tbl clazz="ax-search-tbl" minWidth="500px">

                    <ax:tr>

                        <ax:td label='ax.admin.search' width="300px">

                            <ax:input type="text" name="filter" id="filter" clazz="form-control" placeholder="ax.admin.input.search"/>

                        </ax:td>

                    </ax:tr>

                </ax:tbl>

            </ax:form>

            <div class="H10"></div>

        </div>


        <ax:split-layout name="ax1" orientation="horizontal">

            <ax:split-panel width="*" style="">


                <!-- 목록 -->

                <div class="ax-button-group" data-fit-height-aside="grid-view-01">

                    <div class="left">

                        <h2>

                            <i class="cqc-list"></i>

                            <!--<ax:lang id="ax.admin.commoncode.title"/>-->

                            제품코드 관리

                        </h2>

                    </div>

                    <div class="right">

                        <button type="button" class="btn btn-default" data-grid-view-01-btn="add"><i class="cqc-circle-with-plus"></i> <ax:lang id="ax.admin.add"/></button>

                        <button type="button" class="btn btn-default" data-grid-view-01-btn="delete"><i class="cqc-circle-with-minus"></i> <ax:lang id="ax.admin.delete"/></button>

                    </div>

                </div>

                <div data-ax5grid="grid-view-01" data-fit-height-content="grid-view-01" style="height: 300px;"></div>


            </ax:split-panel>

        </ax:split-layout>



    </jsp:body>

</ax:layout> 

8) 화면에서 확인하기( 프로그램 추가 및 메뉴생성 )

   a) 먼저 서버를 구동하여 초기 셋팅되어있는 system 계정으로 접속한다.

   


    b) 프로그램추가 : 시스템관리 > 프로그램 관리 메뉴로 들어가서 아래와 같이 프로그램을 추가한다. 

        - 경로는 앞부분에 만들었던 product.jsp의 경로를 입력해주면 되며, 권한체크/조회/저장을 체크해준다.


   c) 메뉴추가 : 시스템관리 > 메뉴관리 메뉴에서 아래와 같이 처리한다.

 


    d) 메뉴와 프로그램 연결 및 기타설정

       : 아래와 같이 생성된 "제품정보"를 클릭하여 프로그램 연결 수 다국어명칭 및 권한을 설정한다.

   


  e) 새로고침(F5)을 눌러 생성된 메뉴와 화면이 잘 연결된 것을 확인할 수 있다.



*"Axboot 신규화면 만들기 3 - 정상적인 CRUD를 위한 JAVA소스 수정" 에서 계속..............

  (JAVA소스에 약간의 양념을 넣어줘야 제대로 작동한다.)



만족하셨나요? ~~~~~~~




+ Recent posts