feat(frontend): create QuantGridAdapter unified PrimeVue DataTable wrapper with built-in search, sorting, multi-selection, and CSV export
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 10s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 12s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 19s
Validators (Pushes and Pull Requests) / Security & Secrets (push) Successful in 10s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 7s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 10s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 12s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 19s
Validators (Pushes and Pull Requests) / Security & Secrets (push) Successful in 10s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 7s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
<!-- Enterprise Unified Grid Adapter Wrapper: QuantGridAdapter -->
|
||||
<template>
|
||||
<div class="quant-grid-adapter flex flex-col w-full h-full border border-slate-300 rounded-lg shadow-2xs bg-white overflow-hidden">
|
||||
<!-- 그리드 상단 툴바 (엑셀 내보내기, 전체 검색, 컬럼 필터 등) -->
|
||||
<div v-if="showToolbar !== false" class="flex items-center justify-between px-3 py-2 bg-slate-100 border-b border-slate-200 text-xs">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold text-slate-700 flex items-center gap-1">
|
||||
<span>📊</span> {{ title || '데이터 그리드' }}
|
||||
</span>
|
||||
<span v-if="items" class="text-[11px] text-slate-500 font-mono">
|
||||
({{ items.length.toLocaleString() }}건)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<InputText
|
||||
v-if="showSearch !== false"
|
||||
v-model="globalSearchQuery"
|
||||
placeholder="전체 검색..."
|
||||
class="h-7 w-44 text-[11px] px-2 border-slate-300"
|
||||
/>
|
||||
<Button
|
||||
v-if="showExport !== false"
|
||||
label="엑셀 내보내기"
|
||||
icon="pi pi-file-excel"
|
||||
class="p-button-sm p-button-success h-7 text-[11px] px-2 font-bold"
|
||||
@click="exportCSV"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PrimeVue DataTable 래핑 레이어 -->
|
||||
<DataTable
|
||||
ref="dt"
|
||||
v-bind="$attrs"
|
||||
:value="filteredItems"
|
||||
:loading="loading"
|
||||
:paginator="paginator !== false"
|
||||
:rows="rows || 15"
|
||||
:rowsPerPageOptions="[10, 15, 30, 50, 100]"
|
||||
:selection="selectedRows"
|
||||
:selectionMode="selectionMode"
|
||||
dataKey="id"
|
||||
responsiveLayout="scroll"
|
||||
resizableColumns
|
||||
columnResizeMode="fit"
|
||||
reorderableColumns
|
||||
class="p-datatable-sm w-full text-xs flex-1"
|
||||
tableStyle="min-width: 50rem"
|
||||
@update:selection="onSelectionChange"
|
||||
>
|
||||
<template #empty>
|
||||
<div class="text-center py-8 text-slate-400 font-medium">
|
||||
조회된 데이터가 존재하지 않습니다.
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 외부 정의 슬롯 100% 바이패스 전파 -->
|
||||
<template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
|
||||
<slot :name="slotName" v-bind="slotProps || {}"></slot>
|
||||
</template>
|
||||
|
||||
<!-- Checkbox 선택 컬럼 -->
|
||||
<Column v-if="selectionMode === 'multiple'" selectionMode="multiple" headerStyle="width: 3rem" />
|
||||
|
||||
<!-- 동적 컬럼 바인딩 -->
|
||||
<Column
|
||||
v-for="col in columns"
|
||||
:key="col.key"
|
||||
:field="col.key"
|
||||
:header="col.label"
|
||||
:style="{ width: col.width || 'auto', textAlign: col.align || 'left' }"
|
||||
sortable
|
||||
>
|
||||
<template #body="slotProps">
|
||||
<slot :name="col.key" :item="slotProps.data" :value="slotProps.data[col.key]">
|
||||
<span v-if="col.type === 'number'" class="font-mono font-bold text-slate-900">
|
||||
{{ formatNumber(slotProps.data[col.key]) }}
|
||||
</span>
|
||||
<span v-else-if="col.type === 'currency'" class="font-mono font-bold text-blue-700">
|
||||
₩{{ formatNumber(slotProps.data[col.key]) }}
|
||||
</span>
|
||||
<span v-else class="font-medium text-slate-800">
|
||||
{{ slotProps.data[col.key] ?? '-' }}
|
||||
</span>
|
||||
</slot>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import DataTable from 'primevue/datatable';
|
||||
import Column from 'primevue/column';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Button from 'primevue/button';
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false
|
||||
});
|
||||
|
||||
export interface AdapterGridColumn {
|
||||
key: string;
|
||||
label: string;
|
||||
width?: string;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
type?: 'text' | 'number' | 'currency' | 'date' | 'status';
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
title?: string;
|
||||
columns: AdapterGridColumn[];
|
||||
items: any[];
|
||||
loading?: boolean;
|
||||
paginator?: boolean;
|
||||
rows?: number;
|
||||
selectionMode?: 'single' | 'multiple';
|
||||
showToolbar?: boolean;
|
||||
showSearch?: boolean;
|
||||
showExport?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['selection-change', 'update:selection']);
|
||||
|
||||
const dt = ref();
|
||||
const globalSearchQuery = ref('');
|
||||
const selectedRows = ref<any>(null);
|
||||
|
||||
const filteredItems = computed(() => {
|
||||
if (!props.items) return [];
|
||||
if (!globalSearchQuery.value) return props.items;
|
||||
const q = globalSearchQuery.value.toLowerCase();
|
||||
return props.items.filter(item => {
|
||||
return Object.values(item).some(val => String(val ?? '').toLowerCase().includes(q));
|
||||
});
|
||||
});
|
||||
|
||||
const formatNumber = (val: any) => {
|
||||
if (val === undefined || val === null || val === '') return '-';
|
||||
const num = Number(val);
|
||||
return isNaN(num) ? String(val) : num.toLocaleString();
|
||||
};
|
||||
|
||||
const onSelectionChange = (val: any) => {
|
||||
selectedRows.value = val;
|
||||
emit('update:selection', val);
|
||||
emit('selection-change', val);
|
||||
};
|
||||
|
||||
const exportCSV = () => {
|
||||
if (dt.value) {
|
||||
dt.value.exportCSV();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -83,6 +83,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-slate-50 rounded border flex flex-col gap-2">
|
||||
<span class="text-xs font-bold text-slate-800">17. QuantGridAdapter (통합 PrimeVue 그리드 어댑터: 검색, 정렬, 엑셀 내보내기 내장)</span>
|
||||
<QuantGridAdapter
|
||||
title="OMS 주문 실시간 목록"
|
||||
:columns="[
|
||||
{ key: 'orderNo', label: '주문번호', width: '140px' },
|
||||
{ key: 'customerName', label: '거래처명' },
|
||||
{ key: 'amount', label: '금액', width: '130px', type: 'currency', align: 'right' },
|
||||
{ key: 'orderDate', label: '주문일자', width: '110px', align: 'center' }
|
||||
]"
|
||||
:items="showcaseRowData"
|
||||
selectionMode="multiple"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-slate-50 rounded border">
|
||||
<span class="text-xs font-bold text-slate-800 mb-1 block">17. QuantSearchHeaderBar</span>
|
||||
<QuantSearchHeaderBar v-model:searchQuery="showcaseSearch" v-model:selectedStatus="showcaseStatus" :statusOptions="['전체', '승인완료', '검토중']" />
|
||||
@@ -202,6 +217,7 @@ import QuantSearchHeaderBar from '../components/QuantSearchHeaderBar.vue';
|
||||
import QuantStatusChip from '../components/QuantStatusChip.vue';
|
||||
import QuantTabPanel from '../components/QuantTabPanel.vue';
|
||||
import QuantSplitter from '../components/QuantSplitter.vue';
|
||||
import QuantGridAdapter from '../components/QuantGridAdapter.vue';
|
||||
|
||||
import QuantFormModal from '../components/QuantFormModal.vue';
|
||||
import QuantLookupModal from '../components/QuantLookupModal.vue';
|
||||
|
||||
Reference in New Issue
Block a user