cd-vue-filter 2.2.5 → 2.2.6

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.
@@ -1,412 +0,0 @@
1
- <template>
2
- <div class="cd-filter-bar">
3
- <!-- 内联筛选组件区域 -->
4
- <div class="inline-filter-toolbar" v-if="filterCount > 0">
5
- <FilterComponent
6
- v-for="(cond, idx) in inlineHeaderConditions"
7
- :key="idx"
8
- :field-options="fieldOptions"
9
- :size="size"
10
- :filter-condition="inlineHeaderConditions[idx]"
11
- :select-options="selectOptions"
12
- />
13
- </div>
14
- <!-- 我的方案 - Radio Group 模式(当筛选长度为0时) -->
15
- <div class="plan-radio-group" v-if="filterCount === 0 && planFilterOptions.length > 0">
16
- <span class="plan-label">我的方案:</span>
17
- <t-radio-group v-model="selectedPlanValue" :size="size" @change="handlePlanRadioChange">
18
- <t-radio-button
19
- v-for="plan in planFilterOptions"
20
- :key="plan.value"
21
- :value="plan.value"
22
- :disabled="plan.disabled"
23
- >
24
- {{ plan.content }}
25
- </t-radio-button>
26
- </t-radio-group>
27
- <!-- 高级筛选图标按钮 -->
28
- <t-button theme="default" variant="outline" :size="size" @click="openAdvancedFilter" title="高级筛选">
29
- <template #icon>
30
- <t-icon name="filter" />
31
- </template>
32
- </t-button>
33
- </div>
34
- <!-- 操作按钮区域 -->
35
- <div class="filter-action-buttons">
36
- <!-- 搜索按钮 -->
37
- <t-button theme="primary" :size="size" @click="emitSearch" title="搜索">
38
- <template #icon>
39
- <t-icon name="search" />
40
- </template>
41
- </t-button>
42
- <!-- 重置按钮 -->
43
- <t-button theme="primary" :size="size" @click="handleReset" title="重置">
44
- <template #icon>
45
- <t-icon name="refresh" />
46
- </template>
47
- </t-button>
48
- <!-- 高级筛选按钮(当筛选长度大于0时) -->
49
- <t-button v-if="filterCount > 0" theme="primary" :size="size" @click="openAdvancedFilter">
50
- <template #icon>
51
- <t-icon name="filter" />
52
- </template>
53
- </t-button>
54
- <!-- 我的方案下拉菜单(当筛选长度大于0时) -->
55
- <t-dropdown v-if="filterCount > 0" :options="dropdownOptions" @click="handlePlanSelect">
56
- <t-button theme="primary" :size="size" class="margin-reset">
57
- 我的方案
58
- <template #suffix><t-icon name="chevron-down" /></template>
59
- </t-button>
60
- </t-dropdown>
61
- </div>
62
- <!-- 高级筛选对话框 -->
63
- <cd-filter
64
- v-model:visible="showFilterDialog"
65
- :field-options="fieldOptions"
66
- :select-options="selectOptions"
67
- :plan-filter-options="planFilterOptions"
68
- :filter-cards="innerFilterCards"
69
- :top-op="innerTopOp"
70
- :size="size"
71
- :person-tabs="personTabs"
72
- :person-organizations="personOrganizations"
73
- :visible-columns="visibleColumns"
74
- width="1000"
75
- @confirm="handleConfirm"
76
- @reset="handleReset"
77
- @delete-plan="(plan) => emit('delete-plan', plan)"
78
- @copy-plan="(plan) => emit('copy-plan', plan)"
79
- @set-default-plan="(plan) => emit('set-default-plan', plan)"
80
- @update-plan="(payload) => emit('update-plan', payload)"
81
- @load-users="(payload) => emit('load-users', payload)"
82
- @search="(payload) => emit('search-persons', payload)"
83
- @dept-click="(payload) => emit('dept-click', payload)"
84
- @column-change="(columns) => emit('column-change', columns)"
85
- />
86
- <!-- 保存筛选方案弹窗 -->
87
- <t-dialog
88
- :header="planTitle"
89
- :visible="savePlanDialogVisible"
90
- width="600px"
91
- @close="closeSavePlanDialog"
92
- >
93
- <template #body>
94
- <div class="save-plan-content">
95
- <t-form :data="newPlanData" label-width="100px">
96
- <t-form-item label="方案名称" required>
97
- <t-input v-model="newPlanData.name" placeholder="请输入筛选方案名称" />
98
- </t-form-item>
99
- </t-form>
100
- </div>
101
- </template>
102
- <template #footer>
103
- <t-button theme="default" @click="closeSavePlanDialog">取消</t-button>
104
- <t-button theme="primary" @click="emitSavePlan">确定</t-button>
105
- </template>
106
- </t-dialog>
107
- </div>
108
- </template>
109
- <script setup>
110
- import { ref, computed, watch } from 'vue'
111
- import FilterComponent from './FilterComponent.vue'
112
- import CdFilter from './cd-filter.vue'
113
- // Props
114
- const props = defineProps({
115
- fieldOptions: { type: Array, required: true },
116
- filterCount: { type: Number, default: 2 },
117
- selectOptions: { type: Array, default: () => [] },
118
- filterCards: {
119
- type: Array,
120
- default: () => [
121
- {
122
- id: 1,
123
- connector: 'and',
124
- conditions: [
125
- { field: '', operator: 'eq', value: '' },
126
- { field: '', operator: 'eq', value: '' }
127
- ]
128
- }
129
- ]
130
- },
131
- topOp: { type: String, default: 'and' },
132
- planFilterOptions: { type: Array, default: () => [] },
133
- size: { type: String, default: 'small' },
134
- width: { type: String, default: '1000px' },
135
- personTabs: { type: Array, default: () => [] },
136
- personOrganizations: { type: Array, default: () => [] },
137
- visibleColumns: { type: Array, default: () => [] }
138
- })
139
- const emit = defineEmits([
140
- 'search',
141
- 'reset',
142
- 'save-plan',
143
- 'delete-plan',
144
- 'copy-plan',
145
- 'set-default-plan',
146
- 'update-plan',
147
- 'confirm',
148
- 'load-users',
149
- 'search-persons',
150
- 'dept-click',
151
- 'column-change'
152
- ])
153
- // 内联条件根据 filterComCount 派生
154
- const inlineHeaderConditions = ref([])
155
- const makeBlankCondition = () => ({ field: '', operator: 'eq', value: '' })
156
- const initInlineHeaderConditions = () => {
157
- // 如果 filterCount 为 0,不初始化内联条件
158
- if (props.filterCount === 0) {
159
- inlineHeaderConditions.value = []
160
- return
161
- }
162
-
163
- const base = props.filterCards?.[0]?.conditions || []
164
- inlineHeaderConditions.value = Array.from({ length: props.filterCount }, (_, i) => {
165
- return base[i] ? JSON.parse(JSON.stringify(base[i])) : makeBlankCondition()
166
- })
167
- }
168
- initInlineHeaderConditions()
169
- const clearInlineHeaderConditions = () => {
170
- // 如果 filterCount 为 0,清空即可
171
- if (props.filterCount === 0) {
172
- inlineHeaderConditions.value = []
173
- return
174
- }
175
- inlineHeaderConditions.value = Array.from({ length: props.filterCount }, () => makeBlankCondition())
176
- }
177
- const planTitle = ref('保存筛选方案')
178
- // 高级筛选内部状态
179
- const showFilterDialog = ref(false)
180
- const innerTopOp = ref(props.topOp)
181
- const innerFilterCards = ref(JSON.parse(JSON.stringify(props.filterCards)))
182
- watch(() => props.topOp, (v) => (innerTopOp.value = v))
183
- watch(() => props.filterCards, (v) => (innerFilterCards.value = JSON.parse(JSON.stringify(v))))
184
- // 方案相关
185
- const selectedPlanFilter = ref(null)
186
- const selectedPlanValue = ref('') // Radio Group 选中的值
187
-
188
- const dropdownOptions = computed(() => {
189
- return props.planFilterOptions && props.planFilterOptions.length > 0
190
- ? props.planFilterOptions
191
- : [{ content: '暂无方案', value: 'empty', disabled: true }]
192
- })
193
-
194
- // Radio Group 方案选择处理
195
- const handlePlanRadioChange = (value) => {
196
- const selectedPlan = props.planFilterOptions.find(p => p.value === value)
197
- if (selectedPlan) {
198
- handlePlanSelect(selectedPlan)
199
- }
200
- }
201
-
202
- // 通用方案选择处理(适用于 dropdown 和 radio)
203
- const handlePlanSelect = (option) => {
204
- if (!option.precepts || option.precepts.length === 0) return
205
- // 更新选中的方案
206
- selectedPlanFilter.value = option
207
- selectedPlanValue.value = option.value // 同步更新 radio 选中值
208
-
209
- // 更新内部筛选卡片和顶层操作符
210
- innerFilterCards.value = JSON.parse(JSON.stringify(option.precepts))
211
- innerTopOp.value = option.sqlConnectType
212
-
213
- // 同步更新内联条件显示(仅当 filterCount > 0 时)
214
- if (props.filterCount > 0) {
215
- const firstCard = option.precepts[0]
216
- if (firstCard && firstCard.conditions) {
217
- inlineHeaderConditions.value = Array.from({ length: props.filterCount }, (_, i) => {
218
- return firstCard.conditions[i]
219
- ? JSON.parse(JSON.stringify(firstCard.conditions[i]))
220
- : makeBlankCondition()
221
- })
222
- }
223
- }
224
-
225
- const cleaned = cleanFilterCards(option.precepts)
226
- const transformed = transformConditions(cleaned, option.sqlConnectType)
227
- emit('confirm', transformed)
228
-
229
- // 如果方案包含列配置,通知父组件(只传递显示的列)
230
- if (option.columns && option.columns.length > 0) {
231
- const visibleColumns = option.columns.filter(col => col.show !== false)
232
- emit('column-change', visibleColumns)
233
- }
234
- }
235
- const emitSearch = () => {
236
- // 当 filterCount 为 0 时,不处理内联条件,直接发送空筛选
237
- if (props.filterCount === 0) {
238
- emit('search', { conditions: { and: [] } })
239
- return
240
- }
241
-
242
- const cleaned = cleanFilterCards([
243
- { id: 1, connector: innerTopOp.value, conditions: inlineHeaderConditions.value }
244
- ])
245
- const transformed = transformConditions(cleaned, 'and')
246
- emit('search', transformed)
247
- }
248
- // 高级筛选弹窗开关
249
- const openAdvancedFilter = () => {
250
- showFilterDialog.value = true
251
- }
252
- const closeAdvancedFilter = () => {
253
- showFilterDialog.value = false
254
- }
255
- // 清理筛选卡片,保证字段有效
256
- const cleanFilterCards = (cards) => {
257
- return cards
258
- .map((card) => {
259
- const validConditions = card.conditions
260
- .filter((condition) => condition.field && condition.field.trim() !== '')
261
- .map((condition) => {
262
- let cleanValue = ''
263
- if (condition.value !== null && condition.value !== undefined) {
264
- if (Array.isArray(condition.value)) {
265
- cleanValue = condition.value.join('/')
266
- } else {
267
- cleanValue = String(condition.value)
268
- }
269
- }
270
- return {
271
- field: condition.field.trim(),
272
- operator: condition.operator || 'eq',
273
- value: cleanValue
274
- }
275
- })
276
- return {
277
- ...card,
278
- connector: card.connector || 'and',
279
- conditions: validConditions.length > 0 ? validConditions : [{ field: '', operator: 'eq', value: '' }]
280
- }
281
- })
282
- .filter((card) => card.conditions.length > 0 && card.conditions[0].field !== '')
283
- }
284
- // 构建新的查询条件格式
285
- function transformConditions(conditions, type) {
286
- if (type !== 'and' && type !== 'or') {
287
- throw new Error('type参数必须是"and"或"or"')
288
- }
289
- function transformSingleCondition(condition) {
290
- return {
291
- [condition.field]: {
292
- [condition.operator]: condition.value
293
- }
294
- }
295
- }
296
- function transformConditionGroup(group) {
297
- if (group.conditions.length === 1) {
298
- return transformSingleCondition(group.conditions[0])
299
- }
300
- return {
301
- [group.connector]: group.conditions.map(transformSingleCondition)
302
- }
303
- }
304
- const result = {
305
- conditions: {
306
- [type]: conditions.map(transformConditionGroup)
307
- }
308
- }
309
- return result
310
- }
311
- // 保存方案对话框
312
- const openSavePlanDialog = (plan) => {
313
- if (selectedPlanFilter.value) {
314
- planTitle.value = '更新筛选方案'
315
- newPlanData.value.name = selectedPlanFilter.value.content || ''
316
- } else {
317
- planTitle.value = '保存筛选方案'
318
- newPlanData.value.name = ''
319
- selectedPlanFilter.value = null
320
- }
321
- savePlanDialogVisible.value = true
322
- }
323
- const closeSavePlanDialog = () => {
324
- savePlanDialogVisible.value = false
325
- }
326
- // 重置、保存
327
- const handleReset = () => {
328
- clearInlineHeaderConditions()
329
- selectedPlanFilter.value = null
330
- selectedPlanValue.value = '' // 清空 radio 选中值
331
- innerFilterCards.value = [
332
- {
333
- id: 1,
334
- connector: 'and',
335
- conditions: [
336
- { field: '', operator: 'eq', value: '' },
337
- { field: '', operator: 'eq', value: '' }
338
- ]
339
- }
340
- ]
341
- innerTopOp.value = 'and'
342
- emit('reset')
343
- emitSearch()
344
- }
345
- const emitSavePlan = () => {
346
- if (selectedPlanFilter.value) {
347
- const cleaned = cleanFilterCards(innerFilterCards.value)
348
- emit('update-plan', {
349
- name: newPlanData.value.name,
350
- precepts: cleaned,
351
- topOp: innerTopOp.value,
352
- plan: selectedPlanFilter.value,
353
- columns: props.visibleColumns || [] // 包含当前列配置
354
- })
355
- } else {
356
- const cleaned = cleanFilterCards(innerFilterCards.value)
357
- emit('save-plan', {
358
- name: newPlanData.value.name,
359
- precepts: cleaned,
360
- topOp: innerTopOp.value,
361
- plan: selectedPlanFilter.value,
362
- columns: props.visibleColumns || [] // 包含当前列配置
363
- })
364
- }
365
- closeSavePlanDialog()
366
- }
367
- const handleConfirm = (condition) => {
368
- const transformed = transformConditions(condition.filterCards, condition.type1)
369
- emit('confirm', transformed)
370
- // 仅在 filterCount > 0 时清空内联条件
371
- if (props.filterCount > 0) {
372
- clearInlineHeaderConditions()
373
- }
374
- closeAdvancedFilter()
375
- }
376
- // 保存方案弹窗数据
377
- const savePlanDialogVisible = ref(false)
378
- const newPlanData = ref({ name: '' })
379
- </script>
380
- <style scoped lang="scss">
381
- .cd-filter-bar {
382
- display: flex;
383
- align-items: center;
384
- gap: 8px;
385
- }
386
- .inline-filter-toolbar {
387
- display: inline-flex;
388
- align-items: center;
389
- gap: 8px;
390
- }
391
- .plan-radio-group {
392
- display: flex;
393
- align-items: center;
394
- gap: 12px;
395
- flex: 1;
396
-
397
- .plan-label {
398
- font-size: 14px;
399
- font-weight: 500;
400
- color: #333;
401
- white-space: nowrap;
402
- }
403
- }
404
- .filter-action-buttons {
405
- display: flex;
406
- gap: 8px;
407
- align-items: center;
408
- }
409
- .margin-reset {
410
- margin: 0;
411
- }
412
- </style>