vxe-grid 컴포넌트에서 사용자 정의 템플릿 활용하기

vxe-grid 컴포넌트는 vxe-table을 기반으로 하여 테이블을 렌더링할 때, 기본 설정만으로는 충족되지 않는 복잡한 비즈니스 요구사항을 구현해야 할 경우 사용자 정의 템플릿을 통해 유연성을 극대화할 수 있습니다. vxe-grid는 슬롯(slot)을 이용한 템플릿 사용자 정의를 지원합니다.

셀 내용 사용자 정의

테이블 셀에 표시되는 내용을 동적으로 변경하거나 특정 컴포넌트를 삽입해야 할 때 셀 템플릿을 사용합니다. 각 셀은 자체적인 슬롯을 가지며, 이를 통해 해당 셀의 렌더링 방식을 정의할 수 있습니다.

<template>
  <div>
    <vxe-grid v-bind="gridOptions">
      <!-- 이미지 URL을 vxe-image 컴포넌트로 렌더링 -->
      <template #imageUrl_cell="{ row }">
        <vxe-image :src="row.imageUrl" width="40" height="35" radius="4"></vxe-image>
      </template>

      <!-- 이름 필드 클릭 시 상세 정보 모달 호출 -->
      <template #name_cell="{ row }">
        <vxe-button mode="text" @click="showDetails(row)">{{ row.name }} 보기</vxe-button>
      </template>

      <!-- 성별 코드를 텍스트로 변환하여 표시 -->
      <template #gender_cell="{ row }">
        <span>{{ formatGender(row.gender) }}</span>
      </template>

      <!-- 주소 필드에 아이콘과 텍스트 조합 -->
      <template #address_cell="{ row }">
        <vxe-icon name="location" style="color: #1890ff;"></vxe-icon>
        <span>{{ row.address }}</span>
        <vxe-icon name="refresh" style="color: #f5222d;"></vxe-icon>
      </template>
    </vxe-grid>
  </div>
</template>

<script setup>
import { reactive } from 'vue'
import { VxeUI } from 'vxe-table' // 모달 메시지 표시를 위해 import

const gridOptions = reactive({
  border: true,
  showHeader: true,
  columns: [
    { type: 'seq', width: 70, title: '번호' },
    { field: 'imageUrl', title: '사진', slots: { default: 'imageUrl_cell' } },
    { field: 'name', title: '이름', width: 150, slots: { default: 'name_cell' } },
    {
      title: '정보 그룹',
      children: [
        { field: 'gender', title: '성별', slots: { default: 'gender_cell' } },
        { field: 'score', title: '점수', align: 'right' },
        { field: 'age', title: '나이', align: 'right' }
      ]
    },
    { field: 'address', title: '주소', width: 250, slots: { default: 'address_cell' } }
  ],
  data: [
    { id: 1, name: '김철수', gender: '1', age: 25, score: 95, imageUrl: 'https://vxeui.com/resource/img/fj577.jpg', address: '서울시 강남구' },
    { id: 2, name: '이영희', gender: '0', age: 30, score: 88, imageUrl: 'https://vxeui.com/resource/img/fj581.jpeg', address: '부산시 해운대구' },
    { id: 3, name: '박지성', gender: '1', age: 22, score: 92, imageUrl: 'https://vxeui.com/resource/img/fj581.jpeg', address: '대구시 수성구' }
  ]
})

// 성별 코드를 텍스트로 변환하는 함수
const formatGender = (genderCode) => {
  return genderCode === '1' ? '남성' : '여성'
}

// 상세 정보 표시 함수
const showDetails = (row) => {
  VxeUI.modal.message({
    status: 'info',
    content: `${row.name}님의 상세 정보를 표시합니다.`
  })
}
</script>

헤더 내용 사용자 정의

테이블 헤더에 복잡한 구조나 인터랙티브 요소를 추가하고 싶을 때 헤더 템플릿을 사용합니다. 헤더 슬롯을 통해 헤더 셀의 내용을 완전히 대체할 수 있습니다.

<template>
  <div>
    <vxe-grid v-bind="gridOptions">
      <!-- '이름' 헤더를 다중 라인으로 구성 -->
      <template #header_columnName>
        <div class="custom-header">
          <div class="header-top">사용자</div>
          <div class="header-bottom">이름</div>
        </div>
      </template>

      <!-- '성별' 헤더에 검색 입력 필드 추가 -->
      <template #header_gender>
        <vxe-input v-model="genderFilter" placeholder="성별 검색" size="mini"></vxe-input>
      </template>

      <!-- '정보 그룹' 헤더에 스타일 적용 -->
      <template #header_infoGroup>
        <span style="color: #007bff; font-weight: bold;">개인 정보</span>
      </template>
    </vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const genderFilter = ref('') // 성별 검색어

const gridOptions = reactive({
  border: true,
  columns: [
    { type: 'seq', width: 70, title: '순번' },
    { field: 'name', title: '이름', width: 180, slots: { header: 'header_columnName' } },
    {
      title: '정보 그룹',
      field: 'infoGroup', // 그룹 필드 지정 (선택 사항)
      slots: { header: 'header_infoGroup' },
      children: [
        { field: 'gender', title: '성별', slots: { header: 'header_gender' } },
        { field: 'score', title: '점수', align: 'right' },
        { field: 'age', title: '나이', align: 'right' }
      ]
    },
    { field: 'address', title: '주소' }
  ],
  data: [
    { id: 1, name: '김철수', gender: '1', age: 25, score: 95, address: '서울시 강남구' },
    { id: 2, name: '이영희', gender: '0', age: 30, score: 88, address: '부산시 해운대구' },
    { id: 3, name: '박지성', gender: '1', age: 22, score: 92, address: '대구시 수성구' }
  ]
})
</script>

<style lang="scss" scoped>
.custom-header {
  position: relative;
  height: 25px;
  .header-top {
    position: absolute;
    right: 5px;
    top: -12px;
    font-size: 10px;
    color: #999;
  }
  .header-bottom {
    position: absolute;
    left: 5px;
    bottom: -12px;
    font-weight: bold;
  }
</style>

푸터 내용 사용자 정의

테이블 하단에 요약 정보나 합계 등을 표시할 때 푸터 템플릿을 활용합니다. `showFooter: true` 옵션과 함께 사용되며, 특정 컬럼의 푸터 렌더링을 지정할 수 있습니다.

<template>
  <div>
    <vxe-grid v-bind="gridOptions" :footerData="footerData">
      <!-- '점수' 컬럼의 푸터에 통화 기호와 함께 표시 -->
      <template #score_footer="{ row }">
        <span style="color: #00a870; font-weight: bold;">SUM: ¥{{ row.score }}</span>
      </template>
    </vxe-grid>
  </div>
</template>

<script setup>
import { computed, reactive } from 'vue'

// 특정 필드의 합계를 계산하는 함수
const calculateSum = (list, fieldName) => {
  let total = 0
  if (list && list.length > 0) {
    list.forEach(item => {
      total += Number(item[fieldName] || 0)
    })
  }
  return total
}

const gridOptions = reactive({
  border: true,
  showFooter: true, // 푸터 표시 활성화
  columns: [
    { type: 'seq', width: 70, title: '순번' },
    { field: 'name', title: '이름' },
    { field: 'gender', title: '성별' },
    { field: 'score', title: '점수', align: 'right', slots: { footer: 'score_footer' } },
    { field: 'age', title: '나이', align: 'right' },
    { field: 'address', title: '주소' }
  ],
  data: [
    { id: 1, name: '김철수', gender: '1', age: 25, score: 95, address: '서울시 강남구' },
    { id: 2, name: '이영희', gender: '0', age: 30, score: 88, address: '부산시 해운대구' },
    { id: 3, name: '박지성', gender: '1', age: 22, score: 92, address: '대구시 수성구' }
  ]
})

// 푸터 데이터를 계산하는 computed 속성
const footerData = computed(() => {
  return [
    { name: '총 합계', score: calculateSum(gridOptions.data, 'score') }
  ]
})
</script>

편집 가능한 셀 템플릿

셀 편집 시 사용자 정의 컴포넌트를 사용하거나 편집 로직을 변경해야 할 때 편집 템플릿을 적용합니다. `editRender` 속성과 함께 `slots.edit`을 사용하여 편집 템플릿을 지정합니다.

<template>
  <div>
    <vxe-grid v-bind="gridOptions">
      <!-- 성별 편집 시 vxe-select 사용 -->
      <template #gender_editor="{ row }">
        <vxe-select v-model="row.gender" :options="genderOptions"></vxe-select>
      </template>
      <!-- 성별 표시 템플릿 (편집 불가능할 때) -->
      <template #gender_cell="{ row }">
        <span>{{ formatGenderText(row.gender) }}</span>
      </template>

      <!-- 점수 편집 시 vxe-input 사용 (숫자만 입력 가능하도록) -->
      <template #score_editor="{ row }">
        <vxe-input v-model="row.score" type="number"></vxe-input>
      </template>
    </vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const gridOptions = reactive({
  border: true,
  editConfig: { // 편집 설정
    trigger: 'click', // 클릭 시 편집 모드 활성화
    mode: 'cell' // 셀 단위 편집
  },
  columns: [
    { type: 'checkbox', width: 50 },
    { field: 'name', title: '이름' },
    {
      field: 'gender',
      title: '성별',
      editRender: {}, // 편집 렌더러 활성화
      slots: { edit: 'gender_editor', default: 'gender_cell' } // 편집 및 기본 템플릿 지정
    },
    {
      field: 'score',
      title: '점수',
      align: 'right',
      editRender: {}, // 편집 렌더러 활성화
      slots: { edit: 'score_editor' } // 편집 템플릿 지정
    },
    { field: 'age', title: '나이', align: 'right' },
    { field: 'address', title: '주소' }
  ],
  data: [
    { id: 1, name: '김철수', gender: '1', age: 25, score: 95, address: '서울시 강남구' },
    { id: 2, name: '이영희', gender: '0', age: 30, score: 88, address: '부산시 해운대구' },
    { id: 3, name: '박지성', gender: '1', age: 22, score: 92, address: '대구시 수성구' }
  ]
})

// 성별 옵션
const genderOptions = ref([
  { label: '남성', value: '1' },
  { label: '여성', value: '0' }
])

// 성별 코드를 텍스트로 변환하는 함수 (표시용)
const formatGenderText = (genderCode) => {
  const option = genderOptions.value.find(item => item.value === genderCode)
  return option ? option.label : '알 수 없음'
}
</script>

태그: vxe-grid vxe-table Vue.js 슬롯 템플릿

9월 4일 08:13에 게시됨