= 공통컴포넌트 & 유틸

== DatePicker
=== DatePicker Component 사용방법
template에 component 추가

[source, html]
----
<template>
    <sdl-datepicker
        v-model="searchPeriod"
        :period="searchPeriod">
    </sdl-datepicker>
</template>
----
props 설명

[%header,cols=3*]
|===
|props명
|설명
|예

|refKey
|component Reference Key
|

|period
|type: Object, //기간 정보
|

|minimumView
|type: String, //최소 표현 단위 - ex) month로 설정 시 '월' 단위로 picker 선택 가능

default: 'day',
|

|wrapClass
|type: String,  //class명

default: 'wd150',
|

|disabled
|type: Boolean, //달력 선택 가능여부
|default: false,
|===

== Excel Download Button
=== Excel Download Component 사용방법
template에 component 추가

[source, html]
----
<template>
    <sdl-exceldownloadbtn
        :excelInfo="excelInfo"
        :btnClass="btnClass"
        :before-click="beforeClick"
    />
</template>
----
props 설명

[%header,cols=3*]
|===
|props명
|설명
|예

|btnLabel
|type: String, //버튼 label 정의

default:'sdp.common.label.excelDownload'
|

|excelInfo
|type: Object
|excelInfo: {

columnInfoFile:'externalUser',

fileName:'externalUser', //다운받을 엑셀파일명

queryId:'selectUser', //쿼리 아이디

sheetName:'외부 사용자', //sheet명

useNumberField:'Y', //엑셀 처음 컬럼에 넘버링 사용 여부

param: { deleted: '0', activeFlag: '1', externalUser: '1' },

}

|beforeClick
|엑셀 다운로드 전에 체크하고자 하는 로직이 있을 경우 사용

다운로드 가능할 경우 true 리턴
|template :

<sdl-exceldownloadbtn :before-click="beforeClick"/>

method :

beforeClick() {

return true;

},
default: false,
|btnClass
|type: String, //버튼 class 정의

default: 'btn btn-secondary'
|
|===

== Excel Upload Button
=== Excel Upload Component 사용방법
template에 component 추가

[source, html]
----
<template>
    <sdl-exceluploadbtn
        :excelInfo="excelUpInfo"
        @after-upload="afterUpload"
        :btnClass="btnClass"
        :before-click="beforeClick"
    />
</template>
----
props 설명

[%header,cols=3*]
|===
|props명
|설명
|예

|btnLabel
|type: String, //버튼 label 정의

default:'sdp.common.label.excelUpload'
|

|excelInfo
|type: Object
|excelInfo:{

columnInfoFile:'largeExcelupload', //Column Info XML 파일

queryId:'selectUser', //실행할 query Id

returnPath:'', //엑셀 저장 후 이동할 페이지 경로('after-upload' event로 대체)

startRow:4, //저장할 데이터의 첫 Row

useObjectName:'com.samsung.sample.entity.ExcelTestObject', //데이터를 처리하기 위한 자바 객체

},

|beforeClick
|엑셀 업로드 전에 체크하고자 하는 로직이 있을 경우 사용

업로드 가능할 경우 true 리턴
|template :

<sdl-exceluploadbtn :before-click="beforeClick"/>

method :

beforeClick() {

return true;

},
|btnClass
|type: String, //버튼 class 정의

default: 'btn btn-secondary'
|
|===
event 설명

[%header,cols=3*]
|===
|event명
|설명
|예

|after-upload
|엑셀 업로드 후 진행할 로직을 추가
|template :

<sdl-exceldownloadbtn @after-upload="afterUpload"/>

method :

afterUpload(rtn){

console.log(rtn);

SDLUtil.alert('excel upload 완료. 다음 작업 할것');

},
|===

== File Component
=== File Component 사용방법
- template에 component 추가

[source, html]
----
<template>
    <sdl-fileuploader
        style="width:1532px"
        ref="uploader1"
        :fileList="fileList"
        @complete="rtnUploadList"
        downloadType="A"
        :maxTotalSize="uploadFileSize"
        :modifyFlag="true"
        useExtList="zip"
        multiFileNm="정test"
    ></sdl-fileuploader>
</template>
----
- 선택 파일 다운로드 시 파일별다운로드(default) 및 압축파일다운로드 기능 제공
- MultipleFileUploader.vue 파일 내 checkedDownload method 부분 참고

props 설명

[%header,cols=3*]
|===
|props명
|설명
|예

|modifyFlag
|type: Boolean, //true(등록/삭제 가능), false(다운로드만 가능)

default: true,
|

|uploadURL
|type: String, //업로드할 url 등록

default: '${SDLUtil.API_URL}/resource/attachments/multifile-upload' //SDL 사용시 default
|

|multiFileNm(필수)
|type: String //멀티 다운로드시 대표 이름
|

|downloadType
|type: String //file component를 한 vue에서 여러개 사용할 경우 downloadType으로 구분한다.

ex) 'A', 'B',... or 'F', 'I'
|

|fileList
|type: Array //파일 리스트를 component에 전달(SDL을 사용할 경우 API에서 넘어온 리스트 그대로 전달할것)
|fileList: [

{

firstRegDatetime:1563412831556,

firstRegrId:'sdp-front',

lastModDatetime:1563412831556,

lastModrId:'sdp-front',

fileId:'AWwCqp1KAADw5f1I',

fileName:'정sdl_html_20190708.zip',

filePathName:'C:\\NAS\\SDL\\upload',

fileSize:14154292,

fileExtensionName:'b518546c12f1482a87ce44772fd56057',

fileMimeTypeName:'application/x-zip-compressed',

deleted:false,

downloadType:'A',

orderIdx:0,

},

|useExtList
|type: String, //업로드할 파일 확장자를 세팅

default: 'zip,xlsx,xls,ppt,pptx'
|

|maxItems
|type: Number, //최대 업로드 가능한 파일 갯수 세팅

default: 30
|

|maxTotalSize
|type: Number, //최대 첨부파일 용량을 세팅

default: 1073741824
|
|===
event 설명

[%header,cols=3*]
|===
|event명
|설명
|예

|complete
|업로드 후 업로드한 파일 리스트를 return 해준다.
|template :

<sdl-fileuploader @complete="rtnUploadList"/>

method :

rtnUploadList(downloadType, fileList) {

console.log('downloadType:', downloadType);

console.log('rtnUploadList:', fileList);

SDLUtil.alert('받은 업로드 리스트 다음 작업 할것');

},

|init
|파일 컴퍼넌트를 init 처리한다.
|template :

<sdl-fileuploader ref="uploader1"/>

method :

this.$refs.uploader1.init()

|onUpload
|추가한 첨부파일을 업로드 한다. 업로드 후 complete에 지정한 메소드로 리스트 return 해줌
|template :

<sdl-fileuploader ref="uploader1"/>

method :

this.$refs.uploader1.onUpload()
|===

== Modal
Modal 팝업 띄우기

image::front_01_01.png[]
* 형식 : SDLUtil.show(import된 컴포넌트, 컴포넌트에 전달할 인자, modal properties, events)

props 설명

[%header,cols=5*]
|===
|props명
|필수여부
|Type
|Default
|설명

|name
|true
|[String, Number]
|
|모달 명

|resizable
|false
|Boolean
|false
|resize 가능여부

|draggable
|false
|[Boolean, String]
|false
|drag 가능 여부

|scrollable
|false
|Boolean
|false
|스크롤 가능 여부

|width
|false
|[String, Number]
|600
|

|height
|false
|[String, Number]
|300
|

|minWidth
|false
|Number (px)
|0
|

|minHeight
|false
|Number (px)
|0
|

|maxWidth
|false
|Number (px)
|Infinity
|

|maxHeight
|false
|Number (px)
|Infinity
|
|===
event 설명

[%header,cols=2*]
|===
|event 명
|설명

|before-open
|모달 오픈전 실행할 이벤트 정의

|opened
|모달 오픈후 실행할 이벤트 정의

|before-close
|모달 닫기전 실행할 이벤트 정의

|closed
|모달 닫기 후 실행할 이벤트 정의
|===

== Pagination
=== Pagination Component 사용방법
template에 component 추가

[source, html]
----
<template>
    <sdl-pagination
        :current-page="currentPage"
        :total-rows="records"
        :per-page="pageSize"
        @change="onSearch"
        align="center"
    />
</template>
----
props 설명

[%header,cols=3*]
|===
|props명
|설명
|비고

|currentPage
|type: Number //현재 페이지
|

|totalRows
|type: Number //총 데이터 갯수
|

|perPage
|type: Number //한 페이지에 보여질 데이터 갯수
|

|limit
|type: Number, //페이징 영역내에 보여질 페이지 숫자 갯수

default: 10
|
|===

== Tree
=== Tree Component 사용 방법
template에 component 추가

[source, html]
----
<template>
    <sdl-tree
        ref="tree"
        :data="data"
        :showCheckbox="false"
        :item-events="itemEvents"
        show-checkbox
        :multiple="false"
        allow-batch
        whole-row
        draggable
        @item-click="itemClick"
        @item-drag-start="itemDragStart"
        @item-drag-end="itemDragEnd"
        @item-drop-before="itemDropBefore"
        @item-drop="itemDrop"
    >
    </sdl-tree>
</template>
----
props 설명

[%header,cols=4*]
|===
|props명
|Type
|Default
|설명

|data
|Array
|
|set tree data

|size
|String
|
|set tree item size , value : 'large' or '' or ''small'

|show-checkbox
|Boolean
|false
|set it show checkbox

|allow-transition
|Boolean
|true
|allow use transition animation

|whole-row
|Boolean
|false
|use whole row state

|no-dots
|Boolean
|false
|show or hide dots

|collapse
|Boolean
|false
|set all tree item collapse state

|multiple
|Boolean
|false
|set multiple selected tree item

|allow-batch
|Boolean
|false
|in multiple choices. allow batch select

|text-field-name
|String
|'text'
|set tree item display field

|value-field-name
|String
|'value'
|set tree item value field

|children-field-name
|String
|'children'
|set tree item children field

|item-events
|Object
|{}
|register any event to tree item, example

|async
|Function
|
|async load callback function , if node is a leaf ,you can set 'isLeaf: true' in data

|loading-text
|String
|'Loading'
|set loading text

|draggable
|Boolean
|false
|set tree item can be dragged , selective drag and drop can set 'dragDisabled: true' and 'dropDisabled: true' , all default value is 'false'

|drag-over-background-color
|String
|'#C9FDC9'
|set drag over background color

|klass
|String
|
|set append tree class
|===
node.model 의 method

[%header,cols=2*]
|===
|method 명
|params

|addChild
|(object) newDataItem

|addAfter
|(object) newDataItem, (object) selectedNode

|addBefore
|(object) newDataItem, (object) selectedNode

|openChildren
|

|closeChildren
|
|===
event 설명

[%header,cols=2*]
|===
|event 명
|설명

|item-click
|item-click(node, item, e)

|item-toggle
|item-toggle(node, item, e)

|item-drag-start
|item-drag-start(node, item, e)

|item-drag-end
|item-drag-end(node, item, e)

|item-drop-before
|item-drop-before(node, item, draggedItem, e)

|item-drop
|item-drop(node, item, draggedItem, e)

|node
|current node vue object

|item
|current node data item object
|===
data item의 properties

[%header,cols=4*]
|===
|properties명
|type
|default
|설명

|icon
|String
|
|custom icon css class

|opened
|Boolean
|false
|set leaf opened

|selected
|Boolean
|false
|set node selected

|disabled
|Boolean
|false
|set node disabled

|isLeaf
|Boolean
|false
|if node is a leaf , set true can hide '+'

|dragDisabled
|Boolean
|false
|selective drag

|dropDisabled
|Boolean
|false
|selective drop
|===

== SDLUtil 사용 방법
=== SDL 공통 Util 사용방법

[source, javascript]
----
import SDLUtil from '@/utils/SDLUtil';
// 또는
import { SDLUtil, StringUtil } from '@/utils';

// import 후 사용
----

[%header,cols=3*]
|===
|method 명
|설명
|예

|getLoginedUserInfo
|로그인한 사용자 정보
|

|getMsgProp
|메시지 프로퍼티에서 값 가져오기
|SDLUtil.getMsgProp('sdl.user.label.work', ['하나', '둘'])

|alert
|레이어 alert
|SDLUtil.alert({

msg:'I am a tiny dialog box.<br/>And I render <b>HTML!</b>',

title:'alert',

okLabel:'ok',

onOkEvt: () => console.log('ok'),

});

SDLUtil.alert("메시지"); //이 경우 나머지 입력값들은 default로 세팅되고 msg만 입력됨
|confirm
|레이어 confirm
|SDLUtil.confirm({

msg: 'confirm body',

title: 'confirm title',

okLabel: 'ok label',

cancelLabel: 'cancel label',

onOkEvt: () => console.log('ok'),

onCancelEvt: () => console.log('cancel'),

});
|showLoadingBar
|default loading bar show/hide
|SDLUtil.showLoadingBar(true);

|getCommCodeList
|로딩시 가져온 전체 코드리스트에서 특정 parentId에 대한 코드 리스트 return
|const codelist = SDLUtil.getCommCodeList({ commCodeTypeId: 'MENUTYPE' });

console.log(codelist);
|openUserPopup
|사용자 검색 팝업
|SDLUtil.openUserPopup({

searchColumn: 'userName',

searchTxt: this.searchUserNm,

rtnFunc: this.getUserList,

knoxSearch: !this.internalFlag,

});

|isLogin
|로그인 여부
|

|getI18nLanguage
|현재 로케일 정보
|

|setI18nLanguage
|로케일 변경
|

|show
|모달창 오픈
|SDLUtil.show(

WorkgroupPopup,

{

rtnFunc: args => {

this.checkRoleWorkgroupList(args);

},

},

{ width: '850px', height: 'auto' },

);

|loadAllCommCodeList
|전체 공통코드 리스트 load
|

|formParameters
|object 를 formData로 변경
|
|===

== StringUtil 사용 방법
=== StringUtil 사용방법

[source, javascript]
----
import StringUtil from '@/utils/stringUtil';
// 또는
import { SDLUtil, StringUtil } from '@/utils';

// import 후 사용
----

[%header,cols=3*]
|===
|method 명
|설명
|예

|queryStringfy
|url 쿼리 문자열로 변경
|

|checkPatternUrl
|입력된 문자열이 url 패턴인지 체크
|
|===

== DateUtil 사용 방법
=== DateUtil 사용방법

[source, javascript]
----
import DateUtil from '@/utils/DateUtil';
// import 후 사용
----

IMPORTANT: DateUtil의 기본 데이터 포맷은 'YYYY-MM-DD' 형식이며, 국가별 언어에 따른 message.data-format.properties 는 사용자에게 보여줄 때의 형식이다.

[%header,cols=3*]
|===
|method 명
|설명
|예

|now
|현재 날짜
|

|addDate
|현재 날짜 기준 이후 데이터
|.addDate(1, 'M') 현재 날짜에서 한 달을 더한 날

|subDate
|날짜 기준 이전 데이터
|.subDate(1, 'M') 현재 날짜에서 한 달을 뺀 날

|stdFormat
|표준 포맷으로 변환
|Backend로 데이터를 보낼때의 표준 포맷(YYYY-MM-DD 순)
|===

== 공통 validation
필수 입력값 validation을 onBlur 시 그리고 저장/수정 시 하기 위해서는 아래와 같은 방법으로 처리한다.

image::front_01_02.png[]
validation 하고자 하는 tag 에 v-validation(directive) 이용(errorMessage 를 지정하지 않을 경우 default로 표시 됨, default message의 경우 다국어 지원이 되지 않음)

* 팝업 없는 화면인 경우(directive 지정시 별도 내용 없이 처리)

[source, html]
----
<div class="mb-2" style="position:relative;">
<label for="title">{{ $t('sdl.approval.label.title') }} <span class="text-required">&#42;</span></label>
<input
  id="title"
  type="text"
  class="form-control"
  v-model="apprDoc.title"
  :readonly="apprDoc.docStatus !== ''"
  v-validation=""
  :errorMessage="$t('sdl.department.message.requiredValue')"
/>
</div>
----
* 등록 화면에 팝업 등록이 또 있는 경우(groupId 를 다르게 써서 체크)

[source, html]
----
<tr>
    <th class="text-nowrap" scope="row">
        {{ $t('sdl.commonCode.label.order') }}
        <span class="text-required">*</span>
    </th>
    <td>
        <input type="number" min="0" max="99999" class="form-control" v-model.number="code.ord" v-validation="{ groupId: 'popup' }" />
    </td>
</tr>
----
* 값이 있고 없고 외에 별도 처리의 경우 함수와 에러메시지를 배열로 정의

[source, html]
----
<tr>
<th class="text-nowrap" scope="row">URL<span class="text-required">*</span></th>
<td>
<input
    id="linkSiteUrl"
    type="text"
    class="form-control"
    placeholder=""
    v-model.trim="linkInfo.url"
    v-validation="{
        groupId: '',
        valids: [
            {
                validFuncs: () => {
                    return linkInfo.url.length <= 250;
                },
                errorMessage: 'test err',
            },
            {
                validFuncs: () => {
                    return !checkPatternUrl(linkInfo.url);
                },
                errorMessage: 'sdl.samsung.validate.url',
            },
        ],
    }"
    :errorMessage="'sdl.department.message.requiredValue'"
/>
</td>
</tr>
----
* 저장/수정 버튼 클릭 시 directive로 지정한 tag들의 일괄 validation 처리(groupId가 있을 경우 넣고 없으면 안 넣어도 됨)

[source, javascript]
----
if (SDLUtil.onSubmitValidation('popup')) return;
----

IMPORTANT: Bootstrap을 이용한 퍼블리싱이 아닐 경우 안 될 수 있음

== 다국어 관련 날짜 포맷
SDL에서는 다국어 포맷을 Server로 부터 message properties를 통해 받아서 사용한다.

.message.properties
[source, json]
----
 {
  "ko_KR":{
    "data-format.time.hm":"HH:mm",
    "data-format.datetime.ymdhms":"YYYY-MM-DD HH:mm:ss",
    "data-format.time.hms":"HH:mm:ss",
    "data-format.date.ym":"YYYY-MM",
    "data-format.date.yw":"YYYY-W",
    "data-format.date.ymw":"YYYY-MM(W)",
    "data-format.date.ymd":"YYYY-MM-DD"
  },
  "en_US":{
    "data-format.time.hm":"HH:mm",
    "data-format.datetime.ymdhms":"YYYY-MM-DD HH:mm:ss",
    "data-format.time.hms":"HH:mm:ss",
    "data-format.date.ym":"YYYY-MM",
    "data-format.date.yw":"YYYY-W",
    "data-format.date.ymw":"YYYY-MM(W)",
    "data-format.date.ymd":"MM/DD/YYYY"
  }
}
----

.moment() 를 이용한 현재 날짜의 언어별 포맷(SDLUtil.getMsgProp) 표기법
[source, javascript]
----
firstRegDatetime: moment().format(SDLUtil.getMsgProp('data-format.date.ymd')),
----

.filter를 통해 template에서 사용가능
[source, html]
----
<!-- Vue2 -->
<!-- default 'YYYY-MM-DD' -->
<small class="float-right">{{ row.regDate || dateFormat }}</small>
<!-- Vue3 -->
<small class="float-right">{{ $filters.dateFormat(row.regDate) }}</small>

<!-- Vue2 -->
<!-- 포맷을 임의로 지정 -->
<td class="text-center">{{ mail.sendDateTime || dateFormat($t('data-format.date.ymd')) }}</td>
<!-- Vue3 -->
<td class="text-center">{{ $filters.dateFormat(mail.sendDateTime, $t('data-format.date.ymd')) }}</td>
----

== 화면별 권한처리
SDL에서는 역할관리에서 정의해놓은 역할별로 4개의 권한을 지정할 수 있다.
그리고 지정한 권한을 화면에서 버튼에 directive로 지정함으로써 show/hide 처리를 자동으로 해준다.

image::front_01_03.png[]

* vue 내에서 authorization directive를 통해 버튼 별 권한을 정의한다.(배열로 정의할 경우 여러개 중에 하나만 매칭되도 show처리 됨)

** 여러개의 권한을 둘 경우
+
[source, html]
----
<button
    type="button"
    class="btn btn-primary btn-sm"
    v-authorization="['UPDATE', 'EXECUTE']"
    @click="onApprWrite"
>
    {{ $t('sdl.approval.label.APPROVE') }}
</button>
----

** 하나의 권한을 둘 경우
+
[source, javascript]
----
<button type="button"
    class="btn btn-primary btn-sm"
    v-authorization="'UPDATE'"
    @click="onApprWrite"
>
    {{ $t('sdl.approval.label.APPROVE') }}
</button>
----

* 관리자일 경우는 directive 지정 상관 없이 무조건 show 처리된다.

== 목록 페이지와 상세 페이지 검색조건
=== 목록 페이지
mixins 에 StoreParams 추가

[source, javascript]
----
 // 상단
import StoreParams from '@/mixin/StoreParams';
// script
export default {
mixins: [StoreParams],
/*
** 조회하는 method에 this.setStoreParameter(params); 호출하여 파라미터 저장
*/
 methods: {
      getList(pageIndex = 1) {
          const params = {
            pageIndex,
            pageSize: this.pageSize,
            searchCondition: this.searchCondition,
            searchKeyword: this.searchKeyword,
          };
        // 해당코드 추가
        this.setStoreParameter(params);
        // Loading bar 실행
        SDLUtil.showLoadingBar(true);
        // axios api 호출 로직 생략
      }
  }
----
created() 항목에 파라미터 복원 로직 추가
[source, javascript]
----
 created() {
    const storeParams = this.getStoreParameter();
    // 저장된 파라미터가 있으면 복원
    if (storeParams) {
      this.searchCondition = storeParams.searchCondition;
      this.searchKeyword = storeParams.searchKeyword;
      this.pageIndex = storeParams.pageIndex;
      this.pageSize = storeParams.pageSize;

      this.getList(this.pageIndex);
    } else {
      // 저장된 파라미터가 없이 들어왔을때 로직
      this.getList();
    }
  },
----

=== 상세 페이지
mixins에 StoreParams 추가 후 목록버튼에 goBackToList() 호출하여 목록으로 이동
[source, javascript]
----
 // 상단
import StoreParams from '@/mixin/StoreParams';
// script
export default {
  mixins: [StoreParams],
  // ... 생략 ...
  methods: {
      // template 내에서는 goBackToList 바로 호출
      // 로직상 필요한 경우도 호출
      deleteDetail() {
          // 해당페이지 상세 삭제후 목록이동
          // 목록페이지로 돌아가기
          this.goBackToList();
      }
  }
----

== 기타 유용한 filter
=== cutString

원하는 길이만큼 문자열 컷 + '...'

[source, html]
----
<!-- vue2 -->
<td class="text-left">{{ grid.docTitle | cutString(100) }}</td>

<!-- vue3 -->
<td class="text-left">{{ $filters.cutString(grid.docTitle, 100) }}</td>
----

=== dateFormat
timestamp 형태의 값을 지정된 날짜 포맷으로 변경(default : 'YYYY-MM-DD')

[source, html]
----
<!-- vue2 -->
<!-- default 'YYYY-MM-DD' -->
<small class="float-right">{{ row.regDate | dateFormat }}</small>

<!-- vue3 -->
<small class="float-right">{{ $filters.dateFormat(row.regDate) }}</small>

<!-- 포맷 임의 지정 -->
<!-- vue2 -->
<td class="text-center">{{ mail.sendDateTime | dateFormat($t('data-format.date.ymd')) }}</td>

<!-- vue3 -->
<td class="text-center">{{ $filters.dateFormat(mail.sendDateTime, $t('data-format.date.ymd')) }}</td>
----

=== numberFormat
숫자를 금액 단위로 표시(999,999,999)

[source, html]
----
<!-- vue2 -->
 <td class="text-right">{{ log.hitCount | numberFormat }}</td>

<!-- vue3 -->
 <td class="text-right">{{ $filters.numberFormat(log.hitCount) }}</td>
----

=== nl2Br
\n 을 <br/> 로 변경(v-text 인 경우는 tag가 안 되므로 v-html 사용)

[source, html]
----
<!-- vue2 -->
 <div
  v-else
  v-html="$options.filters.nl2br(post.postDetail)"
  ref="postDetail"
  class="form-control"
  style="overflow:scroll; height:250px;max-height:250px"
  contenteditable="true"
></div>

<!-- vue3 -->
<div
  v-else
  v-html="$filters.nl2br(post.postDetail)"
  ref="postDetail"
  class="form-control"
  style="overflow:scroll; height:250px;max-height:250px"
  contenteditable="true"
></div>
----

