Giao diện
CFilterBar
Thanh lọc ngang đặt phía trên bảng: ô tìm kiếm dựng sẵn, chỗ cắm vài control lọc, nút Đặt lại.
Dùng khi: trang danh sách chỉ cần hai đến ba điều kiện lọc và muốn chúng luôn nhìn thấy.
Không dùng khi: từ bốn điều kiện trở lên, hoặc điều kiện phức tạp (khoảng ngày, chọn nhiều cấp) — dùng drawer lọc dựng sẵn của CTable, gọn hơn và có sẵn thanh điều kiện đang áp dụng.
vue
<script setup lang="ts">
import { computed, ref } from 'vue'
import { CSelect, CSelectOption, message } from '@tasco/ui'
// Thanh lọc ngang đặt trên bảng: ô tìm kiếm dựng sẵn + control lọc do trang cắm vào
// slot mặc định. CFilterBar KHÔNG tự gọi API — nó chỉ phát `search` và `reset`.
interface Order { id: number, code: string, customer: string, status: 'new' | 'done' }
const ALL: Order[] = [
{ id: 1, code: 'DH-0001', customer: 'Công ty An Bình', status: 'done' },
{ id: 2, code: 'DH-0002', customer: 'Tập đoàn Bắc Sơn', status: 'new' },
{ id: 3, code: 'DH-0003', customer: 'Cửa hàng Cường Thịnh', status: 'new' },
]
const columns = [
{ title: 'Mã đơn', dataIndex: 'code', key: 'code', width: 140 },
{ title: 'Khách hàng', dataIndex: 'customer', key: 'customer' },
]
const keyword = ref('')
const status = ref<string | undefined>()
const applied = ref({ keyword: '', status: undefined as string | undefined })
const dataSource = computed(() =>
ALL.filter((order) => {
const byKeyword = !applied.value.keyword
|| `${order.code} ${order.customer}`.toLowerCase().includes(applied.value.keyword.toLowerCase())
const byStatus = !applied.value.status || order.status === applied.value.status
return byKeyword && byStatus
}),
)
function onSearch(value: string) {
applied.value = { keyword: value, status: status.value }
message.info('Trang tự gọi useTable.reload() ở bước này')
}
function onReset() {
status.value = undefined
applied.value = { keyword: '', status: undefined }
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<CFilterBar
v-model:search-value="keyword"
search-placeholder="Tìm mã đơn hoặc khách hàng…"
@search="onSearch"
@reset="onReset"
>
<CSelect
v-model:value="status"
placeholder="Trạng thái"
allow-clear
style="min-width: 160px"
>
<CSelectOption value="new">
Mới
</CSelectOption>
<CSelectOption value="done">
Hoàn thành
</CSelectOption>
</CSelect>
<template #actions>
<CButton variant="outline">
Xuất Excel
</CButton>
</template>
</CFilterBar>
<CTable
row-key="id"
:columns="columns"
:data-source="dataSource"
:pagination="false"
/>
</div>
</template>Component không tự gọi API
CFilterBar chỉ phát sự kiện; trang quyết định làm gì:
| Sự kiện | Khi nào |
|---|---|
@search(value) | Bấm nút tìm hoặc nhấn Enter trong ô tìm kiếm |
@reset | Bấm "Đặt lại" — ô tìm kiếm đã được xoá, các control khác do trang tự xoá |
update:searchValue | Mỗi lần gõ (qua v-model:search-value) |
ts
function onSearch(value: string) {
filters.value = { ...filters.value, keyword: value }
reload() // useTable
}
function onReset() {
status.value = undefined // xoá control của trang
filters.value = {}
reload()
}Quên xử lý @reset là lỗi hay gặp nhất: ô tìm kiếm trắng nhưng select vẫn giữ giá trị cũ.
Cắm control lọc
Slot mặc định nhận bất kỳ control nào của @tasco/ui:
vue
<CFilterBar v-model:search-value="keyword" @search="onSearch" @reset="onReset">
<CSelect v-model:value="status" placeholder="Trạng thái" allow-clear style="min-width: 160px" />
<CRangePicker v-model:value="range" />
<template #actions>
<CButton variant="outline">Xuất Excel</CButton>
</template>
</CFilterBar>Slot #actions đặt nút phụ sau ô tìm kiếm — dành cho hành động liên quan tới tập dữ liệu đang lọc, không phải hành động của cả trang (những cái đó thuộc về CPageHeader).
Đặt min-width cho select để thanh lọc không nhảy khi đổi giá trị.
Khoá khi đang tải
vue
<CFilterBar :loading="loading" />loading khoá ô tìm kiếm và nút, tránh người dùng bắn liên tiếp nhiều lời gọi trong lúc bảng đang tải.
API
Props
| Tên | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
searchValue | string | '' | v-model:searchValue — text ô tìm kiếm. |
searchPlaceholder | string | 'Tìm kiếm…' | Gợi ý trong ô tìm kiếm. |
showSearch | boolean | true | Hiện ô tìm kiếm. |
showReset | boolean | true | Hiện nút "Đặt lại" (sự kiện reset). |
loading | boolean | false | Khoá thanh lọc khi trang đang tải dữ liệu. |
Sự kiện
| Tên | Payload | Mô tả |
|---|---|---|
update:searchValue | [v: string] | v-model:searchValue. |
search | [value: string] | Bấm nút tìm kiếm hoặc nhấn Enter — trang tự gọi useTable.reload(). |
reset | [] | Bấm "Đặt lại": text tìm kiếm đã được xoá, trang tự xoá các bộ lọc còn lại. |
Slot
| Tên | Props của slot | Mô tả |
|---|---|---|
#default | any | Các control lọc đặt bên trái (CSelect, CDatePicker…). |
#actions | any | Nút phụ đặt sau ô tìm kiếm. |
Khả năng tiếp cận
- Ô tìm kiếm là
<input>thật, Enter kích hoạt tìm kiếm — không cần bấm chuột. - Mỗi control lọc cắm vào slot cần nhãn riêng (
placeholderlà gợi ý, không thay được nhãn với trình đọc màn hình): dùngaria-labelkhi không có nhãn hiển thị. - Sau khi bấm Đặt lại, giữ focus ở thanh lọc để người dùng bàn phím tiếp tục thao tác.
Lưu ý
- Không tự debounce: gõ tới đâu gọi API tới đó là quyết định của trang.
- Điều kiện đang áp dụng không hiện thành thẻ như drawer của
CTable— cần nhìn thấy điều kiện thì chọn drawer. - Thanh lọc nằm ngoài bảng, nên khi bảng thu gọn (
collapsible) thanh lọc vẫn hiện.
Liên quan
- Lọc và tìm kiếm của CTable — drawer cho nhiều điều kiện.
- Trang danh sách — chọn giữa hai cách lọc.