Skip to content

setup

setupDynamicComponent.ts

메뉴 클릭시 로딩될 컴포넌트를 등록해준다.

Smart-erp-lite에서 메뉴 선택시 열린 프로그램은 상단에 탭으로 추가되는데, vue의 <component>를 이용해서 메뉴의 programUrl에 해당하는 컴포넌트가 동적 로딩되도록 한다.

  • 프로그램 작성순서
  1. 프로그램관리 테이블 SYS_PROGRAMPROGRAM_URL 컬럼에 각 프로그램의 메인이 되는 대표 컴포넌트의 경로를 등록한다.
PROGRAM_NMPROGRAM_URL
경비승인관리/views/fim/expense-approval-management/ExpenseApprovalManagement.vue

program_url은 /view/ 경로로 시작하고, 최종 경로의 폴더명은 컴포넌트 이름을 snake-case로 하고, 파일명은 컴포넌트이름과 vue 확장명으로 작성한다.

  • 컴포넌트 등록 스크립트
import { App } from "vue"
import _ from 'lodash'
import { usePopupSettingMap } from '@/core'

export default function setupDynamicComponent(app: App) {
  // https://vitejs-kr.github.io/guide/features.html#glob-import
  let componentsCore = import.meta.globEager('../core/components/**/*.vue')
  let componentsViews = import.meta.globEager('../views/**/*.vue')
  let componentsSample = import.meta.globEager('../views-sample/**/*.vue')

  let components = {
    ...componentsCore,
    ...componentsViews
  }
  if (import.meta.env.DEV) {
    components = {
      ...components,
      ...componentsSample
    }
  }

  const popupSettingMap = new Map<string, PopupSetting>()
  usePopupSettingMap(popupSettingMap)
  Object.entries(components).forEach(([path, definition]) => {
    // Get name of component, based on filename
    // "./components/Fruits.vue" will become "Fruits"

    if (path.includes('xxx')) {
      return
    }
    if (path.includes('/directive/')) {
      console.warn(path)
      return
    }

    const splitPath = path?.split('/')
    const componentName = splitPath.pop()?.replace(/\.\w+$/, '')!
    const parentPath = _.upperFirst(_.camelCase(splitPath.pop()))

    // console.log(path, componentName)
    // 샘플은 '프로그램명폴더/프로그램명컴포넌트' 규칙 제외
    if (splitPath.includes('views-sample') || splitPath.includes('core')) {
      app.component(componentName, definition.default)
    } else {
      // Register component on this Vue instance
      // console.log(componentName , parentPath);
      // 프로그램경로명과 컴포넌트이름이 같은 규칙이면 등록
      // 팝업도 등록
      if (componentName === parentPath || componentName.endsWith('Popup')) {
        app.component(componentName, definition.default)
      }
    }
    if (!componentName.startsWith('Vm') && componentName.endsWith('Popup')) {
      // console.log(componentName, definition.default);
      if (definition.default.popupSetting) definition.default.popupSetting.popupName = componentName
      popupSettingMap.set(componentName, definition.default.popupSetting || {
        popupName: componentName,
        path
      })
    }
  })
}

setupDynamicSvg.ts

프로젝트에서 모든 이미지는 svg를 사용한다. vue 프로젝트에서 이미지와 같은 static resource는 'src/asset'에 위치한다.

scss에서 background-image: url(@/assets/icon/xxx.svg) 로 사용하거나

<VmIcon name="아이콘 이름"/> 컴포넌트에 전체 경로 대신 이름만 입력해서 사용한다.

import { App } from "vue"
import _ from 'lodash'

const icons: {[index: string]: any} = {}
export default function setupDynamicSvg(app: App) {
  // https://vitejs-kr.github.io/guide/features.html#glob-import
  let svgs = import.meta.globEager('../assets/**/**/*.svg')

  Object.entries(svgs).forEach(([path, definition]) => {
    // console.log(path, definition)
    // Get name of component, based on filename
    // "./components/Fruits.vue" will become "Fruits"
    const splitPath = path?.split('/')
    const svgName = splitPath.pop()?.split('.').shift()!
    // console.log(svgName)
    if (icons[svgName]) {
      throw `svg 파일이름 중복 ${path}  ${icons[svgName]}`

    }
    icons[svgName] = definition.default
  })

  // console.log('setup icon', icons);
}

export function useIcon(name?: string) {
  return name ? icons[name] : icons
}

setupInputmaskExtends.ts

<VmSearch> 컴포넌트에서 사용할 입력마스킹(Inputmask)을 정의한다. setupInputmaskExtends.ts에 규칙을 추가하면 @/core/types/Mask.ts에도 alias를 선언하여 마스크 사용시 타입체크를 할 수 있게 한다.

Mask type

volar-components

<template>에서 import하지 않고 사용하는 Global Components는 vscode의 volar에서 타입지원을 하기위해 volar-components.tsType을 등록한다.
volar 사용시 Global Components의 타입-체킹을 가능하게 해준다. Global Components는 setupDynamicComponent.ts에서 등록 하고 있으므로 타입만 선언한다.

volar Define Global Components

// https://github.com/johnsoncodehk/volar/tree/master/extensions/vscode-vue-language-features
declare module '@vue/runtime-core' {
  export interface GlobalComponents {

    // layout
    //VmScroll: typeof import('@/core/components/layout/VmScroll.vue')['default']
    
    
    // button
    VmButton: typeof import('@/core/components/button/VmButton.vue')['default']
    VmButtonPrimary: typeof import('@/core/components/button/VmButtonPrimary.vue')['default']
    VmButtonApply: typeof import('@/core/components/button/VmButtonApply.vue')['default']
    VmButtonClose: typeof import('@/core/components/button/VmButtonClose.vue')['default']
    VmButtonQuery: typeof import('@/core/components/button/VmButtonQuery.vue')['default']
    VmButtonPrint: typeof import('@/core/components/button/VmButtonPrint.vue')['default']
    VmButtonSave: typeof import('@/core/components/button/VmButtonSave.vue')['default']

    // container, row, cell
    VmContainer: typeof import('@/core/components/container/VmContainer.vue')['default']
    VmContainerToggle: typeof import('@/core/components/container/VmContainerToggle.vue')['default']
    VmRow: typeof import('@/core/components/container/VmRow.vue')['default']
    VmCell: typeof import('@/core/components/container/VmCell.vue')['default']
    VmCellLine: typeof import('@/core/components/container/VmVertLine.vue')['default']
    VmLabel: typeof import('@/core/components/container/VmLabel.vue')['default']
    VmLineBreak: typeof import('@/core/components/layout/VmLineBreak.vue')['default']
    VmVertLine: typeof import('@/core/components/layout/VmVertLine.vue')['default']
    // 
    VmProgramTopArea: typeof import('@/core/components/container/VmProgramTopArea.vue')['default']

    // flex
    VmFlexCol: typeof import('@/core/components/container/VmFlexCol.vue')['default']
    VmFlexRow: typeof import('@/core/components/container/VmFlexRow.vue')['default']
    VmFlexBreak: typeof import('@/core/components/container/VmFlexBreak.vue')['default']

    // form
    VmAddrSearch: typeof import('@/core/components/form/VmAddrSearch.vue')['default']
    VmCheckbox: typeof import('@/core/components/form/VmCheckbox.vue')['default']
    VmDate: typeof import('@/core/components/form/VmDate.vue')['default']
    VmMonth: typeof import('@/core/components/form/VmMonth.vue')['default']
    VmYear: typeof import('@/core/components/form/VmYear.vue')['default']
    VmDateRange: typeof import('@/core/components/form/VmDateRange.vue')['default']
    VmMonthRange: typeof import('@/core/components/form/VmMonthRange.vue')['default']
    // VmDateTime: typeof import('@/core/components/form/VmDateTime.vue')['default']
    VmIcon: typeof import('@/core/components/form/VmIcon.vue')['default']
    VmPopup: typeof import('@/core/components/form/VmPopup.vue')['default']
    VmRadio: typeof import('@/core/components/form/VmRadio.vue')['default']
    VmSearch: typeof import('@/core/components/form/VmSearch.vue')['default']
    VmSelect: typeof import('@/core/components/form/VmSelect.vue')['default']
    VmTextarea: typeof import('@/core/components/form/VmTextarea.vue')['default']
    VmFile: typeof import('@/core/components/form/VmFile.vue')['default']

    // modal
    VmModal: typeof import('@/core/components/modal/VmModal.vue')['default']
    VmModalComp: typeof import('@/core/components/modal/VmModalComp.vue')['default']

    // grid
    VmGrid: typeof import('@/core/components/grid/VmGrid.vue')['default']
    
    // tab
    VmTabs: typeof import('@/core/components/tab/VmTabs.vue')['default']
    VmTab: typeof import('@/core/components/tab/VmTab.vue')['default']

    // tree
    VmTree: typeof import('@/core/components/tree/VmTree.vue')['default']
  }
}
export { }

Hello