A powerful and flexible query builder component for Vue 3 with Element Plus
npm install @mvtcode/vue3-querybuilderA powerful and flexible query builder component for Vue 3 with Element Plus.
``bash`
npm install @mvtcode/vue3-querybuilder
`bash`
git clone git@github.com:mvtcode/vue3-querybuilder.git
`vue
`
`typescript`
enum FilterType {
STRING = 'string',
NUMBER = 'number',
BOOLEAN = 'boolean',
DATE = 'date',
}
`typescript`
enum Operator {
EQUAL = 'equal',
NOT_EQUAL = 'not_equal',
CONTAINS = 'contains',
NOT_CONTAINS = 'not_contains',
BEGINS_WITH = 'begins_with',
ENDS_WITH = 'ends_with',
GREATER = 'greater',
GREATER_OR_EQUAL = 'greater_or_equal',
LESS = 'less',
LESS_OR_EQUAL = 'less_or_equal',
IN = 'in',
NOT_IN = 'not_in',
BETWEEN = 'between',
NOT_BETWEEN = 'not_between',
IS_EMPTY = 'is_empty',
IS_NOT_EMPTY = 'is_not_empty',
}
| Prop | Type | Default | Description |
| ---------- | --------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| modelValue | QueryBuilderRule \| QueryBuilderGroup | - | The current query value |Filter[]
| filters | | [] | Array of available filters |number
| maxDepth | | 0 | Maximum depth of nested groups. Set to 0 for unlimited depth, 1 to disable nested groups, or any positive number to limit the nesting level |string
| language | | 'vi' | Language for the component UI (supports 'en' and 'vi') |
| Event | Parameters | Description |
| ----------------- | ------------------------------------------------ | ------------------------------------ |
| update:modelValue | (value: QueryBuilderRule \| QueryBuilderGroup) | Emitted when the query value changes |
`typescript
interface Filter {
field: string
label: string
type: 'string' | 'number' | 'boolean' | 'date'
operators: string[]
}
interface QueryBuilderRule {
type: 'rule'
field: string
operator: string
value: any
}
interface QueryBuilderGroup {
type: 'group'
condition: 'and' | 'or'
rules: (QueryBuilderRule | QueryBuilderGroup)[]
}
`
Each filter can be configured with the following properties:
`typescript`
interface Filter {
field: string // Field name
label: string // Display label
type: 'string' | 'number' | 'boolean' | 'date' // Data type
operators: string[] // Allowed operators
input?: string // Input type ('select', 'radio', 'date', etc.)
values?: Array<{
// Values for select/radio
value: string
text: string
}>
validation?: {
// Validation rules
format?: string // Format for date (YYYY-MM-DD)
min?: number // Minimum value for number
max?: number // Maximum value for number
step?: number // Step for number
}
}
`typescript`
{
field: 'name',
label: 'Name',
type: FilterType.STRING,
operators: [
Operator.EQUAL,
Operator.NOT_EQUAL,
Operator.CONTAINS,
Operator.NOT_CONTAINS,
Operator.BEGINS_WITH,
Operator.NOT_BEGINS_WITH,
Operator.ENDS_WITH,
Operator.NOT_ENDS_WITH,
Operator.IS_EMPTY,
Operator.IS_NOT_EMPTY,
],
}
`typescript`
{
field: 'email',
label: 'Email',
type: FilterType.EMAIL,
operators: [Operator.EQUAL, Operator.NOT_EQUAL, Operator.CONTAINS, Operator.NOT_CONTAINS],
input: 'email',
}
`typescript`
{
field: 'age',
label: 'Age',
type: FilterType.INTEGER,
validation: {
min: 0,
max: 100,
},
operators: [
Operator.EQUAL,
Operator.NOT_EQUAL,
Operator.GREATER,
Operator.GREATER_OR_EQUAL,
Operator.LESS,
Operator.LESS_OR_EQUAL,
Operator.BETWEEN,
Operator.NOT_BETWEEN,
],
}
`typescript`
{
field: 'birthdate',
label: 'Birth Date',
type: FilterType.DATE,
input: 'date',
validation: {
format: 'YYYY-MM-DD',
},
operators: [
Operator.EQUAL,
Operator.NOT_EQUAL,
Operator.GREATER,
Operator.GREATER_OR_EQUAL,
Operator.LESS,
Operator.LESS_OR_EQUAL,
Operator.BETWEEN,
Operator.NOT_BETWEEN,
],
}
`typescript`
{
field: 'active',
label: 'Active',
type: FilterType.BOOLEAN,
input: 'checkbox',
}
`typescript`
{
field: 'status',
label: 'Status',
type: FilterType.STRING,
input: 'select',
value: 'pending', // Giá trị mặc định
operators: [Operator.EQUAL, Operator.NOT_EQUAL, Operator.IN, Operator.NOT_IN],
}
Component supports the following operators:
- equal: Equal (=)not_equal
- : Not Equal (≠)contains
- : Contains (⊃)not_contains
- : Not Contains (⊅)begins_with
- : Begins Withends_with
- : Ends Withgreater
- : Greater Than (>)greater_or_equal
- : Greater Than or Equal (≥)less
- : Less Than (<)less_or_equal
- : Less Than or Equal (≤)in
- : In Listnot_in
- : Not In Listbetween
- : Betweennot_between
- : Not Betweenis_empty
- : Is Emptyis_not_empty
- : Is Not Empty
The result is returned as an object with the following structure:
`typescript`
{
type: 'group',
condition: 'and' | 'or',
rules: [
{
type: 'rule',
field: string,
operator: string,
value: any
},
// or another group
{
type: 'group',
condition: 'and' | 'or',
rules: []
}
]
}
`typescript
import { toSQL } from '@mvtcode/vue3-querybuilder'
const rules = {
type: 'group',
condition: 'and',
rules: [
{
type: 'rule',
field: 'name',
operator: Operator.EQUAL,
value: 'John',
},
{
type: 'rule',
field: 'age',
operator: Operator.GREATER_OR_EQUAL,
value: 18,
},
],
}
const sqlWhere = toSQL(rules)
// Output: name = 'John' AND age >= 18
`
`typescript
import { toMongo } from '@mvtcode/vue3-querybuilder'
const rules = {
type: 'group',
condition: 'and',
rules: [
{
type: 'rule',
field: 'name',
operator: Operator.EQUAL,
value: 'John',
},
{
type: 'rule',
field: 'age',
operator: Operator.GREATER_OR_EQUAL,
value: 18,
},
],
}
const mongoQuery = toMongo(rules)
// Output: {
// $and: [
// { name: { $eq: 'John' } },
// { age: { $gte: 18 } }
// ]
// }
`
`typescript
import { fromSQL } from '@mvtcode/vue3-querybuilder'
const rules = fromSQL("name = 'John' AND age >= 18")
// Output: {
// type: 'group',
// condition: 'and',
// rules: [
// {
// type: 'rule',
// field: 'name',
// operator: Operator.EQUAL,
// value: 'John'
// },
// {
// type: 'rule',
// field: 'age',
// operator: Operator.GREATER_OR_EQUAL,
// value: 18
// }
// ]
// }
`
`typescript
import { fromMongo } from '@mvtcode/vue3-querybuilder'
const rules = fromMongo({
$and: [{ name: { $eq: 'John' } }, { age: { $gte: 18 } }],
})
// Output: {
// type: 'group',
// condition: 'and',
// rules: [
// {
// type: 'rule',
// field: 'name',
// operator: Operator.EQUAL,
// value: 'John'
// },
// {
// type: 'rule',
// field: 'age',
// operator: Operator.GREATER_OR_EQUAL,
// value: 18
// }
// ]
// }
`
| QueryBuilder Operator | SQL Operator | MongoDB Operator |
| --------------------- | ------------ | ---------------- |
| EQUAL | = | $eq |
| NOT_EQUAL | != | $ne |
| CONTAINS | LIKE | $regex |
| NOT_CONTAINS | NOT LIKE | $not |
| BEGINS_WITH | LIKE | $regex |
| ENDS_WITH | LIKE | $regex |
| GREATER | > | $gt |
| GREATER_OR_EQUAL | >= | $gte |
| LESS | < | $lt |
| LESS_OR_EQUAL | <= | $lte |
| IN | IN | $in |
| NOT_IN | NOT IN | $nin |
| BETWEEN | BETWEEN | $and |
| NOT_BETWEEN | NOT BETWEEN | $nor |
| IS_EMPTY | IS NULL | $exists: false |
| IS_NOT_EMPTY | IS NOT NULL | $exists: true |
Component cung cấp dynamic slots cho mỗi field với các props mở rộng:
`vue${widthValueInput}px
placeholder="Enter email"
clearable
:style="{ width: }"
/>
v-model="rule.value"
:min="0"
:max="100"
clearable
:style="{ width: ${widthValueInput}px }"
/>
:min="0"
:max="100"
clearable
:style="{ width: ${widthValueInput}px }"${widthValueInput}px
/>
and
:min="0"
:max="100"
clearable
:style="{ width: }"
/>
:type="isBetween ? 'daterange' : 'date'"
placeholder="Select date"
range-separator="To"
start-placeholder="Start date"
end-placeholder="End date"
clearable
:style="{ width: ${widthValueInput}px }"
/>
>Active/Inactive
placeholder="Select status"
:multiple="[Operator.IN, Operator.NOT_IN].includes(rule.operator)"
clearable
:style="{ width: ${widthValueInput}px }"
>
`
- FilterType.EMAIL: Tự động validation email
- FilterType.INTEGER: Hỗ trợ validation min/max và BETWEEN operators
- FilterType.DATE: Hỗ trợ date picker và date range
- FilterType.BOOLEAN: Hỗ trợ checkbox input
- FilterType.STRING: Hỗ trợ tất cả string operators
- BETWEEN/NOT_BETWEEN: Tự động chuyển đổi input thành range
- IN/NOT_IN: Tự động enable multiple selection
- IS_EMPTY/IS_NOT_EMPTY: Không cần input value
- BEGINS_WITH/ENDS_WITH: Hỗ trợ pattern matching
Tất cả slots đều nhận widthValueInput prop để control width của input:
`vue`
| Name | Type | Description |
| ----------------- | ------------------ | ---------------------------------------------- |
| rule | QueryBuilderRule | Toàn bộ rule object với field, operator, value |operator
| | string | Current operator của rule |value
| | any | Current value của rule (alias cho rule.value) |isBetween
| | boolean | Có phải BETWEEN/NOT_BETWEEN operator không |widthValueInput
| | number | Width được tính toán cho value input |index
| | number | Index của rule trong group |
Component tự động tạo slots dựa trên field names trong filters configuration. Ví dụ, nếu bạn có filter với field: 'name', bạn có thể sử dụng #name slot để customize input.
Mỗi slot nhận các props:
- rule: Toàn bộ rule object với field, operator, valueoperator
- : Current operator được chọn cho rulevalue
- : Current value của rule (alias cho rule.value)isBetween
- : Boolean flag chỉ ra operator có phải BETWEEN hoặc NOT_BETWEEN khôngwidthValueInput
- : Width được tính toán cho value inputindex
- : Index của rule trong group
Khi isBetween là true, rule.value sẽ là array với 2 elements cho range values.
Với cấu hình filters trong ví dụ, bạn có thể tạo ra các query phức tạp như:
`json`
{
"condition": "AND",
"rules": [
{
"id": "uuid-1",
"field": "name",
"operator": "contains",
"value": "John"
},
{
"id": "uuid-2",
"field": "age",
"operator": "between",
"value": [18, 65]
},
{
"id": "uuid-3",
"field": "birthdate",
"operator": "greater",
"value": "1990-01-01"
},
{
"id": "uuid-4",
"field": "active",
"operator": "equal",
"value": true
},
{
"id": "uuid-5",
"field": "status",
"operator": "in",
"value": ["pending", "completed"]
}
]
}
Query này sẽ được convert thành:
SQL:
`sql`
name LIKE '%John%'
AND age BETWEEN 18 AND 65
AND birthdate > '1990-01-01'
AND active = true
AND status IN ('pending', 'completed')
MongoDB:
`javascript`
{
$and: [
{ name: { $regex: 'John', $options: 'i' } },
{ age: { $gte: 18, $lte: 65 } },
{ birthdate: { $gt: '1990-01-01' } },
{ active: { $eq: true } },
{ status: { $in: ['pending', 'completed'] } },
]
}
`shInstall dependencies
npm install
Examples
$3
Đây là ví dụ đầy đủ từ file App.vue với tất cả các loại filter và custom slots:
`vue
Vue 3 QueryBuilder Demo
v-model="rule.value"
placeholder="Enter email"
clearable
:style="{ width: ${widthValueInput}px }"
/>
v-if="!isBetween"
v-model="rule.value"
:min="0"
:max="100"
clearable
:style="{ width: ${widthValueInput}px }"
/>
v-model="(rule.value as number[])[0]"
:min="0"
:max="100"
clearable
:style="{ width: ${widthValueInput}px }"
/>
and
v-model="(rule.value as number[])[1]"
:min="0"
:max="100"
clearable
:style="{ width: ${widthValueInput}px }"
/>
v-model="rule.value"
:type="isBetween ? 'daterange' : 'date'"
placeholder="Select date"
range-separator="To"
start-placeholder="Start date"
end-placeholder="End date"
clearable
:style="{ width: ${widthValueInput}px }"
/>
}"
>Active/Inactive >
v-model="rule.value"
placeholder="Select status"
:multiple="[Operator.IN, Operator.NOT_IN].includes(rule.operator)"
clearable
:style="{ width: ${widthValueInput}px }"
>
Current Rules:
{{ JSON.stringify(rules, null, 2) }}
`$3
`vue
v-model="query"
:filters="filters"
:max-depth="2"
@update:modelValue="onQueryChange"
/>
`$3
-
maxDepth={0}: Unlimited nesting (default)
- maxDepth={1}: Disable nested groups completely
- maxDepth={n}`: Limit nesting to n levels (where n is a positive number)MIT
!Vue 3 QueryBuilder Screenshot
Mạc Tân (Tanmv)
Email: tanmv@mpos.vn
FB: Mạc Tân
Telegram: @tanmac
Skype: trai_12a1