@hbdlzy/ui-core 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +34 -2
  2. package/components.manifest.json +31 -2
  3. package/dist/components/BaseTable/BaseTable.types.d.ts +17 -2
  4. package/dist/components/BaseTable/BaseTable.vue.d.ts +4 -4
  5. package/dist/components/BaseTable/index.d.ts +1 -1
  6. package/dist/components/OutlinedTreeSelect/OutlinedTreeSelect.vue.d.ts +1 -1
  7. package/dist/components/SvgIcon/SvgIcon.types.d.ts +6 -0
  8. package/dist/components/SvgIcon/SvgIcon.vue.d.ts +32 -0
  9. package/dist/components/SvgIcon/index.d.ts +3 -0
  10. package/dist/index.cjs +5 -5
  11. package/dist/index.d.ts +51 -12
  12. package/dist/index.js +1014 -147
  13. package/dist/ripple/index.cjs +42 -0
  14. package/dist/ripple/index.d.ts +8 -1
  15. package/dist/ripple/index.js +171 -0
  16. package/dist/style.css +1 -1
  17. package/package.json +17 -2
  18. package/src/components/BaseCard/BaseCard.types.d.ts +36 -0
  19. package/src/components/BaseCard/BaseCard.vue.d.ts +122 -0
  20. package/src/components/BaseCard/README.md +33 -0
  21. package/src/components/BaseCard/index.d.ts +3 -0
  22. package/src/components/BaseEChart/BaseEChart.types.d.ts +26 -0
  23. package/src/components/BaseEChart/BaseEChart.vue.d.ts +66 -0
  24. package/src/components/BaseEChart/README.md +33 -0
  25. package/src/components/BaseEChart/index.d.ts +3 -0
  26. package/src/components/BaseExportButton/BaseExportButton.types.d.ts +21 -0
  27. package/src/components/BaseExportButton/BaseExportButton.utils.d.ts +4 -0
  28. package/src/components/BaseExportButton/BaseExportButton.vue.d.ts +128 -0
  29. package/src/components/BaseExportButton/README.md +34 -0
  30. package/src/components/BaseExportButton/index.d.ts +5 -0
  31. package/src/components/BaseTable/BaseTable.types.d.ts +178 -0
  32. package/src/components/BaseTable/BaseTable.types.ts +19 -2
  33. package/src/components/BaseTable/BaseTable.vue +1105 -118
  34. package/src/components/BaseTable/BaseTable.vue.d.ts +157 -0
  35. package/src/components/BaseTable/README.md +133 -5
  36. package/src/components/BaseTable/index.d.ts +3 -0
  37. package/src/components/BaseTable/index.ts +5 -0
  38. package/src/components/OutlinedCascader/OutlinedCascader.types.d.ts +28 -0
  39. package/src/components/OutlinedCascader/OutlinedCascader.vue.d.ts +94 -0
  40. package/src/components/OutlinedCascader/README.md +34 -0
  41. package/src/components/OutlinedCascader/index.d.ts +3 -0
  42. package/src/components/OutlinedDatePicker/OutlinedDatePicker.types.d.ts +30 -0
  43. package/src/components/OutlinedDatePicker/OutlinedDatePicker.vue.d.ts +97 -0
  44. package/src/components/OutlinedDatePicker/README.md +34 -0
  45. package/src/components/OutlinedDatePicker/index.d.ts +3 -0
  46. package/src/components/OutlinedDateTimePicker/OutlinedDateTimePicker.types.d.ts +29 -0
  47. package/src/components/OutlinedDateTimePicker/OutlinedDateTimePicker.vue.d.ts +106 -0
  48. package/src/components/OutlinedDateTimePicker/README.md +33 -0
  49. package/src/components/OutlinedDateTimePicker/index.d.ts +3 -0
  50. package/src/components/OutlinedInput/OutlinedInput.types.d.ts +32 -0
  51. package/src/components/OutlinedInput/OutlinedInput.vue.d.ts +106 -0
  52. package/src/components/OutlinedInput/README.md +34 -0
  53. package/src/components/OutlinedInput/index.d.ts +3 -0
  54. package/src/components/OutlinedSelect/OutlinedSelect.types.d.ts +41 -0
  55. package/src/components/OutlinedSelect/OutlinedSelect.vue.d.ts +125 -0
  56. package/src/components/OutlinedSelect/README.md +34 -0
  57. package/src/components/OutlinedSelect/index.d.ts +3 -0
  58. package/src/components/OutlinedTimePicker/OutlinedTimePicker.types.d.ts +31 -0
  59. package/src/components/OutlinedTimePicker/OutlinedTimePicker.vue.d.ts +103 -0
  60. package/src/components/OutlinedTimePicker/README.md +33 -0
  61. package/src/components/OutlinedTimePicker/index.d.ts +3 -0
  62. package/src/components/OutlinedTreeSelect/OutlinedTreeSelect.types.d.ts +49 -0
  63. package/src/components/OutlinedTreeSelect/OutlinedTreeSelect.vue.d.ts +146 -0
  64. package/src/components/OutlinedTreeSelect/README.md +34 -0
  65. package/src/components/OutlinedTreeSelect/index.d.ts +3 -0
  66. package/src/components/SvgIcon/README.md +86 -0
  67. package/src/components/SvgIcon/SvgIcon.types.d.ts +6 -0
  68. package/src/components/SvgIcon/SvgIcon.types.ts +6 -0
  69. package/src/components/SvgIcon/SvgIcon.vue +68 -0
  70. package/src/components/SvgIcon/SvgIcon.vue.d.ts +32 -0
  71. package/src/components/SvgIcon/index.d.ts +3 -0
  72. package/src/components/SvgIcon/index.ts +5 -0
  73. package/src/echarts/index.d.ts +9 -0
  74. package/src/excel/exportExcel.d.ts +18 -0
  75. package/src/index.d.ts +30 -0
  76. package/src/index.ts +79 -12
  77. package/src/ripple/README.md +160 -0
  78. package/src/ripple/index.d.ts +9 -0
  79. package/src/ripple/index.ts +15 -1
@@ -1,23 +1,58 @@
1
1
  <template>
2
2
  <div class="base-table">
3
+ <svg class="base-table__svg-sprite" aria-hidden="true" focusable="false">
4
+ <symbol :id="`${baseTableIconPrefix}-search`" viewBox="0 0 17.814 14.035">
5
+ <path fill="currentColor" d="M172.218,201.2a5.254,5.254,0,1,0-1.057.786l2.85,3.721a.659.659,0,0,0,1.047-.8l-2.841-3.709Zm-7.7-3.8a4.056,4.056,0,1,1,4.055,4.044,4.054,4.054,0,0,1-4.055-4.044Zm-2.554-3.968h-3.954a.657.657,0,1,1,0-1.313h3.952a.657.657,0,1,1,0,1.313Zm-.159,8.751h-3.8a.656.656,0,1,1,0-1.312h3.793a.656.656,0,1,1,0,1.312Zm-.78-3.958h-3.016a.659.659,0,1,1,0-1.319h3.015a.659.659,0,1,1,0,1.319Zm4.246,7.929h-7.261a.659.659,0,0,1,0-1.319h7.261a.659.659,0,1,1,0,1.319Z" transform="translate(-157.354 -192.113)" />
6
+ </symbol>
7
+ <symbol :id="`${baseTableIconPrefix}-search-y`" viewBox="0 0 17.814 14.035">
8
+ <path fill="#0938f7" d="M172.218,201.2a5.254,5.254,0,1,0-1.057.786l2.85,3.721a.659.659,0,0,0,1.047-.8l-2.841-3.709Zm-7.7-3.8a4.056,4.056,0,1,1,4.055,4.044,4.054,4.054,0,0,1-4.055-4.044Zm-2.554-3.968h-3.954a.657.657,0,1,1,0-1.313h3.952a.657.657,0,1,1,0,1.313Zm-.159,8.751h-3.8a.656.656,0,1,1,0-1.312h3.793a.656.656,0,1,1,0,1.312Zm-.78-3.958h-3.016a.659.659,0,1,1,0-1.319h3.015a.659.659,0,1,1,0,1.319Zm4.246,7.929h-7.261a.659.659,0,0,1,0-1.319h7.261a.659.659,0,1,1,0,1.319Z" transform="translate(-157.354 -192.113)" />
9
+ </symbol>
10
+ <symbol :id="`${baseTableIconPrefix}-select`" viewBox="0 0 16.04 12.653">
11
+ <path fill="currentColor" d="M0,12.653V11.248H11.792v1.406ZM9.328,4.429H16.04l-3.356,3.8ZM0,7.029V5.624H7.823V7.029ZM0,1.406V0H14.5V1.406Z" />
12
+ </symbol>
13
+ <symbol :id="`${baseTableIconPrefix}-select-y`" viewBox="0 0 16.04 12.653">
14
+ <path fill="#0938f7" d="M0,12.653V11.248H11.792v1.406ZM9.328,4.429H16.04l-3.356,3.8ZM0,7.029V5.624H7.823V7.029ZM0,1.406V0H14.5V1.406Z" />
15
+ </symbol>
16
+ <symbol :id="`${baseTableIconPrefix}-sort-none`" viewBox="0 0 15.351 16.031">
17
+ <g transform="translate(-1299.19 -185.182)">
18
+ <path fill="currentColor" d="M100.721,140.352l-3.679,3.679-3.679-3.679h2.944v-8.831h1.472v8.831h2.944Z" transform="translate(1213.82 57.182)" />
19
+ <path fill="currentColor" d="M92.692,131.679H89.749v8.831H88.277v-8.831H85.333L89.013,128l3.679,3.679Z" transform="translate(1213.856 57.182)" />
20
+ </g>
21
+ </symbol>
22
+ <symbol :id="`${baseTableIconPrefix}-sort-up`" viewBox="0 0 15.351 16.031">
23
+ <g transform="translate(-1299.19 -185.182)">
24
+ <path fill="#8c8c8c" d="M100.721,140.352l-3.679,3.679-3.679-3.679h2.944v-8.831h1.472v8.831h2.944Z" transform="translate(1213.82 57.182)" />
25
+ <path fill="#0938f7" d="M92.692,131.679H89.749v8.831H88.277v-8.831H85.333L89.013,128l3.679,3.679Z" transform="translate(1213.856 57.182)" />
26
+ </g>
27
+ </symbol>
28
+ <symbol :id="`${baseTableIconPrefix}-sort-down`" viewBox="0 0 15.351 16.031">
29
+ <g transform="translate(-1299.19 -185.182)">
30
+ <path fill="#0938f7" d="M100.721,140.352l-3.679,3.679-3.679-3.679h2.944v-8.831h1.472v8.831h2.944Z" transform="translate(1213.82 57.182)" />
31
+ <path fill="#8c8c8c" d="M92.692,131.679H89.749v8.831H88.277v-8.831H85.333L89.013,128l3.679,3.679Z" transform="translate(1213.856 57.182)" />
32
+ </g>
33
+ </symbol>
34
+ </svg>
35
+
3
36
  <div v-if="shouldRenderToolbar" class="base-table__toolbar">
4
37
  <div class="base-table__toolbar-left">
5
38
  <slot name="toolbar"></slot>
6
39
  </div>
7
40
  <div v-if="showPagination" class="base-table__toolbar-right">
8
- <el-pagination
9
- small
10
- background
11
- layout="total, prev, pager, next, sizes, jumper"
12
- :total="paginationState.total"
13
- :current-page="paginationState.currentPage"
14
- :page-size="paginationState.pageSize"
15
- :page-sizes="paginationState.pageSizes"
16
- :pager-count="5"
17
- :popper-append-to-body="false"
18
- @size-change="handleSizeChange"
19
- @current-change="handleCurrentChange"
20
- />
41
+ <el-config-provider :locale="zhCn">
42
+ <el-pagination
43
+ small
44
+ background
45
+ layout="total, prev, pager, next, sizes, jumper"
46
+ :total="paginationState.total"
47
+ :current-page="paginationState.currentPage"
48
+ :page-size="paginationState.pageSize"
49
+ :page-sizes="paginationState.pageSizes"
50
+ :pager-count="5"
51
+ :popper-append-to-body="false"
52
+ @size-change="handleSizeChange"
53
+ @current-change="handleCurrentChange"
54
+ />
55
+ </el-config-provider>
21
56
  </div>
22
57
  </div>
23
58
 
@@ -76,53 +111,202 @@
76
111
  </template>
77
112
 
78
113
  <template v-else-if="shouldRenderBuiltInHeader(column)" #header>
79
- <div class="base-table__header">
80
- <span class="base-table__header-label">{{ column.label }}</span>
81
-
82
- <div class="base-table__header-actions">
83
- <el-dropdown
84
- :ref="bindHeaderSearchDropdown(column)"
85
- class="base-table__header-dropdown"
86
- trigger="click"
87
- :hide-on-click="false"
88
- placement="bottom"
89
- popper-class="base-table__header-search-dropdown"
114
+ <div
115
+ class="base-table__header"
116
+ :class="[
117
+ `base-table__header--${resolveHeaderContentAlign(column)}`,
118
+ {
119
+ 'base-table__header--has-actions': hasHeaderSearch(column),
120
+ 'base-table__header--has-sort': isHeaderSortable(column)
121
+ }
122
+ ]"
123
+ >
124
+ <span class="base-table__header-main">
125
+ <span class="base-table__header-label">{{ column.label }}</span>
126
+ <span
127
+ v-if="isHeaderSortable(column)"
128
+ class="base-table__header-sort"
129
+ :class="{ 'is-active': isHeaderSortActive(column) }"
130
+ aria-hidden="true"
131
+ >
132
+ <SvgIcon
133
+ :prefix="baseTableIconPrefix"
134
+ :icon-class="getHeaderSortIconName(column)"
135
+ class-name="base-table__header-sort-icon"
136
+ />
137
+ </span>
138
+ </span>
139
+
140
+ <div v-if="hasHeaderSearch(column)" class="base-table__header-actions">
141
+ <button
142
+ :ref="bindHeaderSearchTrigger(column)"
143
+ class="base-table__header-icon"
144
+ :class="{ 'is-active': hasHeaderSearchValue(column) }"
145
+ type="button"
146
+ @click.stop="toggleHeaderSearch(column)"
90
147
  >
91
- <button
92
- class="base-table__header-icon"
93
- :class="{ 'is-active': hasHeaderSearchValue(column) }"
94
- type="button"
148
+ <SvgIcon
149
+ :prefix="baseTableIconPrefix"
150
+ :icon-class="getHeaderSearchIconName(column)"
151
+ class-name="base-table__header-search-icon"
152
+ />
153
+ </button>
154
+
155
+ <teleport to="body">
156
+ <div
157
+ v-if="isHeaderSearchVisible(column)"
158
+ :key="getHeaderSearchStateKey(column)"
159
+ class="base-table__header-search-panel"
160
+ :style="getHeaderSearchPanelStyle(column)"
161
+ @click.stop
162
+ @mousedown.stop
95
163
  >
96
- <span class="base-table__header-icon-mark" aria-hidden="true"></span>
97
- </button>
164
+ <div
165
+ v-if="isHeaderSearchSelect(column)"
166
+ class="base-table__header-select"
167
+ >
168
+ <button
169
+ class="base-table__header-select-trigger"
170
+ type="button"
171
+ @click="toggleHeaderSearchSelect(column)"
172
+ >
173
+ <span
174
+ v-if="isHeaderSearchMultiple(column) && getHeaderSearchSelectedLabels(column).length"
175
+ class="base-table__header-select-tags"
176
+ >
177
+ <span
178
+ v-for="label in getHeaderSearchSelectedLabels(column).slice(0, 2)"
179
+ :key="label"
180
+ class="base-table__header-select-tag"
181
+ >
182
+ {{ label }}
183
+ </span>
184
+ <span
185
+ v-if="getHeaderSearchSelectedLabels(column).length > 2"
186
+ class="base-table__header-select-tag base-table__header-select-tag--count"
187
+ >
188
+ +{{ getHeaderSearchSelectedLabels(column).length - 2 }}
189
+ </span>
190
+ </span>
191
+ <span
192
+ v-else
193
+ class="base-table__header-select-text"
194
+ :class="{ 'is-placeholder': !hasHeaderSearchValue(column) }"
195
+ >
196
+ {{ getHeaderSearchSelectLabel(column) }}
197
+ </span>
198
+ <span
199
+ class="base-table__header-select-arrow"
200
+ :class="{ 'is-open': isHeaderSearchSelectOpen(column) }"
201
+ aria-hidden="true"
202
+ ></span>
203
+ </button>
204
+
205
+ <div
206
+ v-if="isHeaderSearchSelectOpen(column)"
207
+ class="base-table__header-select-options"
208
+ >
209
+ <button
210
+ v-if="!isHeaderSearchMultiple(column)"
211
+ class="base-table__header-select-option"
212
+ :class="{ 'is-selected': !hasHeaderSearchValue(column) }"
213
+ type="button"
214
+ @click="handleHeaderSearchSelectEmpty(column)"
215
+ >
216
+ {{ getHeaderSearchPlaceholder(column) }}
217
+ </button>
218
+ <button
219
+ v-for="option in getHeaderSearchOptions(column)"
220
+ :key="String(getHeaderSearchOptionValue(option, column))"
221
+ class="base-table__header-select-option"
222
+ :class="{ 'is-selected': isHeaderSearchOptionSelected(option, column) }"
223
+ type="button"
224
+ :disabled="isHeaderSearchOptionDisabled(option, column)"
225
+ @click="handleHeaderSearchSelectValue(column, getHeaderSearchOptionValue(option, column))"
226
+ >
227
+ <span class="base-table__header-select-option-label">
228
+ {{ getHeaderSearchOptionLabel(option, column) }}
229
+ </span>
230
+ </button>
231
+ </div>
232
+ </div>
233
+
234
+ <div
235
+ v-else-if="isHeaderSearchCascader(column)"
236
+ class="base-table__header-cascader"
237
+ >
238
+ <button
239
+ class="base-table__header-select-trigger"
240
+ type="button"
241
+ @click="toggleHeaderSearchSelect(column)"
242
+ >
243
+ <span
244
+ class="base-table__header-select-text"
245
+ :class="{ 'is-placeholder': !hasHeaderSearchValue(column) }"
246
+ >
247
+ {{ getHeaderSearchCascaderLabel(column) }}
248
+ </span>
249
+ <span
250
+ class="base-table__header-select-arrow"
251
+ :class="{ 'is-open': isHeaderSearchSelectOpen(column) }"
252
+ aria-hidden="true"
253
+ ></span>
254
+ </button>
98
255
 
99
- <template #dropdown>
100
- <el-dropdown-menu class="base-table__header-search-menu">
101
256
  <div
102
- class="base-table__header-search-panel"
103
- :style="{ width: toCssValue(getHeaderSearchWidth(column)) || '280px' }"
104
- @click.stop
257
+ v-if="isHeaderSearchSelectOpen(column)"
258
+ class="base-table__header-cascader-panel"
105
259
  >
106
- <el-input
107
- :model-value="getHeaderSearchValue(column)"
108
- :placeholder="getHeaderSearchPlaceholder(column)"
109
- clearable
110
- @update:model-value="updateHeaderSearchValue(column, $event)"
111
- @keyup.enter="handleHeaderSearch(column)"
112
- />
113
-
114
- <div class="base-table__header-search-footer">
115
- <el-button link type="primary" @click="handleHeaderSearch(column)">
116
- {{ getHeaderSearchSearchText(column) }}
117
- </el-button>
118
- <el-button link @click="handleHeaderSearchReset(column)">
119
- {{ getHeaderSearchResetText(column) }}
120
- </el-button>
260
+ <div
261
+ v-for="(levelOptions, levelIndex) in getHeaderSearchCascaderColumns(column)"
262
+ :key="levelIndex"
263
+ class="base-table__header-cascader-menu"
264
+ >
265
+ <button
266
+ v-for="option in levelOptions"
267
+ :key="`${levelIndex}-${String(getHeaderSearchOptionValue(option, column))}`"
268
+ class="base-table__header-cascader-option"
269
+ :class="{
270
+ 'is-active': isHeaderSearchCascaderOptionActive(column, option, levelIndex),
271
+ 'is-selected': isHeaderSearchCascaderOptionSelected(column, option, levelIndex)
272
+ }"
273
+ type="button"
274
+ :disabled="isHeaderSearchOptionDisabled(option, column)"
275
+ @click="handleHeaderSearchCascaderOption(column, option, levelIndex)"
276
+ >
277
+ <span class="base-table__header-cascader-label">
278
+ {{ getHeaderSearchOptionLabel(option, column) }}
279
+ </span>
280
+ <span
281
+ v-if="hasHeaderSearchOptionChildren(option, column)"
282
+ class="base-table__header-cascader-arrow"
283
+ aria-hidden="true"
284
+ ></span>
285
+ </button>
121
286
  </div>
122
287
  </div>
123
- </el-dropdown-menu>
124
- </template>
125
- </el-dropdown>
288
+ </div>
289
+
290
+ <input
291
+ v-else
292
+ class="base-table__header-search-input"
293
+ type="text"
294
+ :value="getHeaderSearchInputValue(column)"
295
+ :placeholder="getHeaderSearchPlaceholder(column)"
296
+ @input="handleHeaderSearchInput(column, $event)"
297
+ @keyup.enter="handleHeaderSearch(column)"
298
+ />
299
+
300
+ <div class="base-table__header-search-footer">
301
+ <el-button link type="primary" @click="handleHeaderSearch(column)">
302
+ {{ getHeaderSearchSearchText(column) }}
303
+ </el-button>
304
+ <el-button link @click="handleHeaderSearchReset(column)">
305
+ {{ getHeaderSearchResetText(column) }}
306
+ </el-button>
307
+ </div>
308
+ </div>
309
+ </teleport>
126
310
  </div>
127
311
  </div>
128
312
  </template>
@@ -207,15 +391,21 @@
207
391
  </template>
208
392
 
209
393
  <script setup lang="ts">
210
- import { computed, onMounted, reactive, ref, useSlots, watch } from 'vue'
394
+ import { computed, onBeforeUnmount, onMounted, reactive, ref, useSlots, watch } from 'vue'
395
+ import type { ComponentPublicInstance } from 'vue'
211
396
  import type { TableInstance } from 'element-plus'
397
+ import zhCn from 'element-plus/es/locale/lang/zh-cn'
398
+ import SvgIcon from '../SvgIcon'
212
399
  import type {
213
400
  BaseTableAction,
401
+ BaseTableAlign,
214
402
  BaseTableCellPayload,
215
403
  BaseTableColumn,
216
404
  BaseTableCssValue,
217
405
  BaseTableExpose,
218
406
  BaseTableHeaderSearchConfig,
407
+ BaseTableHeaderSearchPrimitiveValue,
408
+ BaseTableHeaderSearchValue,
219
409
  BaseTableLoadedPayload,
220
410
  BaseTableOption,
221
411
  BaseTablePagination,
@@ -227,6 +417,8 @@ import type {
227
417
  BaseTableSortPayload
228
418
  } from './BaseTable.types'
229
419
 
420
+ let baseTableIconSeed = 0
421
+
230
422
  defineOptions({
231
423
  name: 'BaseTable'
232
424
  })
@@ -286,8 +478,14 @@ const loading = ref(false)
286
478
  const sourceRows = ref<BaseTableRow[]>(normalizeRows(props.data))
287
479
  const rows = ref<BaseTableRow[]>(normalizeRows(props.data))
288
480
  const selectionRows = ref<BaseTableRow[]>([])
289
- const headerSearchValues = reactive<Record<string, string>>({})
290
- const headerSearchDropdownRefs = ref<Record<string, { handleClose?: () => void }>>({})
481
+ const headerSearchValues = reactive<Record<string, BaseTableHeaderSearchValue>>({})
482
+ const headerSearchVisible = reactive<Record<string, boolean>>({})
483
+ const headerSearchSelectOpen = reactive<Record<string, boolean>>({})
484
+ const headerSearchCascaderActivePaths = reactive<Record<string, BaseTableHeaderSearchPrimitiveValue[]>>({})
485
+ const headerSearchTriggerRects = reactive<Record<string, DOMRect>>({})
486
+ const headerSearchTriggerRefs = ref<Record<string, HTMLElement>>({})
487
+ const baseTableIconPrefix = `base-table-icon-${++baseTableIconSeed}`
488
+ let headerSearchPositionFrame = 0
291
489
  const sortState = reactive({
292
490
  prop: props.defaultSort?.prop,
293
491
  order: props.defaultSort?.order || null
@@ -353,6 +551,9 @@ watch(
353
551
  )
354
552
 
355
553
  onMounted(() => {
554
+ window.addEventListener('scroll', scheduleHeaderSearchPositionUpdate, true)
555
+ window.addEventListener('resize', scheduleHeaderSearchPositionUpdate)
556
+
356
557
  if (props.request && props.autoLoad) {
357
558
  void load()
358
559
  return
@@ -363,6 +564,16 @@ onMounted(() => {
363
564
  }
364
565
  })
365
566
 
567
+ onBeforeUnmount(() => {
568
+ window.removeEventListener('scroll', scheduleHeaderSearchPositionUpdate, true)
569
+ window.removeEventListener('resize', scheduleHeaderSearchPositionUpdate)
570
+
571
+ if (headerSearchPositionFrame) {
572
+ window.cancelAnimationFrame(headerSearchPositionFrame)
573
+ headerSearchPositionFrame = 0
574
+ }
575
+ })
576
+
366
577
  function syncPagination(value?: Partial<BaseTablePagination>) {
367
578
  const nextPageSizes = value?.pageSizes
368
579
 
@@ -527,8 +738,12 @@ function handleInputChange(value: string, row: BaseTableRow, column: BaseTableCo
527
738
  })
528
739
  }
529
740
 
741
+ function resolveHeaderContentAlign(column: BaseTableColumn<BaseTableRow>): BaseTableAlign {
742
+ return column.headerAlign || column.align || 'left'
743
+ }
744
+
530
745
  function shouldRenderBuiltInHeader(column: BaseTableColumn<BaseTableRow>) {
531
- return hasHeaderSearch(column)
746
+ return hasHeaderSearch(column) || isHeaderSortable(column)
532
747
  }
533
748
 
534
749
  function hasHeaderSearch(column: BaseTableColumn<BaseTableRow>) {
@@ -551,47 +766,199 @@ function getHeaderSearchStateKey(column: BaseTableColumn<BaseTableRow>) {
551
766
  return resolveHeaderSearchParamKey(column) || getColumnProp(column) || getColumnKey(column)
552
767
  }
553
768
 
554
- function setHeaderSearchDropdownRef(
555
- instance: { handleClose?: () => void } | null,
769
+ function resolveHeaderSearchParamKey(column: BaseTableColumn<BaseTableRow>) {
770
+ const config = getHeaderSearchConfig(column)
771
+ const legacyParamKey = (column as Record<string, any>).otherProps
772
+
773
+ return config?.paramKey || legacyParamKey || getColumnProp(column) || ''
774
+ }
775
+
776
+ function setHeaderSearchTriggerRef(
777
+ instance: Element | ComponentPublicInstance | null,
556
778
  column: BaseTableColumn<BaseTableRow>
557
779
  ) {
558
780
  const stateKey = getHeaderSearchStateKey(column)
559
781
 
560
- if (!instance) {
561
- delete headerSearchDropdownRefs.value[stateKey]
782
+ if (!(instance instanceof HTMLElement)) {
783
+ delete headerSearchTriggerRefs.value[stateKey]
562
784
  return
563
785
  }
564
786
 
565
- headerSearchDropdownRefs.value[stateKey] = instance
787
+ headerSearchTriggerRefs.value[stateKey] = instance
566
788
  }
567
789
 
568
- function bindHeaderSearchDropdown(column: BaseTableColumn<BaseTableRow>) {
569
- return (instance: { handleClose?: () => void } | null) => {
570
- setHeaderSearchDropdownRef(instance, column)
790
+ function bindHeaderSearchTrigger(column: BaseTableColumn<BaseTableRow>) {
791
+ return (instance: Element | ComponentPublicInstance | null) => {
792
+ setHeaderSearchTriggerRef(instance, column)
571
793
  }
572
794
  }
573
795
 
574
- function resolveHeaderSearchParamKey(column: BaseTableColumn<BaseTableRow>) {
575
- const config = getHeaderSearchConfig(column)
576
- const legacyParamKey = (column as Record<string, any>).otherProps
796
+ function isHeaderSearchVisible(column: BaseTableColumn<BaseTableRow>) {
797
+ return Boolean(headerSearchVisible[getHeaderSearchStateKey(column)])
798
+ }
577
799
 
578
- return config?.paramKey || legacyParamKey || getColumnProp(column) || ''
800
+ function toggleHeaderSearch(column: BaseTableColumn<BaseTableRow>) {
801
+ const stateKey = getHeaderSearchStateKey(column)
802
+ const nextVisible = !headerSearchVisible[stateKey]
803
+
804
+ if (nextVisible) {
805
+ closeAllHeaderSearch()
806
+ updateHeaderSearchTriggerRect(column)
807
+ syncHeaderSearchCascaderActivePath(column)
808
+ }
809
+
810
+ headerSearchVisible[stateKey] = nextVisible
811
+ headerSearchSelectOpen[stateKey] = false
812
+ }
813
+
814
+ function closeAllHeaderSearch() {
815
+ Object.keys(headerSearchVisible).forEach((key) => {
816
+ headerSearchVisible[key] = false
817
+ headerSearchSelectOpen[key] = false
818
+ })
819
+ }
820
+
821
+ function updateHeaderSearchTriggerRect(column: BaseTableColumn<BaseTableRow>) {
822
+ const stateKey = getHeaderSearchStateKey(column)
823
+ const rect = headerSearchTriggerRefs.value[stateKey]?.getBoundingClientRect()
824
+
825
+ if (rect) {
826
+ headerSearchTriggerRects[stateKey] = rect
827
+ }
579
828
  }
580
829
 
581
- function getHeaderSearchValue(column: BaseTableColumn<BaseTableRow>) {
582
- return headerSearchValues[getHeaderSearchStateKey(column)] || ''
830
+ function updateVisibleHeaderSearchPosition() {
831
+ props.columns.forEach((column) => {
832
+ if (hasHeaderSearch(column) && isHeaderSearchVisible(column)) {
833
+ updateHeaderSearchTriggerRect(column)
834
+ }
835
+ })
836
+ }
837
+
838
+ function scheduleHeaderSearchPositionUpdate() {
839
+ if (headerSearchPositionFrame) {
840
+ return
841
+ }
842
+
843
+ headerSearchPositionFrame = window.requestAnimationFrame(() => {
844
+ headerSearchPositionFrame = 0
845
+ updateVisibleHeaderSearchPosition()
846
+ })
847
+ }
848
+
849
+ function getHeaderSearchPanelStyle(column: BaseTableColumn<BaseTableRow>) {
850
+ const stateKey = getHeaderSearchStateKey(column)
851
+ const triggerRect = headerSearchTriggerRects[stateKey]
852
+ const width = toCssValue(getHeaderSearchWidth(column)) || '280px'
853
+
854
+ if (!triggerRect) {
855
+ return {
856
+ width
857
+ }
858
+ }
859
+
860
+ const panelWidth = Number.parseFloat(String(getHeaderSearchWidth(column))) || 280
861
+ const maxLeft = Math.max(8, window.innerWidth - panelWidth - 8)
862
+ const left = Math.min(maxLeft, Math.max(8, triggerRect.right - panelWidth))
863
+ const top = triggerRect.bottom + 8
864
+
865
+ return {
866
+ width,
867
+ left: `${left}px`,
868
+ top: `${top}px`
869
+ }
870
+ }
871
+
872
+ function getHeaderSearchValue(column: BaseTableColumn<BaseTableRow>): BaseTableHeaderSearchValue {
873
+ const stateKey = getHeaderSearchStateKey(column)
874
+ const value = headerSearchValues[stateKey]
875
+
876
+ if (value !== undefined) {
877
+ return value
878
+ }
879
+
880
+ return getHeaderSearchEmptyValue(column)
881
+ }
882
+
883
+ function getHeaderSearchInputValue(column: BaseTableColumn<BaseTableRow>) {
884
+ const value = getHeaderSearchValue(column)
885
+ return Array.isArray(value) ? value.join(' ') : String(value)
583
886
  }
584
887
 
585
888
  function hasHeaderSearchValue(column: BaseTableColumn<BaseTableRow>) {
586
- return Boolean(getHeaderSearchValue(column).trim())
889
+ return isHeaderSearchValueFilled(getHeaderSearchValue(column))
890
+ }
891
+
892
+ function updateHeaderSearchValue(column: BaseTableColumn<BaseTableRow>, value: unknown) {
893
+ headerSearchValues[getHeaderSearchStateKey(column)] = normalizeHeaderSearchValue(column, value)
894
+ }
895
+
896
+ function handleHeaderSearchInput(column: BaseTableColumn<BaseTableRow>, event: Event) {
897
+ updateHeaderSearchValue(column, (event.target as HTMLInputElement | null)?.value || '')
898
+ }
899
+
900
+ function isHeaderSearchSelect(column: BaseTableColumn<BaseTableRow>) {
901
+ return getHeaderSearchConfig(column)?.type === 'select'
902
+ }
903
+
904
+ function isHeaderSearchCascader(column: BaseTableColumn<BaseTableRow>) {
905
+ return getHeaderSearchConfig(column)?.type === 'cascader'
906
+ }
907
+
908
+ function isHeaderSearchChoice(column: BaseTableColumn<BaseTableRow>) {
909
+ return isHeaderSearchSelect(column) || isHeaderSearchCascader(column)
910
+ }
911
+
912
+ function isHeaderSearchMultiple(column: BaseTableColumn<BaseTableRow>) {
913
+ return isHeaderSearchSelect(column) && getHeaderSearchConfig(column)?.multiple === true
914
+ }
915
+
916
+ function isHeaderSearchClearable(column: BaseTableColumn<BaseTableRow>) {
917
+ return getHeaderSearchConfig(column)?.clearable !== false
918
+ }
919
+
920
+ function isHeaderSearchFilterable(column: BaseTableColumn<BaseTableRow>) {
921
+ return getHeaderSearchConfig(column)?.filterable === true
922
+ }
923
+
924
+ function getHeaderSearchIconName(column: BaseTableColumn<BaseTableRow>) {
925
+ const iconName = isHeaderSearchChoice(column) ? 'select' : 'search'
926
+ return hasHeaderSearchValue(column) ? `${iconName}-y` : iconName
927
+ }
928
+
929
+ function isHeaderSortable(column: BaseTableColumn<BaseTableRow>) {
930
+ return Boolean(resolveColumnSortable(column))
931
+ }
932
+
933
+ function isHeaderSortActive(column: BaseTableColumn<BaseTableRow>) {
934
+ return String(sortState.prop || '') === String(getColumnProp(column) || '') && Boolean(sortState.order)
935
+ }
936
+
937
+ function getHeaderSortIconName(column: BaseTableColumn<BaseTableRow>) {
938
+ if (!isHeaderSortActive(column)) {
939
+ return 'sort-none'
940
+ }
941
+
942
+ return sortState.order === 'ascending' ? 'sort-up' : 'sort-down'
587
943
  }
588
944
 
589
- function updateHeaderSearchValue(column: BaseTableColumn<BaseTableRow>, value: string) {
590
- headerSearchValues[getHeaderSearchStateKey(column)] = normalizeHeaderSearchValue(value)
945
+ function isHeaderSearchSelectOpen(column: BaseTableColumn<BaseTableRow>) {
946
+ return Boolean(headerSearchSelectOpen[getHeaderSearchStateKey(column)])
947
+ }
948
+
949
+ function toggleHeaderSearchSelect(column: BaseTableColumn<BaseTableRow>) {
950
+ const stateKey = getHeaderSearchStateKey(column)
951
+ const nextOpen = !headerSearchSelectOpen[stateKey]
952
+
953
+ if (nextOpen) {
954
+ syncHeaderSearchCascaderActivePath(column)
955
+ }
956
+
957
+ headerSearchSelectOpen[stateKey] = nextOpen
591
958
  }
592
959
 
593
960
  function getHeaderSearchPlaceholder(column: BaseTableColumn<BaseTableRow>) {
594
- return getHeaderSearchConfig(column)?.placeholder || '请输入搜索内容'
961
+ return getHeaderSearchConfig(column)?.placeholder || (isHeaderSearchChoice(column) ? '请选择' : '请输入搜索内容')
595
962
  }
596
963
 
597
964
  function getHeaderSearchWidth(column: BaseTableColumn<BaseTableRow>) {
@@ -606,6 +973,218 @@ function getHeaderSearchResetText(column: BaseTableColumn<BaseTableRow>) {
606
973
  return getHeaderSearchConfig(column)?.resetText || '重置'
607
974
  }
608
975
 
976
+ function getHeaderSearchOptions(column: BaseTableColumn<BaseTableRow>) {
977
+ return getHeaderSearchConfig(column)?.options || column.options || []
978
+ }
979
+
980
+ function getHeaderSearchOptionValue(option: BaseTableOption, column: BaseTableColumn<BaseTableRow>) {
981
+ const valueKey = getHeaderSearchConfig(column)?.optionValueKey || column.optionValueKey || 'value'
982
+ return normalizeHeaderSearchPrimitiveValue((option as Record<string, any>)[valueKey])
983
+ }
984
+
985
+ function getHeaderSearchOptionLabel(option: BaseTableOption, column: BaseTableColumn<BaseTableRow>) {
986
+ const labelKey = getHeaderSearchConfig(column)?.optionLabelKey || column.optionLabelKey || 'label'
987
+ const label = (option as Record<string, any>)[labelKey]
988
+ return label === undefined || label === null ? '' : String(label)
989
+ }
990
+
991
+ function isHeaderSearchOptionDisabled(option: BaseTableOption, column: BaseTableColumn<BaseTableRow>) {
992
+ const disabledKey = getHeaderSearchConfig(column)?.optionDisabledKey || 'disabled'
993
+ return Boolean((option as Record<string, any>)[disabledKey])
994
+ }
995
+
996
+ function getHeaderSearchOptionChildren(option: BaseTableOption, column: BaseTableColumn<BaseTableRow>) {
997
+ const childrenKey = getHeaderSearchConfig(column)?.optionChildrenKey || 'children'
998
+ const children = (option as Record<string, any>)[childrenKey]
999
+ return Array.isArray(children) ? children as BaseTableOption[] : []
1000
+ }
1001
+
1002
+ function hasHeaderSearchOptionChildren(option: BaseTableOption, column: BaseTableColumn<BaseTableRow>) {
1003
+ return getHeaderSearchOptionChildren(option, column).length > 0
1004
+ }
1005
+
1006
+ function getHeaderSearchSelectLabel(column: BaseTableColumn<BaseTableRow>) {
1007
+ const selectedLabels = getHeaderSearchSelectedLabels(column)
1008
+
1009
+ if (!selectedLabels.length) {
1010
+ return getHeaderSearchPlaceholder(column)
1011
+ }
1012
+
1013
+ return selectedLabels.join('、')
1014
+ }
1015
+
1016
+ function getHeaderSearchSelectedLabels(column: BaseTableColumn<BaseTableRow>) {
1017
+ const selectedValues = toComparableValues(getHeaderSearchValue(column))
1018
+
1019
+ return selectedValues.map((selectedValue) => {
1020
+ const matchedOption = getHeaderSearchOptions(column).find((option) => {
1021
+ return String(getHeaderSearchOptionValue(option, column)) === selectedValue
1022
+ })
1023
+
1024
+ return matchedOption ? getHeaderSearchOptionLabel(matchedOption, column) : selectedValue
1025
+ })
1026
+ }
1027
+
1028
+ function isHeaderSearchOptionSelected(option: BaseTableOption, column: BaseTableColumn<BaseTableRow>) {
1029
+ const optionValue = String(getHeaderSearchOptionValue(option, column))
1030
+ return toComparableValues(getHeaderSearchValue(column)).includes(optionValue)
1031
+ }
1032
+
1033
+ function handleHeaderSearchSelectEmpty(column: BaseTableColumn<BaseTableRow>) {
1034
+ updateHeaderSearchValue(column, getHeaderSearchEmptyValue(column))
1035
+ headerSearchSelectOpen[getHeaderSearchStateKey(column)] = false
1036
+ }
1037
+
1038
+ function handleHeaderSearchSelectValue(
1039
+ column: BaseTableColumn<BaseTableRow>,
1040
+ optionValue: BaseTableHeaderSearchPrimitiveValue
1041
+ ) {
1042
+ if (!isHeaderSearchMultiple(column)) {
1043
+ updateHeaderSearchValue(column, optionValue)
1044
+ headerSearchSelectOpen[getHeaderSearchStateKey(column)] = false
1045
+ return
1046
+ }
1047
+
1048
+ const currentValues = Array.isArray(getHeaderSearchValue(column))
1049
+ ? [...getHeaderSearchValue(column) as BaseTableHeaderSearchPrimitiveValue[]]
1050
+ : []
1051
+ const comparableValue = String(optionValue)
1052
+ const matchedIndex = currentValues.findIndex((item) => String(item) === comparableValue)
1053
+
1054
+ if (matchedIndex >= 0) {
1055
+ currentValues.splice(matchedIndex, 1)
1056
+ } else {
1057
+ currentValues.push(optionValue)
1058
+ }
1059
+
1060
+ updateHeaderSearchValue(column, currentValues)
1061
+ }
1062
+
1063
+ function handleHeaderSearchSelectOption(column: BaseTableColumn<BaseTableRow>, option: BaseTableOption) {
1064
+ handleHeaderSearchSelectValue(column, getHeaderSearchOptionValue(option, column))
1065
+ }
1066
+
1067
+ function getHeaderSearchCascaderValue(column: BaseTableColumn<BaseTableRow>) {
1068
+ return normalizeHeaderSearchPathValue(getHeaderSearchValue(column))
1069
+ }
1070
+
1071
+ function syncHeaderSearchCascaderActivePath(column: BaseTableColumn<BaseTableRow>) {
1072
+ if (!isHeaderSearchCascader(column)) {
1073
+ return
1074
+ }
1075
+
1076
+ const stateKey = getHeaderSearchStateKey(column)
1077
+ headerSearchCascaderActivePaths[stateKey] = getHeaderSearchCascaderValue(column)
1078
+ }
1079
+
1080
+ function getHeaderSearchCascaderActivePath(column: BaseTableColumn<BaseTableRow>) {
1081
+ const stateKey = getHeaderSearchStateKey(column)
1082
+ const activePath = headerSearchCascaderActivePaths[stateKey]
1083
+
1084
+ if (activePath) {
1085
+ return activePath
1086
+ }
1087
+
1088
+ return getHeaderSearchCascaderValue(column)
1089
+ }
1090
+
1091
+ function getHeaderSearchCascaderColumns(column: BaseTableColumn<BaseTableRow>) {
1092
+ const columns: BaseTableOption[][] = []
1093
+ let levelOptions = getHeaderSearchOptions(column)
1094
+ const activePath = getHeaderSearchCascaderActivePath(column)
1095
+ let levelIndex = 0
1096
+
1097
+ while (levelOptions.length) {
1098
+ columns.push(levelOptions)
1099
+
1100
+ const activeValue = activePath[levelIndex]
1101
+ const activeOption = levelOptions.find((option) => {
1102
+ return String(getHeaderSearchOptionValue(option, column)) === String(activeValue)
1103
+ })
1104
+
1105
+ if (!activeOption) {
1106
+ break
1107
+ }
1108
+
1109
+ levelOptions = getHeaderSearchOptionChildren(activeOption, column)
1110
+ levelIndex += 1
1111
+ }
1112
+
1113
+ return columns
1114
+ }
1115
+
1116
+ function getHeaderSearchCascaderLabel(column: BaseTableColumn<BaseTableRow>) {
1117
+ const path = getHeaderSearchCascaderValue(column)
1118
+
1119
+ if (!path.length) {
1120
+ return getHeaderSearchPlaceholder(column)
1121
+ }
1122
+
1123
+ return getHeaderSearchCascaderPathLabels(column, path).join(' / ')
1124
+ }
1125
+
1126
+ function getHeaderSearchCascaderPathLabels(
1127
+ column: BaseTableColumn<BaseTableRow>,
1128
+ path = getHeaderSearchCascaderValue(column)
1129
+ ) {
1130
+ const labels: string[] = []
1131
+ let levelOptions = getHeaderSearchOptions(column)
1132
+
1133
+ path.forEach((value) => {
1134
+ const matchedOption = levelOptions.find((option) => {
1135
+ return String(getHeaderSearchOptionValue(option, column)) === String(value)
1136
+ })
1137
+
1138
+ if (!matchedOption) {
1139
+ labels.push(String(value))
1140
+ levelOptions = []
1141
+ return
1142
+ }
1143
+
1144
+ labels.push(getHeaderSearchOptionLabel(matchedOption, column))
1145
+ levelOptions = getHeaderSearchOptionChildren(matchedOption, column)
1146
+ })
1147
+
1148
+ return labels
1149
+ }
1150
+
1151
+ function isHeaderSearchCascaderOptionActive(
1152
+ column: BaseTableColumn<BaseTableRow>,
1153
+ option: BaseTableOption,
1154
+ levelIndex: number
1155
+ ) {
1156
+ const activePath = getHeaderSearchCascaderActivePath(column)
1157
+ return String(activePath[levelIndex]) === String(getHeaderSearchOptionValue(option, column))
1158
+ }
1159
+
1160
+ function isHeaderSearchCascaderOptionSelected(
1161
+ column: BaseTableColumn<BaseTableRow>,
1162
+ option: BaseTableOption,
1163
+ levelIndex: number
1164
+ ) {
1165
+ const selectedPath = getHeaderSearchCascaderValue(column)
1166
+ return String(selectedPath[levelIndex]) === String(getHeaderSearchOptionValue(option, column))
1167
+ }
1168
+
1169
+ function handleHeaderSearchCascaderOption(
1170
+ column: BaseTableColumn<BaseTableRow>,
1171
+ option: BaseTableOption,
1172
+ levelIndex: number
1173
+ ) {
1174
+ const stateKey = getHeaderSearchStateKey(column)
1175
+ const optionValue = getHeaderSearchOptionValue(option, column)
1176
+ const nextPath = [
1177
+ ...getHeaderSearchCascaderActivePath(column).slice(0, levelIndex),
1178
+ optionValue
1179
+ ]
1180
+
1181
+ headerSearchCascaderActivePaths[stateKey] = nextPath
1182
+
1183
+ if (!hasHeaderSearchOptionChildren(option, column)) {
1184
+ updateHeaderSearchValue(column, nextPath)
1185
+ }
1186
+ }
1187
+
609
1188
  async function handleHeaderSearch(column: BaseTableColumn<BaseTableRow>) {
610
1189
  closeHeaderSearch(column)
611
1190
  paginationState.currentPage = 1
@@ -620,7 +1199,7 @@ async function handleHeaderSearch(column: BaseTableColumn<BaseTableRow>) {
620
1199
  }
621
1200
 
622
1201
  async function handleHeaderSearchReset(column: BaseTableColumn<BaseTableRow>) {
623
- updateHeaderSearchValue(column, '')
1202
+ updateHeaderSearchValue(column, getHeaderSearchEmptyValue(column))
624
1203
  closeHeaderSearch(column)
625
1204
  paginationState.currentPage = 1
626
1205
  emitPagination()
@@ -634,7 +1213,9 @@ async function handleHeaderSearchReset(column: BaseTableColumn<BaseTableRow>) {
634
1213
  }
635
1214
 
636
1215
  function closeHeaderSearch(column: BaseTableColumn<BaseTableRow>) {
637
- headerSearchDropdownRefs.value[getHeaderSearchStateKey(column)]?.handleClose?.()
1216
+ const stateKey = getHeaderSearchStateKey(column)
1217
+ headerSearchVisible[stateKey] = false
1218
+ headerSearchSelectOpen[stateKey] = false
638
1219
  }
639
1220
 
640
1221
  function buildHeaderSearchParams() {
@@ -644,13 +1225,13 @@ function buildHeaderSearchParams() {
644
1225
  }
645
1226
 
646
1227
  const paramKey = resolveHeaderSearchParamKey(column)
647
- const value = getHeaderSearchValue(column).trim()
1228
+ const value = getHeaderSearchValue(column)
648
1229
 
649
- if (!paramKey || !value) {
1230
+ if (!paramKey || !isHeaderSearchValueFilled(value)) {
650
1231
  return result
651
1232
  }
652
1233
 
653
- result[paramKey] = value
1234
+ result[paramKey] = normalizeHeaderSearchRequestValue(column, value)
654
1235
  return result
655
1236
  }, {})
656
1237
  }
@@ -667,7 +1248,7 @@ function filterLocalRows(data: BaseTableRow[]) {
667
1248
  .filter((column) => hasHeaderSearch(column) && hasHeaderSearchValue(column))
668
1249
  .map((column) => ({
669
1250
  column,
670
- keyword: getHeaderSearchValue(column).trim().toLowerCase()
1251
+ value: getHeaderSearchValue(column)
671
1252
  }))
672
1253
 
673
1254
  if (!activeColumns.length) {
@@ -675,24 +1256,144 @@ function filterLocalRows(data: BaseTableRow[]) {
675
1256
  }
676
1257
 
677
1258
  return normalizeRows(data).filter((row) => {
678
- return activeColumns.every(({ column, keyword }) => {
1259
+ return activeColumns.every(({ column, value: searchValue }) => {
679
1260
  const columnProp = getColumnProp(column)
680
1261
  if (!columnProp) {
681
1262
  return true
682
1263
  }
683
1264
 
684
1265
  const value = getCellValue(row, column)
685
- return toSearchableText(value).includes(keyword)
1266
+ if (isHeaderSearchSelect(column)) {
1267
+ return matchesHeaderSearchSelectValue(value, searchValue)
1268
+ }
1269
+
1270
+ if (isHeaderSearchCascader(column)) {
1271
+ return matchesHeaderSearchCascaderValue(value, searchValue)
1272
+ }
1273
+
1274
+ return toSearchableText(value).includes(toHeaderSearchKeyword(searchValue))
686
1275
  })
687
1276
  })
688
1277
  }
689
1278
 
690
- function normalizeHeaderSearchValue(value: unknown) {
1279
+ function getHeaderSearchEmptyValue(column: BaseTableColumn<BaseTableRow>): BaseTableHeaderSearchValue {
1280
+ return isHeaderSearchMultiple(column) || isHeaderSearchCascader(column) ? [] : ''
1281
+ }
1282
+
1283
+ function normalizeHeaderSearchValue(
1284
+ column: BaseTableColumn<BaseTableRow>,
1285
+ value: unknown
1286
+ ): BaseTableHeaderSearchValue {
1287
+ if (isHeaderSearchCascader(column)) {
1288
+ return normalizeHeaderSearchPathValue(value)
1289
+ }
1290
+
1291
+ if (!isHeaderSearchSelect(column)) {
1292
+ if (value === undefined || value === null) {
1293
+ return ''
1294
+ }
1295
+
1296
+ return String(value)
1297
+ }
1298
+
1299
+ if (isHeaderSearchMultiple(column)) {
1300
+ const values = Array.isArray(value) ? value : [value]
1301
+ return values
1302
+ .filter((item) => isHeaderSearchValueFilled(item))
1303
+ .map((item) => normalizeHeaderSearchPrimitiveValue(item))
1304
+ }
1305
+
1306
+ if (Array.isArray(value)) {
1307
+ const firstFilledValue = value.find((item) => isHeaderSearchValueFilled(item))
1308
+ return firstFilledValue === undefined ? '' : normalizeHeaderSearchPrimitiveValue(firstFilledValue)
1309
+ }
1310
+
691
1311
  if (value === undefined || value === null) {
692
1312
  return ''
693
1313
  }
694
1314
 
695
- return String(value)
1315
+ return normalizeHeaderSearchPrimitiveValue(value)
1316
+ }
1317
+
1318
+ function normalizeHeaderSearchPrimitiveValue(value: unknown): BaseTableHeaderSearchPrimitiveValue {
1319
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
1320
+ return value
1321
+ }
1322
+
1323
+ return String(value ?? '')
1324
+ }
1325
+
1326
+ function normalizeHeaderSearchPathValue(value: unknown): BaseTableHeaderSearchPrimitiveValue[] {
1327
+ const values = Array.isArray(value) ? value : [value]
1328
+
1329
+ return values
1330
+ .filter((item) => !Array.isArray(item) && isHeaderSearchValueFilled(item))
1331
+ .map((item) => normalizeHeaderSearchPrimitiveValue(item))
1332
+ }
1333
+
1334
+ function normalizeHeaderSearchRequestValue(
1335
+ column: BaseTableColumn<BaseTableRow>,
1336
+ value: BaseTableHeaderSearchValue
1337
+ ) {
1338
+ if (Array.isArray(value)) {
1339
+ return [...value]
1340
+ }
1341
+
1342
+ if (isHeaderSearchSelect(column)) {
1343
+ return value
1344
+ }
1345
+
1346
+ return String(value).trim()
1347
+ }
1348
+
1349
+ function isHeaderSearchValueFilled(value: unknown): boolean {
1350
+ if (Array.isArray(value)) {
1351
+ return value.some((item) => isHeaderSearchValueFilled(item))
1352
+ }
1353
+
1354
+ return value !== undefined && value !== null && String(value).trim() !== ''
1355
+ }
1356
+
1357
+ function toHeaderSearchKeyword(value: BaseTableHeaderSearchValue) {
1358
+ if (Array.isArray(value)) {
1359
+ return value.map((item) => String(item).trim()).join(' ').toLowerCase()
1360
+ }
1361
+
1362
+ return String(value).trim().toLowerCase()
1363
+ }
1364
+
1365
+ function matchesHeaderSearchSelectValue(rowValue: unknown, searchValue: BaseTableHeaderSearchValue) {
1366
+ const selectedValues = toComparableValues(searchValue)
1367
+
1368
+ if (!selectedValues.length) {
1369
+ return true
1370
+ }
1371
+
1372
+ const rowValues = toComparableValues(rowValue)
1373
+ return rowValues.some((item) => selectedValues.includes(item))
1374
+ }
1375
+
1376
+ function matchesHeaderSearchCascaderValue(rowValue: unknown, searchValue: BaseTableHeaderSearchValue) {
1377
+ const selectedPath = normalizeHeaderSearchPathValue(searchValue)
1378
+
1379
+ if (!selectedPath.length) {
1380
+ return true
1381
+ }
1382
+
1383
+ const rowPath = normalizeHeaderSearchPathValue(rowValue)
1384
+
1385
+ if (!rowPath.length) {
1386
+ return false
1387
+ }
1388
+
1389
+ return selectedPath.every((item, index) => String(rowPath[index]) === String(item))
1390
+ }
1391
+
1392
+ function toComparableValues(value: unknown) {
1393
+ const values = Array.isArray(value) ? value : [value]
1394
+ return values
1395
+ .filter((item) => isHeaderSearchValueFilled(item))
1396
+ .map((item) => String(item))
696
1397
  }
697
1398
 
698
1399
  function toSearchableText(value: unknown): string {
@@ -979,11 +1680,13 @@ function findHeaderSearchColumn(key: string) {
979
1680
  })
980
1681
  }
981
1682
 
982
- async function setHeaderSearchValue(key: string, value: string, shouldReload = true) {
1683
+ async function setHeaderSearchValue(key: string, value: BaseTableHeaderSearchValue, shouldReload = true) {
983
1684
  const matchedColumn = findHeaderSearchColumn(key)
984
1685
  const stateKey = matchedColumn ? getHeaderSearchStateKey(matchedColumn) : key
985
1686
 
986
- headerSearchValues[stateKey] = normalizeHeaderSearchValue(value)
1687
+ headerSearchValues[stateKey] = matchedColumn
1688
+ ? normalizeHeaderSearchValue(matchedColumn, value)
1689
+ : value
987
1690
 
988
1691
  if (!shouldReload) {
989
1692
  return
@@ -1017,8 +1720,11 @@ async function resetHeaderSearch(key?: string) {
1017
1720
  Object.keys(headerSearchValues).forEach((stateKey) => {
1018
1721
  delete headerSearchValues[stateKey]
1019
1722
  })
1020
- Object.keys(headerSearchDropdownRefs.value).forEach((stateKey) => {
1021
- headerSearchDropdownRefs.value[stateKey]?.handleClose?.()
1723
+ Object.keys(headerSearchVisible).forEach((stateKey) => {
1724
+ headerSearchVisible[stateKey] = false
1725
+ })
1726
+ Object.keys(headerSearchSelectOpen).forEach((stateKey) => {
1727
+ headerSearchSelectOpen[stateKey] = false
1022
1728
  })
1023
1729
  }
1024
1730
 
@@ -1060,6 +1766,13 @@ defineExpose<BaseTableExpose>({
1060
1766
  min-height: 0;
1061
1767
  }
1062
1768
 
1769
+ .base-table__svg-sprite {
1770
+ position: absolute;
1771
+ width: 0;
1772
+ height: 0;
1773
+ overflow: hidden;
1774
+ }
1775
+
1063
1776
  .base-table__toolbar {
1064
1777
  display: flex;
1065
1778
  align-items: center;
@@ -1094,21 +1807,75 @@ defineExpose<BaseTableExpose>({
1094
1807
  min-width: 0;
1095
1808
  }
1096
1809
 
1810
+ .base-table__header--center.base-table__header--has-actions::before {
1811
+ content: '';
1812
+ display: block;
1813
+ width: 24px;
1814
+ flex: 0 0 24px;
1815
+ }
1816
+
1817
+ .base-table__header--center {
1818
+ justify-content: center;
1819
+ }
1820
+
1821
+ .base-table__header--right {
1822
+ justify-content: flex-end;
1823
+ }
1824
+
1825
+ .base-table__header-main {
1826
+ display: inline-flex;
1827
+ align-items: center;
1828
+ gap: 4px;
1829
+ min-width: 0;
1830
+ }
1831
+
1097
1832
  .base-table__header-label {
1098
- flex: 1 1 auto;
1099
1833
  min-width: 0;
1100
1834
  overflow: hidden;
1101
1835
  text-overflow: ellipsis;
1102
1836
  white-space: nowrap;
1103
1837
  }
1104
1838
 
1839
+ .base-table__header--left .base-table__header-main {
1840
+ flex: 1 1 auto;
1841
+ }
1842
+
1843
+ .base-table__header--center .base-table__header-main,
1844
+ .base-table__header--right .base-table__header-main {
1845
+ flex: 0 1 auto;
1846
+ }
1847
+
1848
+ .base-table__header-sort {
1849
+ display: inline-flex;
1850
+ align-items: center;
1851
+ justify-content: center;
1852
+ flex: 0 0 auto;
1853
+ width: 16px;
1854
+ height: 16px;
1855
+ color: #8c8c8c;
1856
+ }
1857
+
1858
+ .base-table__header-sort.is-active {
1859
+ color: #0938f7;
1860
+ }
1861
+
1862
+ .base-table__header-sort-icon {
1863
+ width: 15.351px;
1864
+ height: 16.031px;
1865
+ font-size: 16px;
1866
+ vertical-align: middle;
1867
+ }
1868
+
1105
1869
  .base-table__header-actions {
1106
1870
  display: flex;
1107
1871
  align-items: center;
1108
- margin-left: auto;
1109
1872
  flex-shrink: 0;
1110
1873
  }
1111
1874
 
1875
+ .base-table__header--left .base-table__header-actions {
1876
+ margin-left: auto;
1877
+ }
1878
+
1112
1879
  .base-table__header-icon {
1113
1880
  display: inline-flex;
1114
1881
  align-items: center;
@@ -1130,48 +1897,268 @@ defineExpose<BaseTableExpose>({
1130
1897
  background: rgba(64, 158, 255, 0.12);
1131
1898
  }
1132
1899
 
1133
- .base-table__header-icon-mark {
1900
+ .base-table__header-search-icon {
1901
+ width: 17.814px;
1902
+ height: 14.035px;
1903
+ font-size: 16px;
1904
+ vertical-align: middle;
1905
+ }
1906
+
1907
+ .base-table :deep(.el-table__cell.is-sortable .caret-wrapper) {
1908
+ display: none;
1909
+ }
1910
+
1911
+ .base-table__header-search-panel {
1912
+ position: fixed;
1913
+ z-index: 3000;
1914
+ display: flex;
1915
+ flex-direction: column;
1916
+ gap: 12px;
1917
+ padding: var(--el-popover-padding, 12px);
1918
+ border: 1px solid var(--el-border-color-light);
1919
+ border-radius: var(--el-popover-border-radius, 4px);
1920
+ background: var(--el-bg-color-overlay);
1921
+ box-shadow: var(--el-box-shadow-light);
1922
+ box-sizing: border-box;
1923
+ }
1924
+
1925
+ .base-table__header-search-input {
1926
+ width: 100%;
1927
+ height: 32px;
1928
+ padding: 1px 11px;
1929
+ border: none;
1930
+ border-radius: var(--el-border-radius-base);
1931
+ outline: none;
1932
+ color: var(--el-text-color-regular);
1933
+ background: var(--el-fill-color-blank);
1934
+ box-shadow: 0 0 0 1px var(--el-border-color) inset;
1935
+ box-sizing: border-box;
1936
+ font-size: var(--el-font-size-base);
1937
+ line-height: 30px;
1938
+ transition: box-shadow var(--el-transition-duration);
1939
+ }
1940
+
1941
+ .base-table__header-search-input:hover {
1942
+ box-shadow: 0 0 0 1px var(--el-border-color-hover) inset;
1943
+ }
1944
+
1945
+ .base-table__header-search-input:focus {
1946
+ box-shadow: 0 0 0 1px var(--el-color-primary) inset;
1947
+ }
1948
+
1949
+ .base-table__header-search-input::placeholder {
1950
+ color: var(--el-text-color-placeholder);
1951
+ }
1952
+
1953
+ .base-table__header-select,
1954
+ .base-table__header-cascader {
1134
1955
  position: relative;
1135
- display: inline-block;
1136
- width: 14px;
1137
- height: 14px;
1956
+ width: 100%;
1957
+ }
1958
+
1959
+ .base-table__header-select-trigger {
1960
+ display: flex;
1961
+ align-items: center;
1962
+ justify-content: space-between;
1963
+ gap: 8px;
1964
+ width: 100%;
1965
+ min-height: 32px;
1966
+ padding: 4px 11px;
1967
+ border: none;
1968
+ border-radius: var(--el-border-radius-base);
1969
+ outline: none;
1970
+ color: var(--el-text-color-regular);
1971
+ background: var(--el-fill-color-blank);
1972
+ box-shadow: 0 0 0 1px var(--el-border-color) inset;
1138
1973
  box-sizing: border-box;
1139
- border: 2px solid currentColor;
1140
- border-radius: 999px;
1974
+ cursor: pointer;
1975
+ font-size: var(--el-font-size-base);
1976
+ line-height: 24px;
1977
+ text-align: left;
1978
+ transition: box-shadow var(--el-transition-duration);
1141
1979
  }
1142
1980
 
1143
- .base-table__header-icon-mark::after {
1144
- content: '';
1145
- position: absolute;
1146
- right: -6px;
1147
- bottom: -4px;
1148
- width: 7px;
1149
- height: 2px;
1150
- border-radius: 999px;
1151
- background: currentColor;
1152
- transform: rotate(45deg);
1153
- transform-origin: center;
1981
+ .base-table__header-select-trigger:hover,
1982
+ .base-table__header-select-trigger:focus {
1983
+ box-shadow: 0 0 0 1px var(--el-color-primary) inset;
1984
+ }
1985
+
1986
+ .base-table__header-select-text {
1987
+ flex: 1 1 auto;
1988
+ min-width: 0;
1989
+ overflow: hidden;
1990
+ text-overflow: ellipsis;
1991
+ white-space: nowrap;
1992
+ }
1993
+
1994
+ .base-table__header-select-text.is-placeholder {
1995
+ color: var(--el-text-color-placeholder);
1154
1996
  }
1155
1997
 
1156
- .base-table__header-dropdown {
1998
+ .base-table__header-select-tags {
1999
+ display: flex;
2000
+ align-items: center;
2001
+ gap: 4px;
2002
+ min-width: 0;
2003
+ flex: 1 1 auto;
2004
+ overflow: hidden;
2005
+ }
2006
+
2007
+ .base-table__header-select-tag {
1157
2008
  display: inline-flex;
1158
2009
  align-items: center;
2010
+ max-width: 92px;
2011
+ min-height: 22px;
2012
+ padding: 0 6px;
2013
+ border-radius: 4px;
2014
+ background: var(--el-fill-color-light);
2015
+ color: var(--el-text-color-regular);
2016
+ box-sizing: border-box;
2017
+ font-size: 12px;
2018
+ line-height: 22px;
2019
+ overflow: hidden;
2020
+ text-overflow: ellipsis;
2021
+ white-space: nowrap;
1159
2022
  }
1160
2023
 
1161
- .base-table__header-search-panel {
2024
+ .base-table__header-select-tag--count {
2025
+ flex: 0 0 auto;
2026
+ color: var(--el-color-primary);
2027
+ }
2028
+
2029
+ .base-table__header-select-arrow {
2030
+ width: 8px;
2031
+ height: 8px;
2032
+ border-right: 1px solid currentColor;
2033
+ border-bottom: 1px solid currentColor;
2034
+ color: var(--el-text-color-placeholder);
2035
+ transform: rotate(45deg);
2036
+ transition: transform var(--el-transition-duration);
2037
+ flex: 0 0 auto;
2038
+ }
2039
+
2040
+ .base-table__header-select-arrow.is-open {
2041
+ transform: rotate(225deg);
2042
+ }
2043
+
2044
+ .base-table__header-select-options {
2045
+ max-height: 180px;
2046
+ margin-top: 6px;
2047
+ padding: 4px 0;
2048
+ border: 1px solid var(--el-border-color-light);
2049
+ border-radius: var(--el-border-radius-base);
2050
+ background: var(--el-bg-color-overlay);
2051
+ box-shadow: var(--el-box-shadow-light);
2052
+ overflow-y: auto;
2053
+ }
2054
+
2055
+ .base-table__header-select-option {
1162
2056
  display: flex;
1163
- flex-direction: column;
1164
- gap: 12px;
1165
- padding: 8px;
2057
+ align-items: center;
2058
+ width: 100%;
2059
+ min-height: 32px;
2060
+ padding: 0 12px;
2061
+ border: none;
2062
+ color: var(--el-text-color-regular);
2063
+ background: transparent;
2064
+ cursor: pointer;
2065
+ font-size: var(--el-font-size-base);
2066
+ line-height: 32px;
2067
+ text-align: left;
1166
2068
  }
1167
2069
 
1168
- :deep(.base-table__header-search-menu) {
1169
- padding: 0;
2070
+ .base-table__header-select-option-label {
2071
+ min-width: 0;
2072
+ overflow: hidden;
2073
+ text-overflow: ellipsis;
2074
+ white-space: nowrap;
1170
2075
  }
1171
2076
 
1172
- :deep(.base-table__header-search-dropdown) {
1173
- padding: 0;
1174
- border-radius: 12px;
2077
+ .base-table__header-select-option:hover {
2078
+ background: var(--el-fill-color-light);
2079
+ }
2080
+
2081
+ .base-table__header-select-option.is-selected {
2082
+ color: var(--el-color-primary);
2083
+ font-weight: 700;
2084
+ }
2085
+
2086
+ .base-table__header-select-option:disabled {
2087
+ color: var(--el-text-color-placeholder);
2088
+ cursor: not-allowed;
2089
+ }
2090
+
2091
+ .base-table__header-cascader-panel {
2092
+ display: flex;
2093
+ max-width: calc(100vw - 24px);
2094
+ min-height: 180px;
2095
+ max-height: 260px;
2096
+ margin-top: 6px;
2097
+ border: 1px solid var(--el-border-color-light);
2098
+ border-radius: var(--el-border-radius-base);
2099
+ background: var(--el-bg-color-overlay);
2100
+ box-shadow: var(--el-box-shadow-light);
2101
+ overflow: auto;
2102
+ }
2103
+
2104
+ .base-table__header-cascader-menu {
2105
+ min-width: 132px;
2106
+ padding: 4px 0;
2107
+ border-right: 1px solid var(--el-border-color-lighter);
2108
+ box-sizing: border-box;
2109
+ }
2110
+
2111
+ .base-table__header-cascader-menu:last-child {
2112
+ border-right: none;
2113
+ }
2114
+
2115
+ .base-table__header-cascader-option {
2116
+ display: flex;
2117
+ align-items: center;
2118
+ justify-content: space-between;
2119
+ gap: 8px;
2120
+ width: 100%;
2121
+ min-height: 32px;
2122
+ padding: 0 10px 0 12px;
2123
+ border: none;
2124
+ color: var(--el-text-color-regular);
2125
+ background: transparent;
2126
+ cursor: pointer;
2127
+ font-size: var(--el-font-size-base);
2128
+ line-height: 32px;
2129
+ text-align: left;
2130
+ }
2131
+
2132
+ .base-table__header-cascader-option:hover,
2133
+ .base-table__header-cascader-option.is-active {
2134
+ background: var(--el-fill-color-light);
2135
+ }
2136
+
2137
+ .base-table__header-cascader-option.is-selected {
2138
+ color: var(--el-color-primary);
2139
+ font-weight: 700;
2140
+ }
2141
+
2142
+ .base-table__header-cascader-option:disabled {
2143
+ color: var(--el-text-color-placeholder);
2144
+ cursor: not-allowed;
2145
+ }
2146
+
2147
+ .base-table__header-cascader-label {
2148
+ min-width: 0;
2149
+ overflow: hidden;
2150
+ text-overflow: ellipsis;
2151
+ white-space: nowrap;
2152
+ }
2153
+
2154
+ .base-table__header-cascader-arrow {
2155
+ width: 7px;
2156
+ height: 7px;
2157
+ border-right: 1px solid currentColor;
2158
+ border-bottom: 1px solid currentColor;
2159
+ color: var(--el-text-color-placeholder);
2160
+ transform: rotate(-45deg);
2161
+ flex: 0 0 auto;
1175
2162
  }
1176
2163
 
1177
2164
  .base-table__header-search-footer {