Skip to content

vue-devextreme.md ​

​

bash
// Generate a Vue 2 application
npx devextreme-cli new vue-app app-name

// Generate a Vue 3 application
npx devextreme-cli new vue-app app-name --version=3

cd app-name
npm run serve


devextreme add devextreme-vue

​

bash
npx -p devextreme-cli devextreme add devextreme-vue
npm install devextreme@22.2 devextreme-vue@22.2 --save --save-exact

devextreme 가이드 ​

https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/

검색을 사용해서 찾기

grid checkbox ​

https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/MultipleRecordSelectionModes/Vue/Light/https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/selection/https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/selection/#modehttps://codesandbox.io/s/rj5zzy

<dx-data-grid ref="grid" :data-source="gridData" width="100%" height="100%"
    :selection="{ mode: 'multiple', showCheckBoxesMode: 'onClick' }"
    @selection-changed="onListSelectionChanged"

    >
    <dx-column alignment="center" width="100%" data-field="postId" caption="번호" />
    <dx-column alignment="center" width="100%" data-field="regUsrId" caption="작성ID" />
    <dx-column alignment="center" width="100%" data-field="regDttm" caption="작성일" :calculate-cell-value="data => formatters.datetime(data.regDttm)" />
    <dx-column alignment="center" width="100%" data-field="updUsrId" caption="수정ID" />
    <dx-column alignment="center" width="100%" data-field="updDttm" caption="수정일" :calculate-cell-value="data => formatters.datetime(data.regDttm)" />
</dx-data-grid>

onListSelectionChanged(e) { alert('temp') if (e.selectedRowsData && e.selectedRowsData.length > 0) { // this.disableRemoveButton = false } else { // this.disableRemoveButton = true } }

devextreme grid cell change ​

https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/ColumnCustomization/Vue/Light/

grid cell edit ​

https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/BatchEditing/Vue/Light/

js
<template>
  <dx-data-grid
    ref="grid"
    :data-source="gridData" width="100%" height="600"
    :selected-row-keys="selectedItemKeys"
    :show-borders="true"
    @selection-changed="selectionChanged"
    >
    <dx-column width="80" alignment="center" caption="선택" cell-template="cellTemplate01" />
    <dx-column alignment="center" width="100%" data-field="postId" caption="번호" />
    <dx-column alignment="center" width="100%" data-field="regUsrId" caption="작성ID" >
      <DxLookup :data-source="names" value-expr="code" display-expr="name" />
    </dx-column>
    <dx-column alignment="center" width="100%" data-field="regDttm" caption="작성일" data-type="date" />
    <dx-column alignment="center" width="100%" data-field="updUsrId" caption="수정ID" />
    <dx-column alignment="center" width="100%" data-field="updDttm" caption="수정일" data-type="date" />
    <dx-selection mode="multiple" show-check-boxes-mode="always" select-all-mode="page" />
      <DxEditing
        :allow-updating="true"
        :allow-adding="true"
        :select-text-on-edit-start="true"
        start-edit-action="click"
        mode="form"
      />
  <dx-toolbar>
    <dx-item location="after" template="btnWrap" />
  </dx-toolbar>
    <template #cellTemplate01>
      <dx-check-box :value="false" />
      <!-- <dx-selection mode="multiple" show-check-boxes-mode="always" select-all-mode="page" /> -->
    </template>

              <template #btnWrap>
                <div>
                  <dx-button class="btn--input" :height="34" text="add" icon="exportxlsx" @click="add" />
                  <dx-button class="btn--input" :height="34" text="remove" icon="exportxlsx" @click="del" />
                  <dx-button class="btn--input" :height="34" text="저장" @click="save" />
                </div>
              </template>
  </dx-data-grid>
</template>

<script lang="ts">
import { Component, Ref, Prop, Emit, Watch, Model } from 'vue-property-decorator'
import { DxToolbar, DxItem, DxSelection, DxSorting, DxEditing, DxDataGrid, DxFilterRow, DxColumn, DxPaging, DxLookup } from 'devextreme-vue/data-grid'
import { DxButton as DxTextBoxButton } from 'devextreme-vue/text-box'
import { DxScrollView } from 'devextreme-vue/scroll-view'
import DxButtonGroup from 'devextreme-vue/button-group'
import DataSource from 'devextreme/data/data_source'
import CustomStore from 'devextreme/data/custom_store'
import DxSelectBox from 'devextreme-vue/select-box'


@Component({
  components: {
    DxScrollView,
    DxToolbar,
    DxItem,
    DxTextBoxButton,
    DxButtonGroup,
    DxSelection,
    DxSorting,
    DxEditing,
    DxDataGrid,
    DxColumn,
    DxPaging,
    DxLookup,
    DxSelectBox,
  },
})
export default class {
  @Ref() readonly grid: DxDataGrid
  names: any[] = [{ code: 'admin', name: 'admin' }, { code: 'adminb', name: 'adminb' }, { code: 'adminc', name: 'adminc' }]
  gridData: SampleGrid[] = []
  selectTextOnEditStart: true
  startEditAction: 'click'
  selectedItemKeys: [] = []


  selectionChanged(data) {
    this.selectedItemKeys = data.selectedRowKeys;
    console.log('selectionChanged')
  }

grid summary ​

summary type - "sum","min","max","avg","count","custom" https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/RecalculateWhileEditing/jQuery/Light/https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/summary/totalItems/#summaryType

js
$(() => {
  $('#gridContainer').dxDataGrid({
    summary: {
      recalculateWhileEditing: true,
      totalItems: [
        {
          column: 'OrderNumber',
          summaryType: 'count',
        },
        {
          column: 'SaleAmount',
          summaryType: 'avg',
          valueFormat: 'currency',
        },
      ],
    },
  })
})

button ​

js
<template>
  <dx-button type="success" class="btn--etc--ct06" icon="search" :height="34" text="상품검색" @click="showProductPopup = true" id="product-sch-btn" />
</template>
<style scoped>
#product-sch-btn {
  color: white;
  background-color: #004b29;
}

#product-sch-btn.dx-state-hover {
  background-color: #004b29;
  color: white;
}

#product-sch-btn.dx-state-active {
  background-color: #004b29;
  color: white;
}
</style>

vue-lecture.md ​

1. 강의 오리엔테이션 ​

제작할 애플리케이션 소개 ​

현대 프런트엔드 개발 절차와 역할 ​

요구사항 > 서비스 기획 > UI, UX 상세 설계 > GUI 디자인 > 퍼블리싱 > 백엔드 API 개발 > 프런트엔드 개발 > QA

수업에서 사용할 API 문서 소개 ​

2. 개발환경 구성 ​

개발 환경 소개 ​

VSCode 플러그인 및 테마 설정 ​

ctrl + shift + p > Color Theme > Night owl
ctrl + shift + p > File Icon Theme > material icon theme

night owl
material icon theme
vetur
vue vscode snippets

API 서버 프로젝트 구성 ​

https://github.com/joshua1988/vue-til-server

Node.js 버전 관리가 필요한 이유와 버전 변경하는 방법 ​

Node.js 이전 릴리즈 다운로드 페이지
https://nodejs.org/ko/download/releases/
OS 별로 아래 파일을 다운로드 받으시면 됩니다 :)
윈도우 64비트 : x64-msi 파일
윈도우 32비트 : x86-msi 파일
맥 : pkg 파일
리눅스 : tar 파일
NVM 설치 진행 명령어
https://gist.github.com/falsy/8aa42ae311a9adb50e2ca7d8702c9af1

NVM(Node Version Manager) 소개 및 설치 ​

NVM으로 Node.js 버전 변경 및 설치 ​

NVM 설치 및 버전 변경 절차 문서
-https://github.com/joshua1988/vue-til-server#nvm-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EB%B2%84%EC%A0%84-%EB%B3%80%EA%B2%BD-%EB%B0%A9%EB%B2%95

nvm use 버전 이름
nvm 노드 버전

1. 기존에 터미널에서 설치하셨던 nvm을 삭제합니다. 삭제 방법은 구글링하시면 많이 나오더라구요!
2. nvm 깃허브 레포지토리로 가셔서 사용하실 버전에 맞게 Windows 용 setup파일을 다운로드 하고 설치해줍니다.
3. VSCode를 재실행 하시고 터미널에서 nvm -v 로 버전을 확인하시면 설치하려던 버전의 nvm이 정상 설치되어 있는 것을 확인하실 수 있습니다.
4. nvm install 10.16.3 으로 설치시 너무 오래 걸려서 node.js 공식 사이트에서 이전 릴리즈 버전으로 10.16.3 버전을 다운로드 합니다.
5. node.js 공식 사이트에서 받은 10.16.3 파일을 압축 해제합니다. 하시고 폴더 명을 v10.16.3 이런식으로 변경해주세요!
6. nvm을 설치한 폴더로 갑니다.(기본 경로 : C:\Users\사용자 이름\AppData\Roaming\nvm)
이 폴더에 다운로드한 v10.16.3 이름으로 된 폴더를 복사해서 넣어주시면 됩니다.

API 서버 실행 및 확인 ​

node -v
npm i
npm run dev

http://localhost:3000/api/docs/

데이터 베이스 연결 안내 ​

MongoDB 클라우드 사이트 소개 및 가입 안내 ​

https://www.mongodb.com/ko-kr/cloud

MongoDB 클라우드 인스턴스 생성 및 설정 ​

프로젝트생성
클러스터생성

MongoDB 연결 확인 ​

mongodb+srv://1234:<password>@cluster0.2l3sv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
useUnifiedTopology: true,

API 문서 보는 법과 사용하는 방법 ​

3. 프로젝트 생성 및 환경 구성 ​

Vue CLI로 프로젝트 생성 ​

https://cli.vuejs.org/guide/installation.html

*뷰 CLI 버전 4.5.x 이상 설치시 선택
Default ([Vue 2] babel, eslint)

프로젝트 설치 옵션
Manually select features
Babel, Linter, Unit
2.x <-- Vue 2로 하시는게 중요합니다. 아직 Vue 3는 상용 서비스에 적용하기에는 무리가 있습니다.
Prettier
Lint on Save
Jest
In dedicated config files
n


npm install -g @vue/cli
vue --version
vue create vue-til

Manually select features
- Babel, Linter / Formatter, Unit Testing
- ESLint + Prettier
- Lint on save
- Jest
- In dedicated config files

Please pick a preset: Manually select features
Check the features needed for your project: Babel, Linter, Unit
Choose a version of Vue.js that you want to start the project with 2.x
Pick a linter / formatter config: Prettier
Pick additional lint features: Lint on save
Pick a unit testing solution: Jest
Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files

npm run serve

Vue 프로젝트 구조 설명 및 ESLint 에러 확인 ​

.browserslistrc
.eslintrc.js
babel.config.js

https://github.com/vuejs/vue-devtools
https://devtools.vuejs.org/

ESLint 에러가 화면에 표시되지 않게 하는 방법 ​

https://joshua1988.github.io/webpack-guide/devtools/webpack-dev-server.html

ESLint 설정 파일 안내 ​

Prettier 소개 및 ESLint와 같이 사용해야 하는 이유 ​

ESLint에 Prettier 규칙 적용 ​

https://joshua1988.github.io/web-development/vuejs/boost-productivity/

ESLint 플러그인 설치 및 설정 변경 ​

https://github.com/joshua1988/vue-til/blob/complete/settings.json

Open Settings(settings.json)에 아래 내용 추가
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "eslint.workingDirectories": [
    {"mode": "auto"}
  ],
}

Prettier 플러그인 확인 및 설정할 때 주의 사항 ​

프로젝트 레벨로 ESLint 규칙을 관리해야 하는 이유와 ESLint 규칙 설명 ​

파일의 절대 경로를 설정해야 하는 이유 ​

파일을 절대 경로로 찾기 설정 ​

https://github.com/joshua1988/vue-til/blob/complete/jsconfig.json
https://code.visualstudio.com/docs/languages/jsconfig

애플리케이션 코딩 컨벤션 및 뷰 스타일 가이드 소개 ​

https://kr.vuejs.org/v2/style-guide/index.html

4. 라우터 & 컴포넌트 설계 ​

깃헙 리포지토리 안내 및 클론 ​

https://github.com/joshua1988/vue-til

뷰 라우터 설치 및 연결 ​

22년 2월 7일부터 뷰 라우터를 NPM으로 설치할 때 버전을 꼭 명시해 주셔야 강의에서 다루는 Vue 2 버전과 호환됩니다.
npm i vue-router@3.5.3

https://joshua1988.github.io/web-development/vuejs/vue3-as-default/

페이지 컴포넌트 생성 및 연결 ​

라우팅 동작 확인 ​

코드 스플리팅 소개 및 적용 ​

https://developer.mozilla.org/en-US/docs/Web/API/Window/history

https://webpack.js.org/guides/code-splitting/

https://vuejs.org/guide/built-ins/keep-alive.html

초기 진입 페이지 설정 ​

https://github.com/vuejs/vue-router

없는 페이지를 접근할 때의 라우터 처리 ​

history mode 설정 및 싱글 페이지 애플리케이션 배포할 때 주의 사항 ​

https://router.vuejs.org/guide/essentials/history-mode.html

5. 회원 가입 페이지 개발 ​

회원 가입 페이지 개발을 위한 준비 ​

헤더 컴포넌트 마크업 및 회원 가입 컴포넌트 생성 ​

회원 가입 폼 마크업 ​

회원 가입 폼 데이터 바인딩 및 이벤트 연결 ​

회원 가입 API 요청을 위한 문서 확인 ​

API 폴더와 회원 가입 API 함수 생성 ​

회원 가입 API 호출과 주의 사항 ​

회원 가입 이후의 동작 구현 ​

6. 실무 환경을 위한 프로젝트 설정 ​

API 설정 공통화 ​

env 파일과 설정 방법 ​

Vue CLI의 env 파일 규칙과 실무 환경 구성 방법 ​

7. 로그인 페이지 개발 ​

로그인 폼 컴포넌트 생성 및 마크업 ​

로그인 폼 데이터 바인딩과 이벤트 연결 ​

로그인 API 문서 확인 및 API 함수 생성 ​

로그인 기능 구현 ​

에러가 났을 때의 대처 방법과 로그 분석하는 방법 ​

네트워크 에러 확인 방법과 에러 처리 코드 구현 ​

에러 메시지 출력 및 에러 피드백 표시 방법 ​

사용자 폼 유효성 검사 안내 ​

computed 속성을 이용한 이메일 형식 검사 ​

[퀴즈] 회원 가입 컴포넌트 유효성 검사 ​

8. 로그인 상태 관리와 스토어 ​

메인 페이지 개발을 위한 브랜치 안내 및 코드 정리 ​

로그인 동작 확인 및 구현 방향 복습 ​

메인 페이지 라우팅 구현 및 확인 ​

자바스크립트로 페이지 이동하기 구현 및 설명 ​

로그인 이후의 동작 설명 ​

컴포넌트 간 데이터 전달 방법 3가지와 구현 방향 ​

뷰엑스 설치 및 연결 ​

로그인 아이디 헤더에 표시하기 ​

로그인 했을 때의 헤더 버튼 분기 처리 ​

로그 아웃 기능 구현 ​

9. 토큰을 이용한 API 인증 처리 ​

브랜치 전환 및 변경 사항 안내 ​

학습 노트 조회 API를 호출하는 방법과 확인 사항 ​

로그인 토큰 값 확인 ​

HTTP 헤더에 토큰 값을 실는 방법 ​

스토어를 이용한 토큰 저장 및 활용 ​

저장된 토큰 값을 이용한 API 요청 및 의도치 않은 동작 확인 ​

문제가 되는 동작 분석 및 해결 방향 안내 ​

액시오스 인터셉터 소개 ​

액시오스 인터셉터 모듈화 및 인스턴스에 연결하는 방법 ​

인터셉터를 이용한 HTTP 헤더 설정 ​

10. 학습 노트 데이터 조회 ​

학습 노트 조회 API 함수 구현 ​

학습 노트 목록 표시를 위한 데이터 바인딩 ​

학습 노트 생성 및 생성된 데이터 확인 ​

학습 노트 목록 마크업 및 스타일링 ​

학습 노트 목록 아이템 컴포넌트화 ​

데이터 로딩 상태 표시 ​

로딩 상태를 표시하는 스피너 등록 ​

11. 브라우저 저장소를 이용한 인증 값 관리 ​

브랜치 전환 안내 ​

화면 새로 고침 했을 때의 문제점 분석 ​

로그인 인증 값 브라우저 저장소에 저장 후 확인 ​

브라우저 저장소로 인증 값 보존 ​

actions 속성을 이용한 로그인 기능 구현과 비동기 처리시 유의할 점 ​

학습 노트 데이터 생성 ​

학습 노트 등록 페이지 라우터 생성 및 확인 ​

학습 노트 등록 폼 마크업 및 스타일링 ​

학습 노트 등록 폼의 데이터 바인딩 및 이벤트 연결 ​

학습 노트 등록 API 구현 및 동작 확인 ​

학습 노트 등록 에러 처리 ​

학습 노트 본문 길이 유효성 검사 기능 구현 ​

12. 중간 정리 ​

중간 리뷰 ​

이후 강의 내용 요약 ​

13. API 함수 모듈화 ​

API 모듈화를 위한 현재 구조 분석 ​

인스턴스 생성 함수 분할 ​

[실습 안내] 계정 관련 API 분할 및 실습 안내 ​

[실습 답안] API 함수 모듈화 정리 ​

14. 학습 노트 데이터 삭제 ​

삭제 기능 마크업 및 이벤트 연결 ​

삭제 API 함수 및 기능 구현 ​

삭제 기능 UX 개선 ​

학습 노트 생성 기능 관련 UX 수정 ​

15. 학습 노트 데이터 수정 ​

학습 노트 수정 아이콘 이벤트 연결 및 구현 방향 안내 ​

학습 노트 수정 페이지 생성 및 라우터 연결 ​

학습 노트 수정 페이지 마크업 및 라우터 파라미터 연결 ​

학습 노트 수정을 위한 특정 게시물 조회 기능 구현 ​

학습 노트 수정 API 및 기능 구현 ​

16. 데이터 포맷팅 ​

뷰 필터 소개 및 적용 ​

전역 필터 소개 및 설정 ​

17. 라우터 심화 ​

라우터 네비게이션 가드 소개 ​

라우터 네비게이션 가드 기초 코드 ​

페이지별 인증 권한 설정 ​

인증되지 않은 사용자 접근 제어 ​

로그인 상태에 따른 로고 이동 링크 처리 ​

로그 아웃 관련 코드 수정 ​

18. 프런트엔드 테스팅 소개 ​

테스팅 환경 구성 ​

테스트 코드가 필요한 이유 ​

Jest 소개 ​

자바스크립트 테스트 파일 소개 및 파일 경로 관련 안내 ​

자바스크립트 테스트 코드 시작하기 ​

테스트 코드 작성 팁 ​

뷰 컴포넌트 테스트 방법 ​

뷰 테스트 유틸 라이브러리 소개 및 적용 ​

find()를 이용한 컴포넌트 HTML 요소 검색 ​

로그인 폼의 인풋 박스 관련 테스트 코드 작성 ​

이메일 유효성 검사 기능 동작 테스트 코드로 확인 ​

로그인 컴포넌트 첫 번째 테스트 코드 작성 ​

로그인 컴포넌트 두 번째 테스트 코드 작성 ​

[실습 안내] 회원가입 컴포넌트 테스트 코드 작성 ​

19. 강의 마무리 ​

학습 내용 정리 및 향후 학습 로드맵 안내 ​

vue-typescirpt.md ​

Vue TypeScript 설정 ​

bash
npm install -g @vue/cli
yarn global add @vue/cli

vue --version

vue create vue-started
  • TS, Router, Vuex, Linter
  • class-style > Yes
  • Bable alongside TypeScript for auto-detected polyfills > No
  • history mode for router > Yes

vue.md ​

요구사항 > 서비스 기획 > UI, UX 상세 설계 > GUI 디자인 > 퍼블리싱 > 백엔드 API 개발 > 프런트엔드 개발 > QA

night owl material icon theme vetur vue vscode snippets

https://github.com/joshua1988/vue-til-server

bash로 변경 사용자에 폴더에서 .bash_profile 파일추가

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash

nvm -v nvm use system vi ~/.bash_profile

export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm

source ~/.bash_profile

nvm install 12.14.0 nvm install 10.16.3

vue click async ​

https://stackoverflow.com/questions/64883186/basic-async-call-on-click-event-in-template-vuejs

vue computed ​

https://velog.io/@jinsu6688/vuejs-computed

vue dx-grid row ​

https://stackoverflow.com/questions/5497073/how-to-differentiate-single-click-event-and-double-click-event

vue smart editor2 image upload ​

https://javafactory.tistory.com/1259http://naver.github.io/smarteditor2/user_guide/2_install/download.htmlhttps://zero-gravity.tistory.com/171https://github.com/naver/smarteditor2/releases/tag/v2.8.2.3http://webprogramer.kr/blog/P000000364/post.dohttp://www.todayhumor.co.kr/board/view.php?table=programmer&no=11347https://holybell87.tistory.com/12

js
* 사용버전 : 2.8.2.12056
1. /SE2.8.2.O12056/sample/js/plugin/hp_SE2M_AttachQuickPhoto.js
var sPopupUrl = "이미지 업로드 팝업창 표시URL"; 로 수정

2. 이미지 업로드 팝업창을 통해, 업로드 한 후
   Smart Editor에 이미지 업로드 결과를 반영하기 위해 만든 function을 호출하는 부분에서
window.opener.parent.function이름(); 요론식으로 호출해야 한다.
 

Smart Editor가 iFrame으로 생성되니...

이미지 업로드 기능을 만들어 놓지 않았다면
요기로 가면, JSP로 만들어 놓은 분이 계시다.

http://zero-gravity.tistory.com/171

https://lilymate.tistory.com/476

vue grid edit ​

https://js.devexpress.com/Documentation/Guide/UI_Components/DataGrid/Editing/#User_Interaction/Row_Modehttps://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/RowEditingAndEditingEvents/Vue/Light/

vue grid addrow ​

https://js.devexpress.com/Demos/WidgetsGallery/Demo/FloatingActionButton/Overview/Vue/Light/

js
<dx-button class="btn--input" width="68" height="34" text="추가" @click="addNewRow" />
<dx-button class="btn--input" width="68" height="34" text="완료" @click="saveGridInstance" />

  saveGridInstance() {
    console.log('aaaa')
    // debugger
    // this.grid.instance.beginUpdate()
    // this.grid.instance.closeEditCell()
    this.grid.instance.saveEditData()
    // this.grid.instance.refresh()
    console.log('bbb')
  }

devextreme customstore remove ​

https://js.devexpress.com/Documentation/ApiReference/Data_Layer/CustomStore/

devextreme popup ​

js
    <h3 class="hy-h3 required">팝업</h3>
    <div class="row-box mgt-20">
      <dx-button class="btn--input" height="34" text="팝업오픈" @click="popupOpen()" />
    </div>
    <!-- 팝업시작 -->
    <dx-popup :visible.sync="show" width="600" title="팝업가이드">
      <div class="sub-head">
        서브 헤더 영역
      </div>
      <!-- 스크롤이 필요 없을 경우 삭제 -->
      <DxScrollView class="scrollview" ref="scrollViewWidget">
        <!-- popup -->
        <div class="hy-popup">
          팝업 콘텐츠 영역...
    
          <div class="btn-wrap center">
            <!-- 좌우 정렬 span.left 사용 -->
            <!-- <span class="left">
                              <dx-button class="btn--base" width="120" height="40" text="취소" />
                            </span> -->
            <dx-button class="btn--base" width="120" height="40" text="취소" @click="popupClose" />
            <dx-button class="btn--solid" width="120" height="40" text="승인요청" />
          </div>
        </div>
      </DxScrollView>
    </dx-popup>
    <!-- 팝업 종료 -->
  show: boolean = false

  popupOpen() {
    console.log('open')
    this.show = true
  }

  popupClose() {
    console.log('close')
    this.show = false
  }

devextreme textbox showclearbutton ​

js
<dx-text-box v-model="searchCondition.id" :show-clear-button="true" />

https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxTextBox/Configuration/#showClearButton

vue style 적용 ​

js
<div v-show="!!text4" :style="{ display: !!text4? 'inline-block' :'none' }"> </div>

devextreme excel upload ​

js
  <u-file-upload-box
    v-model="result2"
    file-grp-id="file4"
    select-button-text="엑셀파일 업로드"
    upload-type="convert"
    converted-file-type="excel"
    model-class-name="com.test.FileExcelSampleModel"
    :stop-on-error="true"
    :saveable="false"
    :editable="true"
  />

devextreme image upload ​

js

https://codesandbox.io/s/jgpcd2?file=/App.vue
https://codesandbox.io/s/jgpcd2?file=/App.vue:334-345

vue import alias ​

js
import { DxToolbar, DxItem, DxSelection, DxEditing, DxDataGrid } from 'devextreme-vue/data-grid'
import { DxToolbar as DxToolbar2, DxItem as DxItem2, DxSelection as DxSelection2, DxEditing as DxEditing2, DxDataGrid as DxDataGrid2 } from 'devextreme-vue/data-grid'

vue error ​

https://nodejs.org/download/release/v16.17.1/


internal/modules/cjs/loader.js:818
  throw err;
  ^
Error: Cannot find module 'node:fs'


node_modules/rc9/dist/index.cjs
rc9/dist/index.cjs

https://cocoon1787.tistory.com/851
https://cocoon1787.tistory.com/851
https://medium.com/@su_bak/cannot-find-module-fs-promises-%E1%84%8B%E1%85%A6%E1%84%85%E1%85%A5-%E1%84%92%E1%85%A2%E1%84%80%E1%85%A7%E1%86%AF-%E1%84%87%E1%85%A1%E1%86%BC%E1%84%87%E1%85%A5%E1%86%B8-a344921bd430

vue prop ​

prop설정후에는 f5 진행후 하기

// @Prop({ default: '' }) fileId: string @Prop() fileId: string = '' (x) Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "fileId"

https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/columns/#cellTemplate

vue grid focused-row-changing ​

js
      <dx-data-grid ref="grid" :data-source="gridData" width="100%" height="100%" @focused-row-changing="onFocusedRowChanging" @focused-row-enabled="true" key-expr="regDttm">
        <dx-column alignment="center" width="100%" data-field="rownum" caption="번호" />
        <dx-column alignment="center" width="100%" data-field="regUsrId" caption="작성ID" />
        <dx-column alignment="center" width="100%" data-field="regDttm" caption="작성일" :calculate-cell-value="data => formatters.datetime(data.regDttm)" />
        <dx-column alignment="center" width="100%" data-field="updUsrId" caption="수정ID" />
        <dx-column alignment="center" width="100%" data-field="updDttm" caption="수정일" :calculate-cell-value="data => formatters.datetime(data.regDttm)" />
      </dx-data-grid>

// onSelectionChanged({ selectedRowsData }) {
  //   debugger
  //   const data = selectedRowsData[0]
  //   alert('ss')
  // }

  clickTimer: any
  lastRowCLickedId: any
  onFocusedRowChanging(e) {
    //OBTAIN YOUR GRID DATA HERE
    console.log('test')
    if (this.clickTimer && this.lastRowCLickedId === e.rowIndex) {
      console.log('aaaa')
      clearTimeout(this.clickTimer)
      this.clickTimer = null
      this.lastRowCLickedId = e.rowIndex

      const key = e.event && e.event.key
      if (e.event.detail === 2) {
        // alert('test')
      }
      //YOUR DOUBLE CLICK EVENT HERE
      // if (typeof dblClickFunc == 'function') {
      // }
    } else {
      this.clickTimer = setTimeout(function () {}, 250)
    }
    this.lastRowCLickedId = e.rowIndex
  }

  // const pageSize = e.component.pageSize()
  // const pageIndex = e.component.pageIndex()
  // const isLoading = e.component.getController('data').isLoading()
  // const key = e.event && e.event.key
  // if (!isLoading) {
  //   if (key && e.prevRowIndex === e.newRowIndex) {
  //     if (e.newRowIndex === pageSize - 1) {
  //       e.component.pageIndex(pageIndex + 1).done(() => {
  //         e.component.option('focusedRowIndex', 0)
  //       })
  //     } else if (e.newRowIndex === 0) {
  //       e.component.pageIndex(pageIndex - 1).done(() => {
  //         e.component.option('focusedRowIndex', pageSize - 1)
  //       })
  //     }
  //   }
  // }
  // }

​

방법1 node 재설치 ​

  1. node 12버전 삭제
  2. 재설치 https://nodejs.org/download/release/v16.17.1/

방법2 nvm 사용시 ​

  1. nvm install 16.17.1
  2. nvm use 16.17.1
  3. nvm alias default 16.17.1

vue grid ​

js
    this.gridDataSource = new DataSource({
      store: new CustomStore({
        load: async loadOptions => {
          const paginationRequest = createPagingParameter(loadOptions)
          const result = await orderConsignmentListService.getOrderConsignmentList(paginationRequest.header, this.params)
          this.gridData = result.body
          this.totalCount = result.header.totalRecords
          return {
            data: this.gridData,
            totalCount: result.header.totalRecords,
          }
        },
      }),
    })


    this.gridDataSource = new DataSource({
      store: this.gridData
    });

databindiing ​

https://js.devexpress.com/Documentation/Guide/UI_Components/PivotGrid/Data_Binding/

CustomStore ​

https://supportcenter.devexpress.com/ticket/details/t428982/customstore-how-to-process-load-options-provided-by-dxdatagrid

vue grid paging ​

https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/RecordPaging/Vue/Light/

js
<dx-scrolling 
  mode="standard"
  column-rendering-mode="virtual"
  :use-native="false"
  />
<dx-paging
  :enabled="true"
  :page-size="10"
  :page-index="0" />
<dx-pager 
  :show-page-size-selector="true"
  :show-info="true"
  info-text="현재 페이지 {0}. 전체항목: {2}개" />
js
<DxScrolling row-rendering-mode="virtual"/>
<DxPaging :page-size="10"/>
<DxPager
  :visible="true"
  :allowed-page-sizes="pageSizes"
  :display-mode="displayMode"
  :show-page-size-selector="showPageSizeSelector"
  :show-info="showInfo"
  :show-navigation-buttons="showNavButtons"
/>
js
  displayMode:string =  'full'
  pageSizes: any[] = [5, 10, 'all']
  showPageSizeSelector: boolean =true
  showInfo: boolean=true
  showNavButtons: boolean=  true

vue style ​

style="visibility: hidden" style="display: none"

vue dx-list ​

js
<div class="col">
  <strong>대분류</strong>
  <dx-list
    v-model="fstCtgValue"
    :data-source="fstCtgIds" 
    display-expr="codeNm"
    :height="175"
    selection-mode="single"
    page-load-mode="scrollBottom"
    :search-enabled="true"
    search-mode="contains"
    search-expr="codeNm"
    @selection-changed=selectionChangedFirst($event)
    />
</div>
<div class="col">
  <strong>중분류</strong>
  <dx-list 
    v-model="scdCtgValue"
    :data-source="scdCtgIds"
    :height="175"
    selection-mode="single"
    page-load-mode="scrollBottom"
    :search-enabled="true"
    search-mode="contains"
    search-expr="codeNm"
    @selection-changed=selectionChangedSecond($event)
    >
    <template #item="{ data: data, index: index }">
      {{ data.codeNm}}
    </template>  
  </dx-list>

</div>


<dx-list 
  v-model="scdCtgValue"
  :data-source="scdCtgIds"
  :height="175"
  selection-mode="single"
  @selection-changed=selectionChangedSecond($event)
>
  <template #item="{ data: row, index: index }">
    <!-- <ul class="temp" style='display: none'> -->
    <ul class="temp" v-if='index== 2'>
      <li>
          {{ index }}
          {{ row.code}}
      </li> 
    </ul>
  </template>  
</dx-list>

vue dom 사용 ​

https://bono915.tistory.com/entry/VueJS-refs으로-Dom에-접근하는-방법

vue for ​

js
    <template v-for="(item, index) in fileInfo" >
      <template>
        <dd class="hy-attch" v-bind:key="item.fileName" >
            <a href= "javascript:;" @click="()=>{download(item.fileGrpId, item.fileId, item.fileNo, '', 1)}">{{ item.orgName }}</a>
        </dd>
      </template>
    </template>

File chooser dialog can only be shown with a user activation. ​

js

vue emit and model ​

js
// parent -emit 방식
<editor-product-regist @value="changeDatass"/>
  changeDatass(data: string) {
    console.log('my!!!', data)
  }

// parent - 모델방식
<editor-product-regist v-model="changeData"/>

  changeData: string = ''

  @Watch('changeData')
  changeData() {
    console.log(this.changeData)
  }

// child

  @Model('value', { default: null }) readonly value

  @Watch('content', { immediate: true })
  changeContent() {
    this.$emit('value', this.content)
  }

vue paging ​

js
<dx-scrolling 
  mode="standard"
  column-rendering-mode="virtual"
  :use-native="false"
  />
<dx-paging
  :enabled="true"
  :page-size="10"
  :page-index="0" />
<dx-pager 
  :visible="true"
  :show-page-size-selector="true"
  :show-info="true"
  info-text="현재 페이지 {0}. 전체항목: {2}개" 
  />

  @Ref() readonly grid: DxDataGrid
  gridData: OrderConsignmentList[] = []
  loadParam: any

  async loadData() {
    this.loadParam = {
      "currentPage": 1,
      "pageSize": 10,
      "orders": []
    }
    const paginationRequest = createPagingParameter(this.loadParam)
    const result = await testService.getSelect(paginationRequest.header, this.params)
    this.gridData = result.body
    this.totalCount = result.header.totalRecords
  }

  async search(params) {
    this.params = params
    this.loadData()
  }

vue promise ​

js
.catch(error => alert(error.message));

.then(response => response.json())

vue devextreme grid insert ​

js
this.gridDataSource = this.grid.instance.getDataSource()
this.gridDataSource.store().push([{ type: 'insert', data: newDeliveryFee }])

vue devextreme grid checkbox ​

js
:selection="{ mode: 'multiple', showCheckBoxesMode: 'onClick' }"
      @selection-changed="onListSelectionChanged"

onListSelectionChanged(e) {
    alert('temp')
    if (e.selectedRowsData && e.selectedRowsData.length > 0) {
      // this.disableRemoveButton = false
    } else {
      // this.disableRemoveButton = true
    }
  }

vue devextreme theme ​

js
https://js.devexpress.com/Documentation/Guide/Themes_and_Styles/Icons/
https://js.devexpress.com/Demos/WidgetsGallery/Demo/Button/Icons/Vue/Light/

vue se2 ​

js
https://junesker.tistory.com/16
https://m.blog.naver.com/hiizero/221639344451
http://naver.github.io/smarteditor2/user_guide/4_photouploader/intro.html
http://naver.github.io/smarteditor2/user_guide/

vue devextreme data-period-box ​

js
<dx-date-period-box v-model="dateInput5" :width="150" mode="datetime" />

vue window postmessgae ​

http://guide.ustraframework.kro.kr/ref-doc/03/5wtYKOTfWycqrQuucpyn

vue grid option ​

js
<dx-data-grid
  :data-source="gridDataSource"
  :show-borders="true"
  :allowColumnReordering="true"       // <===== 그리드 컬럼 위치 변경 가능 여부
  :allowColumnResizing="true"
  :row-alternation-enabled="false"
  :show-row-lines="true"
  :column-min-width="80"
>
:allowColumnReordering="true"       // <===== 그리드 컬럼 위치 변경 가능 여부
:allowColumnResizing="true"         // <===== 그리그 컬럼 사이즈 변경 가능 여부
<dx-selection mode="single" /> // <==== multiple 체크박스, single 선택한 라인 보이기, none 아무것도 없음
<pager.infoText>
{0} - 현재 선택한 페이지 (shows the current page number.)
{1} - 전체 페이지 (shows the total page count.)
{2} - 전체 행 개수 (shows the total row count.)

vue devextreme grid template ​

js
<dx-data-grid ref="grid" :data-source="gridData" width="100%" height="100%">
   <dx-column alignment="center" width="100%" data-field="postId" caption="번호" />
   <dx-column :width="100" :allow-sorting="false" data-field="Picture" cell-template="cellTemplate"/>
   <template #cellTemplate>
      <img src="http://localhost/image.png">
   </template>
</dx-data-grid>
https://codesandbox.io/s/ixjl38?file=/App.vue
https://supportcenter.devexpress.com/ticket/details/t657515/how-to-show-an-image-in-datagrid-column
https://js.devexpress.com/Documentation/Guide/UI_Components/DataGrid/Columns/Customize_Cells/#Customize_the_Appearance
https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/ColumnTemplate/Knockout/Light/

vue addEvent ​

https://stackoverflow.com/questions/55067565/vue-js-add-event-listener-inside-instance

vue devextreme exprot xlsx, pdf ​

https://shiftkey.tistory.com/28
https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/export/
https://js.devexpress.com/Demos/WidgetsGallery/Demo/DataGrid/ExcelJSOverview/Vue/Light/

vue devextreme grid cell change ​

js
  <dx-data-grid id="sampleGrid" ref="grid" :data-source="gridData" width="100%" height="600"
    :selected-row-keys="selectedItemKeys" :show-borders="true" @editorPreparing="onEditorPreparing"
    @editingStart="onEditingStart($event)">

  onEditingStart(e) {
    // e.column.allowEditing = true
    // console.dir(e.column.name)
    // // debugger
    // console.dir(e.key.postId == 43)
    // if (e.column.name == "isChecked") {
    //   if (e.key.isChecked == true) {
    //     e.cancel = true;

    //     console.log('testa')
    //   }
    // }

    // }
    // console.dir()
    // if (info.key == 2) {
    // info.cancel = true;
    // }
  }

  onEditorPreparing(e) {
    // console.dir(e.row.isSelected)
    // console.log('onEditorPreparing')
    if (e.parentType === 'dataRow' && e.dataField === 'isChecked' && e.row.isSelected == true) {
      console.log('test')

      e.row.isSelected = true
      // e.editorOptions.disabled = true
    }
  }

vue model add ​

js
import { ProofFile } from '@/models/data-model'

getUploadList() {
  var fileIdList = [{ etpsCd: 'a100', fileId: 'aaaaa', fileTpCd: 'F100' }, { etpsCd: 'b100', fileId: 'bbbbb', fileTpCd: 'F200' }]
  var updateList = []
  fileIdList.map(row => {
    let proofFile: ProofFile = {
      fileId: row.fileId,
      etpsCd: row.etpsCd,
      fileTpCd: row.fileTpCd,
      fileNo: 1,
    }
    updateList.push(proofFile)
  })
}

vue refs 사용 ​

js
<file-upload-product-regist fileGrpId="default-file-group" ref = "refFileUpload" />
  getFileList() {
    const val = (this.$refs['refFileUpload'] as FileUpload).getUploadList()
    val.map(test => { console.dir(test) })
  }

vue typeof 와 instanceof의 차이, 타입 또는 클래스 구분하기 ​

https://unikys.tistory.com/260

vue arr인지 확인 ​

Array.isArray()

https://hianna.tistory.com/402

vue tap index ​

js
<dx-tab-panel :selected-index.sync="tabIndex2" :data-source="tab2" height="100%" :width:="500"
  @selection-changed="tab2IndexChanged">
  <template #title="{ data }">
    <span>{{ data.name }}</span>
  </template>
  <template #tab1>
    <!-- <sub-tab ref="grid1" /> -->
  </template>
  <template #tab2>
    <!-- <sub-tab ref="grid2" /> -->
  </template>
  <template #tab3>
    <!-- <sub-tab ref="grid3" /> -->
  </template>
</dx-tab-panel>

  tabIndex2 = 0

  tab2 = [
    { name: '결제건수', template: 'tab1', disabled: false },
    { name: '결제자수', template: 'tab2', disabled: false },
    { name: '결제금액', template: 'tab3', disabled: false },
  ]

  tab2IndexChanged() {
    if (this.tabIndex2 === 0) {
      // @ts-ignore
      // this.$refs.grid1.loadData()
    } else if (this.tabIndex2 === 1) {
      // @ts-ignore
      // this.$refs.grid2.loadData()
    } else if (this.tabIndex2 === 2) {
      // @ts-ignore
      // this.$refs.grid3.loadData()
    }
  }

vue config, querystring ​

js
@Component({
  config: {
    auth: {
      required: false
    }
  },
})

  created() {
    console.log('created')
    console.dir(this.$route.query)
  }

vue notion ​

js
https://www.npmjs.com/package/vue-notion?activeTab=readme

vue querystring ​

js
  funcQueryString(data: any) {
    var keys = []
    for (var number in data) {
      if (data.hasOwnProperty(number)) keys.push(number)
    }
    var querysting = ''
    keys.map((row, index) => {
      if (index == 0) querysting += `?${row}=${data[row]}`
      else querysting += `&${row}=${data[row]}`
    })
    return querysting
  }

vue devextreme floating action button ​

js

    <DxSpeedDialAction :index="1" icon="" label="전체 탭 닫기" :on-click="() => success= true" />


import config from 'devextreme/core/config';
import FloatingActionButtonDirection from 'devextreme/core/config';
import repaintFloatingActionButton from 'devextreme/ui/speed_dial_action/repaint_floating_action_button';~~~~
import DxSpeedDialAction from 'devextreme-vue/speed-dial-action';

    config({
      floatingActionButtonConfig: {
        icon: 'rowfield',
        shading: true,
        direction: 'down',
        position: {
          of: '.dx-datagrid-rowsview',
          my: 'right top',
          at: 'right top',
          offset: '-20 90',
        },
      },
    });

    repaintFloatingActionButton();

https://js.devexpress.com/Demos/WidgetsGallery/Demo/FloatingActionButton/Overview/Vue/Light/

vue devextreme icon ​

js
<dx-button icon="deletetable" v-if="menu.text == 'Home'" @click="clickCloseAllTab()" />

https://js.devexpress.com/Documentation/Guide/Themes_and_Styles/Icons/

vue devextreme validator ​

js
https://js.devexpress.com/Documentation/ApiReference/UI_Components/dxValidator/Validation_Rules/PatternRule/
https://js.devexpress.com/Demos/WidgetsGallery/Demo/Validation/Overview/Vue/Light/

vue devextreme textbox mode 오류 ​

js
// dx-text-box가 password를 누른 상태에서 다른 텍스트 박스를 누를경우 모드가 기존에 클릭한 mode로 되는 현상
<dx-text-box ref="refTextBox" class="side-search-input" v-model="searchText" mode="search"
        placeholder="입력" value-change-event="input" :readOnly="!isFocused" @focusIn="focusIn()"
        @focusOut="focusOut()" />

@Ref() refTextBox: DxTextBox
  focusIn() {
    this.isFocused = true
    this.refTextBox.mode = "text"
  }
  focusOut() {
    this.isFocused = false
    this.refTextBox.mode = "text"
  }

vue file upload download ​

js
https://stackoverflow.com/questions/41803925/download-octet-stream-via-jquery
https://kyounghwan01.github.io/blog/JS/JSbasic/Blob-url/
https://stackoverflow.com/questions/37510801/convert-octet-stream-to-image

nuxt gitlab [error] Couldn't find any versions for ​

js
원인: gitlab에서 nexus에 패키지의 특정 버전이 삭제되면서 발생한 오류

점검1: gitlab에서 빌드를 진행하는 cd /app/gitlab-runner/builds/aaa/0/aaa
=> .npmrc 확인, yarn install 가능한지 확인, yarn관련 파일 삭제후 진행 등

점검2: gitlab-ci.yml
=> 스크립트 확인

점검3: nexus 이동후 점검 Browser
=> 해당 패키지또는 라이브러리 확인

git-runner 
.npmrc


Run yarn again.
Run yarn --force.
If it still does not help, you can wipe yarn.lock and run yarn again.

vue security ​

import VueSanitize from "vue-sanitize";
Vue.use(VueSanitize);

https://dev.grapecity.co.kr/bbs/board.php?bo_table=Insight&wr_id=118

vue cell color change ​

js

vm.statusData = [];
vm.statusData = [
    {
        "iStatus": 1,
        "sName": "No Work",
        "sDesc": "No Work Done",
        "dPercentComplete": 0,
        "sColor": "Red"
    },
    {
        "iStatus": 2,
        "sName": "WIP",
        "sDesc": "Work In Progress",
        "dPercentComplete": 50,
        "sColor": "Yellow"
    },
    {
        "iStatus": 3,
        "sName": "Complete",
        "sDesc": "Work Complete",
        "dPercentComplete": 100,
        "sColor": "Green"
    }
];

onCellPrepared: function(e) {
  if (e.rowType != "data" || e.columnIndex != 1)
    return;
  
  // debugger;  
  var color = e.column.lookup.items[e.value - 1].sColor  
  e.cellElement.css('backgroundColor', color);

},

https://supportcenter.devexpress.com/ticket/details/t571121/dxdatagrid-how-to-set-a-custom-cell-color-based-on-a-lookup-value
https://plnkr.co/edit/Cr8UZvI1hxRY60CBDxX3?p=preview&preview

volar ​

"volar.inlayHints.eventArgumentInInlineHandlers": true

security ​

js
"><script>alert("XSS")</script>
"><script>alert("XSS")</script>

" onclick=alert(1)//<button ' onclick=alert(1)//> 
" onclick=alert(1)//<button ' onclick=alert(1)//> */ alert(1)//


"><marquee/onstart=confirm(String.fromCharCode(88,
"><marquee/onstart=confirm(String.fromCharCode(88,83,83))>//


<html>
  <body onload="document.forms[0].submit()">
    <form action="https://testsite.com/logout">
    </form>
  </body>
</html>

중복체크 ​

js
// 확인할 값
const checkedList = this.refProductDxDataGrid.instance.getSelectedRowsData()

this.gridData.map(row => { if (row.testId.lastIndexOf(checkedList[0].testId) > -1) flag = true })

this.gridData.some(row => row.testId === checkedList[0].testId)

isEmpty ​

js
isEmpty(val) {
  var result = false
  if (val != null && val != 'undefined' && val != '') {
    result = true
  }
  return result
}

references ​

https://junior-datalist.tistory.com/236