Search Form Items
<nv-search-form></nv-search-form>내부 슬롯에서 사용하는 검색용 폼 아이템입니다.
이 컴포넌트들은
nv-input-wrap을 껍데기로 사용합니다. 상세 폼과 같은 그리드 시스템(column-size×merge-cell)을 쓰고,<el-form-item>속성도 그대로 상속받습니다. 자세한 속성 목록은 공식문서를 참고하세요.
각 필드는 상위 nv-search-form 이 내려준 검색 레지스트리에 스스로 등록합니다. 그래서 필드마다 필터 객체를 넘길 필요가 없고, 화면에서 사라진 필드는 조건 태그에서도 자동으로 빠집니다.
공통 Props
| 이름 | 타입 | 기본값 | 설명 |
|---|---|---|---|
nv-field | ModelField | undefined | 모델 필드 정의. 조건 키(key)와 라벨(label)을 함께 제공한다 |
nv-key | string | '' | 조건 키. nv-field 를 주면 그쪽 key 가 쓰인다 |
nv-label | string / null | null | 라벨로 표시될 문자열. 생략하면 nv-field.label |
nv-init-value | string / null / any | null | 초기값 (검색어 또는 초기 선택값 등) |
no-label | boolean | false | true면 라벨 표시 안 함 (검색 바처럼 좁은 곳) |
merge-cell | number | 1 | grid의 column 병합 — #inline 에서도 동작한다 |
column | boolean | false | 위젯을 세로로 쌓는다 |
nv-and-or | string | 'and' | 앞 조건과의 결합자 (and, or) |
nv-oper | string | 'eq' | 연산자 (eq, lf, gte 등) |
nv-filter | SearchRegistry | undefined | 화면이 직접 만든 레지스트리를 쓸 때만 지정. 보통 생략한다 |
nv-search-field
- 검색 필드의 진입점입니다.
nv-field.type으로 위젯을 자동 선택하므로, 화면은 "어떤 필드인지"만 적으면 됩니다. - CMS 의 모든 검색 화면이 이 컴포넌트를 씁니다. 아래의 개별
nv-search-*는 이 컴포넌트가 위임하는 구현체입니다.
nv-field.type | 선택되는 위젯 |
|---|---|
Boolean | nv-search-toggle |
Instant / LocalDateTime / LocalDate / Date | nv-search-period |
그 외 + nv-lv-mapper 지정 | nv-search-select |
| 그 외 | nv-search-text |
타입만으로 정할 수 없는 것은 widget 으로 지정합니다.
widget | 쓰는 경우 |
|---|---|
code | 같은 Boolean/코드 목록이라도 라디오로 보여야 할 때 |
checkbox | 같은 코드 목록이라도 다중 선택이어야 할 때 |
text / select / period / toggle | 자동 선택 결과를 덮어쓸 때 |
custom | 기본 슬롯을 쓸 때 (슬롯을 주면 자동으로 선택됩니다) |
<nv-search-field :nv-field="nv.fields.board.NAME"/> <!-- String → text -->
<nv-search-field :nv-field="nv.fields.board.ENABLED"/> <!-- Boolean → toggle -->
<nv-search-field :nv-field="nv.fields.board.CREATED_DT"/> <!-- Instant → period -->
<nv-search-field :nv-field="nv.fields.board.STATUS" :nv-lv-mapper="codes.status"/> <!-- → select -->
<nv-search-field widget="code" :nv-field="nv.fields.board.ENABLED" :nv-lv-mapper="codes.boolean"/> <!-- 토글 대신 라디오 -->커스텀 위젯 (기본 슬롯)
- 전용
nv-search-*컴포넌트를 새로 만들지 않고도 아무 컴포넌트나 검색 필드로 쓸 수 있습니다. - 기본 슬롯을 주면
nv-search-field가nv-search-custom으로 위임합니다. 등록·해제·그리드·라벨·조건 태그는 껍데기가 처리하고, 슬롯은 조건 노드(model)만 받아v-model로 물리면 됩니다.
| 이름 | 타입 | 기본값 | 설명 |
|---|---|---|---|
nv-format | (node) => string | null | n => n.val | 조건 태그에 표시할 값 |
nv-has-value | (node) => boolean | val 이 비어있지 않은지 | 조건 태그 노출 여부 |
nv-init-node | () => any | {ao, op, val} | 조건 노드의 초기 구조 |
값 하나짜리 위젯
el-rate 처럼 검색 컴포넌트가 아닌 것도 그대로 씁니다. 값을 넣으면 아래에 조건 태그가 뜹니다.
<nv-search-field :nv-field="nv.fields.review.RATE"
:nv-init-value="0"
:nv-has-value="hasRate"
:nv-format="fmtRate"
v-slot="{ model }">
<el-rate v-model="model.val"/>
</nv-search-field>const hasRate = (node) => node.val > 0;
const fmtRate = (node) => '★'.repeat(node.val ?? 0);TIP
콜백은 조건 노드 전체를 받습니다(node.val 이 아니라 node). nv-init-node 로 노드 모양을 바꿀 수 있기 때문입니다.
노드 구조를 바꾸는 위젯 (범위 조건)
nv-init-node 로 노드 모양을 바꾸면 위젯 하나가 조건 여러 개를 만들 수 있습니다. {from, to} 에 ops 를 넣으면 reduceQ 가 같은 경로에 조건 두 개(gte/lte)를 생성합니다.
<nv-search-field :nv-field="nv.fields.order.AMOUNT"
:nv-init-node="amountRange"
:nv-has-value="hasAmount"
:nv-format="fmtAmount"
v-slot="{ model }">
<el-input-number v-model="model.from.value" :min="0" :step="1000" controls-position="right"/>
<span class="q-mx-xs">~</span>
<el-input-number v-model="model.to.value" :min="0" :step="1000" controls-position="right"/>
</nv-search-field>const amountRange = () => ({
ops: ['gte', 'lte'], // 없으면 기간(d_gt/d_lt)으로 해석된다
from: { enabled: true, value: null },
to: { enabled: true, value: null },
});
const hasAmount = (node) => node.from.value != null || node.to.value != null;
const fmtAmount = (node) => `${node.from.value ?? ''} ~ ${node.to.value ?? ''}`;전송되는 조건 트리:
{ "ao": "and", "conds": [
{ "field": "ord.amount", "op": "gte", "val": 10000 },
{ "field": "ord.amount", "op": "lte", "val": 50000 }
]}WARNING
nv-field.key 는 백엔드 {X}SearchSchema 에 선언된 경로여야 하고, 쓰는 연산자(gte/lte 등)도 허용돼 있어야 합니다. 선언에 없으면 조건이 조용히 드롭됩니다(fail-closed). 상세는 저장소의 docs/backend/search-query-spec.md 참고
nv-search-dynamic
- 이 컴포넌트는
<el-select>컴포넌트의 속성들을 그대로 상속받아 활용할 수 있습니다. 자세한 속성 목록은 공식문서를 참고하세요.
<nv-search-dynamic nv-key="keyword" :nv-lv-mapper="codes.keyword" />const codes = ref(
filterTool.toMapper({
keyword: [
{ label: '전체', value: 'acnt.sample1,acnt.sample2' },
{ label: '샘플1', value: 'acnt.sample1' },
{ label: '샘플2', value: 'acnt.sample2' },
],
})
);nv-search-text
- 이 컴포넌트는
<el-input>컴포넌트의 속성들을 그대로 상속받아 활용할 수 있습니다. 자세한 속성 목록은 공식문서를 참고하세요.
<nv-search-text :nv-field="nv.fields.user.SAMPLE"/>const alias = 'acnt';
const SAMPLE = {
key: `${alias}.sample`,
property: 'sample',
type: 'sample',
label: '샘플',
desc: '샘플',
defaultVal: null,
};
const nv = {
fields: {
user: {
SAMPLE: SAMPLE,
},
},
};nv-search-select
- 이 컴포넌트는
<el-select>컴포넌트의 속성들을 그대로 상속받아 활용할 수 있습니다. 자세한 속성 목록은 공식문서를 참고하세요.
<nv-search-select :nv-field="SAMPLE" :nv-lv-mapper="codes.sample"/>const alias = 'acnt';
const SAMPLE = {
key: `${alias}.sample`,
property: 'sample',
type: 'sample',
label: '샘플',
desc: '샘플',
defaultVal: null,
};
const nv = {
fields: {
user: {
SAMPLE: SAMPLE,
},
},
};
const codes = ref(
filterTool.toMapper({
sample: [
{ label: '전체', value: null },
{ label: '샘플1', value: 'a' },
{ label: '샘플2', value: 'b' },
{ label: '샘플3', value: 'c' },
],
})
);nv-search-code (Radio)
- 이 컴포넌트는
<el-radio>컴포넌트의 속성들을 그대로 상속받아 활용할 수 있습니다. 자세한 속성 목록은 공식문서를 참고하세요.
<nv-search-code :nv-field="nv.fields.user.SAMPLE" :nv-lv-mapper="codes.boolean"/>const alias = 'acnt';
const SAMPLE = {
key: `${alias}.sample`,
property: 'sample',
type: 'sample',
label: '샘플',
desc: '샘플',
defaultVal: null,
};
const nv = {
fields: {
user: {
SAMPLE: SAMPLE,
},
},
};
const codes = ref(
filterTool.toMapper({
boolean: [
{ label: 'A', value: 'A' },
{ label: 'B', value: 'B' },
{ label: 'C', value: 'C' },
],
})
);nv-search-period (Date Range Picker)
- 이 컴포넌트는
<el-date-picker>컴포넌트의 속성들을 그대로 상속받아 활용할 수 있습니다. 자세한 속성 목록은 공식문서를 참고하세요.
props
| 이름 | 타입 | 기본값 | 설명 |
|---|---|---|---|
time | boolean | false | 시간까지 포함할지 여부 |
format | string | 'YYYY.MM.DD' | 날짜 포맷 |
nv-init-from-value | string/ null | null | 날짜 range 시작 값 |
nv-init-to-value | string/ null | null | 날짜 range 끝 값 |
<nv-search-period :nv-field="nv.fields.user.SAMPLE"/>const alias = 'acnt';
const SAMPLE = {
key: `${alias}.sample`,
property: 'sample',
type: 'sample',
label: '샘플',
desc: '샘플',
defaultVal: null,
};
const nv = {
fields: {
user: {
SAMPLE: SAMPLE,
},
},
};nv-search-toggle
- 이 컴포넌트는
<el-switch>컴포넌트의 속성들을 그대로 상속받아 활용할 수 있습니다. 자세한 속성 목록은 공식문서를 참고하세요.
<nv-search-toggle :nv-field="nv.fields.user.SAMPLE" :nv-init-value="true"/>const alias = 'acnt';
const SAMPLE = {
key: `${alias}.sample`,
property: 'sample',
type: 'sample',
label: '샘플',
desc: '샘플',
defaultVal: null,
};
const nv = {
fields: {
user: {
SAMPLE: SAMPLE,
},
},
};nv-search-checkbox
- 이 컴포넌트는
<el-checkbox>컴포넌트의 속성들을 그대로 상속받아 활용할 수 있습니다. 자세한 속성 목록은 공식문서를 참고하세요. - 연산자는
in이 기본입니다. 여러 값을 고르면field IN (…)조건이 됩니다.
| 이름 | 타입 | 기본값 | 설명 |
|---|---|---|---|
nv-init-value | any[] / any | null | 초기 선택값. 화면의 기본 범위를 지정할 때 씁니다 |
nv-except-value | any[] | null | 선택할 수 없도록 잠글 값 목록 |
except-null | boolean | false | "전체" 체크박스를 없앱니다 |
<nv-search-checkbox :nv-field="nv.fields.user.SAMPLE" :nv-lv-mapper="codes.check"/>const alias = 'acnt';
const SAMPLE = {
key: `${alias}.sample`,
property: 'sample',
type: 'sample',
label: '샘플',
desc: '샘플',
defaultVal: null,
};
const nv = {
fields: {
user: {
SAMPLE: SAMPLE,
},
},
};
const codes = ref(
filterTool.toMapper({
check: [
{ label: 'A', value: 'A' },
{ label: 'B', value: 'B' },
{ label: 'C', value: 'C' },
],
})
);merge-cell
merge-cellprop을 사용하여 폼 필드가 그리드 시스템에서 차지할 컬럼의 수를 동적으로 조절합니다. 이는 마치 HTML 테이블의colspan속성과 유사한 기능을 제공합니다.- 이를 통해 폼 내에서 특정 필드의 너비를 강조하거나 레이아웃을 시각적으로 조정하는 데 유용합니다. 예를 들어, 중요한 입력 필드를 더 넓게 표시하거나, 관련 없는 필드들을 묶어서 공간을 절약할 수 있습니다.
- 상세 폼(
nv-form-group×nv-input-wrap)과 같은 그리드 시스템입니다. 컨테이너가 열 수를 정하고 셀이 그중 몇 칸을 병합할지 정합니다.
| 영역 | 열 수를 정하는 것 | 비고 |
|---|---|---|
고급 검색 (#filters, 탭 슬롯) | nv-search-form 의 column-size (기본 2) | 화면 폭에 따라 자동으로 줄어든다 |
검색 바 (#inline) | 필드 개수만큼 (각 inline-min-cell-width~inline-max-cell-width) | 남는 폭을 먹지 않는다 — 버튼이 필드 바로 뒤에 붙는다 |
| 상세 폼 | nv-form-group 의 column-size (기본 2) |
merge-cell 은 컨테이너의 실제 열 수를 넘지 않도록 자동으로 잘립니다.
label="셀 병합 조절" merge-cell :
<div>
<small>column-size : </small>
<el-input-number v-model="num" :min="1" :max="5" class="q-mb-md" />
<br/>
<small>label="셀 병합 조절" merge-cell : </small>
<el-input-number v-model="mergeCell" :min="1" :max="5" class="q-mb-md" />
</div>
<nv-search-form always-expanded :column-size="num" :query="queries.SAMPLE" v-if="loaded">
<template #filters>
<nv-search-text :merge-cell="mergeCell" :nv-field="nvFields.SAMPLE8"/>
<nv-search-text :nv-field="nvFields.SAMPLE"/>
<nv-search-text :merge-cell="3" :nv-field="nvFields.SAMPLE3"/>
</template>
</nv-search-form>const num = ref(3)
const mergeCell = ref(2)