@lx-frontend/wrap-element-ui 1.0.15 → 1.0.16-beta.1

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.
@@ -0,0 +1,131 @@
1
+ import dayjs from 'dayjs';
2
+
3
+ // 月份配置 - 月份对应的天数
4
+ const monthConfig = [
5
+ { month: 1, day: 31 },
6
+ { month: 2, day: 29 },
7
+ { month: 3, day: 31 },
8
+ { month: 4, day: 30 },
9
+ { month: 5, day: 31 },
10
+ { month: 6, day: 30 },
11
+ { month: 7, day: 31 },
12
+ { month: 8, day: 31 },
13
+ { month: 9, day: 30 },
14
+ { month: 10, day: 31 },
15
+ { month: 11, day: 30 },
16
+ { month: 12, day: 31 },
17
+ ];
18
+
19
+ /**
20
+ * 组装极联选择组件默认选项
21
+ * example: [
22
+ * {
23
+ * value: '1',
24
+ * label: '1月',
25
+ * children: [
26
+ * { value: '1', label: '1日' },
27
+ * ... // 1月份的每一天
28
+ * ]
29
+ * },
30
+ * ... // 其他月份的选项
31
+ * ]
32
+ */
33
+ export function getMonthDayPickerDefaultOptions() {
34
+ return monthConfig.map(
35
+ (item) => ({
36
+ value: item.month.toString(),
37
+ label: `${ item.month }月`,
38
+ children: Array.from(
39
+ { length: item.day },
40
+ (_, i) => (
41
+ { value: `${ i + 1 }`, label: `${ i + 1 }日` }
42
+ )
43
+ )
44
+ })
45
+ );
46
+ }
47
+
48
+ /**
49
+ * 组装日期范围级联选择组件禁用选项
50
+ * @param date 日期 - 级联选择组件值
51
+ * @param isStartDate 区分传递的date是 开始日期 / 结束日期
52
+ * @param digits 时间范围 n个月
53
+ */
54
+ export function disableOutOfRangeOptions({
55
+ date = [],
56
+ isStartDate = false,
57
+ digits = 3
58
+ }: { date: string[], isStartDate?: boolean, digits: number }) {
59
+ // 写死一个闰年年份 仅用于比较时间
60
+ // 如果非闰年 dayjs('year-02-29') 会输出 year-3-1 导致日期范围选择器出错
61
+ const year = 2024;
62
+ const baseMonthDay = 30;
63
+ const options = getMonthDayPickerDefaultOptions();
64
+
65
+ return options.map(
66
+ (month) => {
67
+ const children = month.children.map(
68
+ (day) => {
69
+ // 组装日期 - 当前这个选项对应的完整日期
70
+ const currentDate = dayjs([year, month.value, day.value].join('-'));
71
+
72
+ // 是否需要禁用当前日期
73
+ let disabled = false;
74
+
75
+ // 如果有传入日期,则判断是否在禁用日期范围内
76
+ if (date.length > 0) {
77
+ // 组装日期 - 传入日期对应的完整日期
78
+ const baseDate = dayjs([year, ...date].join('-'));
79
+
80
+ // 如果是开始时间,则结束时间范围为 baseDate + digits
81
+ // 如果是结束时间,则开始时间范围为 baseDate - digits
82
+ if (isStartDate) {
83
+ const maxDate = baseDate.add(digits * baseMonthDay - 1, 'day');
84
+ // 推算时间范围超过12月31号,结束时间可选年初时间
85
+ if (maxDate.year() > year) {
86
+ disabled = currentDate.isAfter(maxDate.subtract(366, 'day'), 'day') && currentDate.isBefore(baseDate, 'day');
87
+ } else {
88
+ disabled = currentDate.isAfter(maxDate, 'day') || currentDate.isBefore(baseDate, 'day');
89
+ }
90
+ } else {
91
+ const minDate = baseDate.subtract(digits * baseMonthDay - 1, 'day');
92
+ // 推算时间范围在1月1号前,开始时间可选年末时间
93
+ if (minDate.year() < year) {
94
+ disabled = currentDate.isBefore(minDate.add(366, 'day'), 'day') && currentDate.isAfter(baseDate, 'day');
95
+ } else {
96
+ disabled = currentDate.isBefore(minDate, 'day') || currentDate.isAfter(baseDate, 'day');
97
+ }
98
+ }
99
+ }
100
+
101
+ return {
102
+ ...day,
103
+ disabled
104
+ };
105
+ }
106
+ );
107
+
108
+ // 如果当前月份每天都是禁用日期,则整个月份禁用
109
+ const disabled = children.every((day) => day.disabled);
110
+
111
+ return {
112
+ ...month,
113
+ disabled,
114
+ children
115
+ };
116
+ }
117
+ );
118
+ }
119
+
120
+ /**
121
+ * 格式化月日级联选择组件值 MM-dd
122
+ * @param date 日期 - 级联选择组件值
123
+ * @returns 格式化后的日期字符串
124
+ */
125
+ export function formatMonthDayPickerValue(date: string[]) {
126
+ return date
127
+ .map(
128
+ (str) => str.padStart(2, '0')
129
+ )
130
+ .join('-');
131
+ }
@@ -0,0 +1,113 @@
1
+ <!-- 月/日选择器 -->
2
+ <template>
3
+ <div class="editable-table-sort-filter__sort">
4
+ <div class="editable-table-sort-filter__search-title">
5
+ {{ config.label || '筛选' }}
6
+ </div>
7
+ <div class="editable-table-sort-filter__date-picker-content">
8
+ <el-cascader
9
+ clearable
10
+ :value="startDate"
11
+ separator=""
12
+ placeholder="开始日期"
13
+ popper-class="month-day-picker"
14
+ :options="startDateOptions"
15
+ @change="value => handleDateChange(config.prop[0], value)"
16
+ />
17
+ <el-cascader
18
+ clearable
19
+ :value="endDate"
20
+ separator=""
21
+ placeholder="结束日期"
22
+ popper-class="month-day-picker"
23
+ :options="endDateOptions"
24
+ @change="value => handleDateChange(config.prop[1], value)"
25
+ />
26
+ </div>
27
+ </div>
28
+ </template>
29
+ <script setup lang="ts">
30
+ import {
31
+ disableOutOfRangeOptions, formatMonthDayPickerValue,
32
+ getMonthDayPickerDefaultOptions
33
+ } from './BizMonthDayPicker.helper';
34
+ import { computed, toRefs } from 'vue';
35
+ import { IFilterMonthDayPicker } from '../../types';
36
+
37
+ const props = defineProps<{
38
+ config: IFilterMonthDayPicker
39
+ tempFilteredValue: Record<string, string>
40
+ }>()
41
+ const { config, tempFilteredValue } = toRefs(props)
42
+
43
+ const emit = defineEmits<{
44
+ (e: 'update:tempFilteredValue', key: string, value: string): void
45
+ }>()
46
+
47
+ const formatDate = (date: string) => {
48
+ if (date) {
49
+ return date
50
+ .split('-')
51
+ // 01 -> 1
52
+ .map(
53
+ (v) => String(Number(v))
54
+ )
55
+ }
56
+
57
+ return []
58
+ }
59
+
60
+ const startDate = computed(() => {
61
+ const { prop } = config.value;
62
+ return formatDate(tempFilteredValue.value[prop[0]]);
63
+ })
64
+
65
+ const endDate = computed(() => {
66
+ const { prop } = config.value;
67
+ return formatDate(tempFilteredValue.value[prop[1]]);
68
+ })
69
+
70
+ // 开始日期范围限制
71
+ const startDateOptions = computed(
72
+ () => {
73
+ if (endDate.value.length === 0) {
74
+ return getMonthDayPickerDefaultOptions();
75
+ }
76
+
77
+ return disableOutOfRangeOptions({
78
+ date: endDate.value,
79
+ isStartDate: false,
80
+ digits: config.value.limit
81
+ });
82
+ }
83
+ );
84
+
85
+ // 结束日期范围限制
86
+ const endDateOptions = computed(
87
+ () => {
88
+ if (startDate.value.length === 0) {
89
+ return getMonthDayPickerDefaultOptions();
90
+ }
91
+
92
+ return disableOutOfRangeOptions({
93
+ date: startDate.value,
94
+ isStartDate: true,
95
+ digits: config.value.limit
96
+ });
97
+ }
98
+ );
99
+
100
+ /**
101
+ * 选择时间更改统一回调
102
+ */
103
+ const handleDateChange = (key: string, value: string[]) => {
104
+ const currentValue = value.length === 0
105
+ ? ''
106
+ : formatMonthDayPickerValue(value)
107
+ emit('update:tempFilteredValue', key, currentValue);
108
+ }
109
+ </script>
110
+
111
+ <style scoped lang="less">
112
+
113
+ </style>
@@ -0,0 +1,44 @@
1
+ <!-- 单选框 -->
2
+ <template>
3
+ <div class="editable-table-sort-filter__filter">
4
+ <div class="editable-table-sort-filter__filter-title">
5
+ {{ config.label || '筛选' }}
6
+ </div>
7
+ <el-radio-group
8
+ style="display: flex;flex-direction: column;gap: 6px;"
9
+ :value="tempFilteredValue[config.prop]"
10
+ @input="val => emit('update:tempFilteredValue', config.prop, val)"
11
+ >
12
+ <el-radio
13
+ v-for="item in config.options"
14
+ :key="item.value"
15
+ :label="item.value"
16
+ :title="item.text"
17
+ >
18
+ <slot
19
+ name="filter-item"
20
+ v-bind="item"
21
+ >
22
+ {{ item.text }}
23
+ </slot>
24
+ </el-radio>
25
+ </el-radio-group>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ import { IFilterSelect } from '../../types';
31
+
32
+ defineProps<{
33
+ config: IFilterSelect
34
+ tempFilteredValue: Record<string, string>
35
+ }>()
36
+
37
+ const emit = defineEmits<{
38
+ (e: 'update:tempFilteredValue', key: string, value: string): void
39
+ }>()
40
+ </script>
41
+
42
+ <style scoped lang="less">
43
+
44
+ </style>
@@ -5,7 +5,7 @@
5
5
  :key="item.prop"
6
6
  >
7
7
  <div class="editable-table-sort-filter__sort-title">
8
- {{ item.label }}
8
+ {{ item.label || '排序' }}
9
9
  </div>
10
10
  <div class="editable-table-sort-filter__sort-btns">
11
11
  <el-button
@@ -47,4 +47,4 @@ const sortConfigs = computed(() => {
47
47
  if (Array.isArray(props.column._sortable)) return props.column._sortable
48
48
  return [{ ...props.column, label: '排序' }]
49
49
  })
50
- </script>
50
+ </script>
@@ -27,80 +27,34 @@
27
27
  {{ column.label }}
28
28
  </div>
29
29
 
30
- <Sort
31
- v-if="column.isColumnSortable"
32
- :column="column"
33
- :temp-sort-prop="tempSortProp"
34
- :temp-sort-type="tempSortType"
35
- @update:sort="(type, prop) => emit('update:sort', type, prop)"
36
- />
37
-
38
- <Search
39
- v-if="!!column.search"
40
- :column="column"
41
- :temp-search-value="tempSearchValue"
42
- @update:tempSearchValue="(key, val) => emit('update:tempSearchValue', key, val)"
43
- >
44
- <template
45
- v-for="searchOption in (Array.isArray(column.search) ? column.search : []).filter(v => v.type === 'slot')"
46
- #[searchOption.slotName]="rest"
47
- >
48
- <slot
49
- :name="searchOption.slotName"
50
- v-bind="rest"
51
- />
52
- </template>
53
- </Search>
30
+ <div class="editable-table-sort-filter__item">
31
+ <BizSortFilter
32
+ v-if="column.isColumnSortable"
33
+ :column="column"
34
+ :temp-sort-prop="tempSortProp"
35
+ :temp-sort-type="tempSortType"
36
+ @update:sort="(type, prop) => emit('update:sort', type, prop)"
37
+ />
38
+ </div>
54
39
 
55
- <div
56
- v-if="column.doubleDatePicker"
57
- class="editable-table-sort-filter__sort"
58
- >
59
- <div class="editable-table-sort-filter__search-title">
60
- {{ column.doubleDatePicker.label }}
61
- </div>
40
+ <template v-if="column.filters">
62
41
  <div
63
- class="editable-table-sort-filter__date-picker-content"
64
- style="display: flex;flex-direction: column;gap: 12px;"
42
+ v-for="(filterItem, index) in column.filters"
43
+ :key="index"
44
+ class="editable-table-sort-filter__item editable-table__filter-group__filter"
65
45
  >
66
- <el-date-picker
67
- @input="val => emit('update:tempSearchValue', column.doubleDatePicker.props[0], val || '')"
68
- :value="tempSearchValue[column.doubleDatePicker.props[0]]"
69
- value-format="yyyy-MM-dd"
70
- format="yyyy-MM-dd"
71
- type="date"
72
- size="small"
73
- placeholder="开始日期"
74
- />
75
- <el-date-picker
76
- @input="val => emit('update:tempSearchValue', column.doubleDatePicker.props[1], val || '')"
77
- :value="tempSearchValue[column.doubleDatePicker.props[1]]"
78
- value-format="yyyy-MM-dd"
79
- format="yyyy-MM-dd"
80
- size="small"
81
- type="date"
82
- placeholder="结束日期"
46
+ <component
47
+ :is="componentMap[filterItem.type]"
48
+ :config="filterItem"
49
+ :temp-filtered-value="tempFilteredValue"
50
+ @update:tempFilteredValue="onUpdate"
83
51
  />
84
52
  </div>
85
- </div>
86
-
87
- <BizFilter
88
- v-if="column.filters && ((Array.isArray(column.filters) ? column.filters : column.filters.options).length > 0)"
89
- :column="column"
90
- :temp-filtered-value="tempFilteredValue"
91
- @update:tempFilteredValue="(key, val) => emit('update:tempFilteredValue', key, val)"
92
- >
93
- <template #filter-item="item">
94
- <slot
95
- name="filter-item"
96
- v-bind="item"
97
- />
98
- </template>
99
- </BizFilter>
53
+ </template>
100
54
 
101
55
  <div
102
56
  v-if="column.summary"
103
- class="editable-table-sort-filter__filter"
57
+ class="editable-table-sort-filter__item editable-table-sort-filter__filter"
104
58
  >
105
59
  <div class="editable-table-sort-filter__filter-title">
106
60
  统计
@@ -144,12 +98,16 @@
144
98
  </template>
145
99
 
146
100
  <script setup lang="ts">
147
- import Search from './search.vue'
148
- import Sort from './sort.vue'
149
- import BizFilter from './bizFilter.vue'
150
-
151
101
  import { ref } from 'vue'
152
- import { IColumnConfig } from '../../types'
102
+
103
+ import BizCheckboxFilter from './BizCheckboxFilter.vue';
104
+ import BizColorRadioFilter from './BizColorRadioFilter.vue';
105
+ import BizDoubleDatePickerFilter from './BizDoubleDatePickerFilter.vue';
106
+ import BizInputFilter from './BizInputFilter.vue';
107
+ import BizMonthDayPicker from './BizMonthDayPicker.vue';
108
+ import BizSortFilter from './BizSortFilter.vue';
109
+ import BizRadioFilter from "./BizRadioFilter.vue";
110
+ import { FilterItem, IColumnConfig } from '../../types'
153
111
 
154
112
  defineProps<{
155
113
  headActive: boolean
@@ -157,12 +115,10 @@ defineProps<{
157
115
  tempSummaryList: string[]
158
116
  tempSortType: 'ascending' | 'descending' | ''
159
117
  tempSortProp: string
160
- tempSearchValue: Record<string, string>
161
118
  tempFilteredValue: Record<string, string | number | number[] | string[]>
162
119
  }>()
163
120
 
164
121
  const emit = defineEmits<{
165
- (e: 'update:tempSearchValue', key: string, value: string): void
166
122
  (e: 'update:tempFilteredValue', key: string, value: string): void
167
123
  (e: 'update:tempSummaryList', value: string[]): void
168
124
  (e: 'update:sort', type: 'ascending' | 'descending', prop: string): void
@@ -171,8 +127,29 @@ const emit = defineEmits<{
171
127
  (e: 'confirm'): void
172
128
  }>()
173
129
 
130
+ // 把 filterItem.type 映射到组件
131
+ const componentMap: Record<FilterItem['type'], any> = {
132
+ /** 输入框 */
133
+ input: BizInputFilter,
134
+ /** 日期范围 */
135
+ doubleDatePicker: BizDoubleDatePickerFilter,
136
+ /** 单选框 */
137
+ radio: BizRadioFilter,
138
+ /** 复选框 */
139
+ checkbox: BizCheckboxFilter,
140
+ /** 月日选择器 */
141
+ monthDayPicker: BizMonthDayPicker,
142
+ /** 颜色选择器 */
143
+ colorRadio: BizColorRadioFilter,
144
+ }
145
+
174
146
  const popoverRef = ref(null as any)
175
147
 
148
+ // 统一的事件派发
149
+ function onUpdate(key: string, val: any) {
150
+ emit('update:tempFilteredValue', key, val)
151
+ }
152
+
176
153
  defineExpose({
177
154
  close: () => {
178
155
  popoverRef.value?.doClose()
@@ -387,9 +387,28 @@
387
387
  border-bottom: 1px solid #d6dbe3;
388
388
  }
389
389
 
390
+ &__item {
391
+ margin-top: 14px;
392
+ }
393
+
394
+ &__date-picker-content {
395
+ display: flex;
396
+ flex-direction: column;
397
+ gap: 12px;
398
+
399
+ .el-date-editor.el-input,
400
+ .el-date-editor.el-input__inner {
401
+ width: 100%;
402
+ }
403
+ }
404
+
390
405
  &__filter,
391
406
  &__search {
392
- padding: 14px;
407
+ padding: 0 14px;
408
+
409
+ .el-select {
410
+ width: 100%;
411
+ }
393
412
 
394
413
  .el-radio {
395
414
  width: 100%;
@@ -422,7 +441,6 @@
422
441
  }
423
442
 
424
443
  &__filter {
425
- padding-top: 0;
426
444
 
427
445
  .el-radio-group,
428
446
  .el-checkbox-group {
@@ -433,10 +451,9 @@
433
451
  }
434
452
 
435
453
  &__sort {
436
- padding: 14px;
454
+ padding: 0 14px;
437
455
  display: flex;
438
456
  flex-direction: column;
439
- gap: 8px;
440
457
  }
441
458
 
442
459
  &__filter-title,
@@ -454,22 +471,19 @@
454
471
  height: 32px;
455
472
  }
456
473
  }
474
+
457
475
  &__date-range {
458
476
  display: flex;
459
477
  flex-direction: column;
460
478
  gap: 12px;
461
479
 
462
- .el-date-editor.el-input, .el-date-editor.el-input__inner {
480
+ .el-date-editor.el-input,
481
+ .el-date-editor.el-input__inner {
463
482
  width: 100%;
464
483
  }
465
484
  }
466
485
  }
467
486
 
468
- &__filter-title {
469
- border-top: 1px solid #f3f3f3;
470
- padding-top: 14px;
471
- }
472
-
473
487
  &__sort-btns {
474
488
  display: flex;
475
489
  justify-content: space-between;
@@ -543,6 +557,7 @@
543
557
  justify-content: flex-end;
544
558
  align-items: center;
545
559
  padding: 0 15px;
560
+ margin-top: 14px;
546
561
  }
547
562
 
548
563
  &__reset-btn,
@@ -802,4 +817,4 @@
802
817
  &::before {
803
818
  bottom: -4px;
804
819
  }
805
- }
820
+ }
@@ -104,7 +104,7 @@
104
104
  :fixed="leftFixedColumnCount > 0 ? 'left' : false"
105
105
  :filtered-value="Array.isArray(filteredValue[colorFilterConfig?.prop]) ? filteredValue[colorFilterConfig?.prop] : []"
106
106
  >
107
- <template #header="scope">
107
+ <template #header>
108
108
  <biz-table-header-popover
109
109
  v-if="colorFilterConfig"
110
110
  :head-active="isColumnHeadActive(colorFilterConfig)"
@@ -114,14 +114,12 @@
114
114
  :temp-sort-prop="tempSortProp"
115
115
  :temp-sort-type="tempSortType"
116
116
  :temp-filtered-value="tempFilteredValue"
117
- :temp-search-value="tempSearchValue"
118
117
  @update:tempSummaryList="val => { tempSummaryList = val }"
119
118
  @update:tempFilteredValue="(key, value) => { $set(tempFilteredValue, key, value) }"
120
- @update:tempSearchValue="(key, value) => { $set(tempSearchValue, key, value) }"
121
119
  @popover-show="() => handleHeaderPopoverShow(colorFilterConfig)"
122
120
  @update:sort="handleSort"
123
- @reset="() => handleHeaderOperationReset(colorFilterConfig, scope)"
124
- @confirm="() => handleHeaderOperationConfirm(colorFilterConfig, scope)"
121
+ @reset="() => handleHeaderOperationReset(colorFilterConfig)"
122
+ @confirm="() => handleHeaderOperationConfirm(colorFilterConfig)"
125
123
  >
126
124
  <template #custom>
127
125
  <div class="editable-table__color-icon" />
@@ -158,8 +156,8 @@
158
156
  v-bind="getColumnBindProps(column)"
159
157
  >
160
158
  <template
159
+ #header
161
160
  v-if="showColumnHeadSortIcon(column)"
162
- #header="scope"
163
161
  >
164
162
  <biz-table-header-popover
165
163
  :head-active="isColumnHeadActive(column)"
@@ -169,14 +167,12 @@
169
167
  :temp-sort-prop="tempSortProp"
170
168
  :temp-sort-type="tempSortType"
171
169
  :temp-filtered-value="tempFilteredValue"
172
- :temp-search-value="tempSearchValue"
173
170
  @update:tempSummaryList="val => { tempSummaryList = val }"
174
171
  @update:tempFilteredValue="(key, value) => { $set(tempFilteredValue, key, value) }"
175
- @update:tempSearchValue="(key, value) => { $set(tempSearchValue, key, value) }"
176
172
  @popover-show="() => handleHeaderPopoverShow(column)"
177
173
  @update:sort="handleSort"
178
- @reset="() => handleHeaderOperationReset(column, scope)"
179
- @confirm="() => handleHeaderOperationConfirm(column, scope)"
174
+ @reset="() => handleHeaderOperationReset(column)"
175
+ @confirm="() => handleHeaderOperationConfirm(column)"
180
176
  >
181
177
  <template #filter-item="item">
182
178
  <slot
@@ -194,15 +190,6 @@
194
190
  {{ column.label }}
195
191
  </slot>
196
192
  </template>
197
- <template
198
- v-for="searchOption in (Array.isArray(column.search) ? column.search : []).filter(v => v.type === 'slot')"
199
- #[searchOption.slotName]="rest"
200
- >
201
- <slot
202
- :name="searchOption.slotName"
203
- v-bind="rest"
204
- />
205
- </template>
206
193
  </biz-table-header-popover>
207
194
  </template>
208
195
  <!-- 默认操作按钮,defaultOperations属性不为空数组时展示。编辑状态下隐藏 -->
@@ -434,7 +421,7 @@ const props = withDefaults(defineProps<IProps>(), {
434
421
  total: 0,
435
422
  defaultOperations: () => [],
436
423
  colorList: () => [],
437
- colorFilterConfig: () => null,
424
+ colorFilterConfig: undefined,
438
425
  leftFixedCount: 1,
439
426
  dragSemiRange: 15,
440
427
  loading: false,
@@ -516,7 +503,7 @@ const {
516
503
  const {
517
504
  setRowStyle,
518
505
  } = useRowBgColor({
519
- colorList: props.colorList,
506
+ colorList: props.colorList || [],
520
507
  emit
521
508
  });
522
509
 
@@ -554,13 +541,11 @@ const {
554
541
  filteredValue,
555
542
  showColumnHeadSortIcon,
556
543
  sortProp,
557
- tempSearchValue,
558
544
  tempFilteredValue,
559
545
  tempSummaryList,
560
546
  tempSortType,
561
547
  tempSortProp,
562
548
  isColumnFiltering,
563
- searchValue,
564
549
  inSorting,
565
550
  } = useColumnHeaderOperation({
566
551
  tableDomRef,