@neatui/vue 2.7.0 → 2.7.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neatui/vue",
3
- "version": "2.7.0",
3
+ "version": "2.7.2",
4
4
  "description": "basic component ui",
5
5
  "main": "./src/index.ts",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div ui-flex="row lm :wrap" class="neatui-upload mb-sm-sub mr-sm-sub" v-bind="{ ...attrs }">
3
- <div v-for="(file, idx) in state.files" :key="idx" ui-flex="col cm" class="flex-fixed bg-fore n-sl w-ms h-ms b-solid bk-line b-xs r-sm pr">
3
+ <div v-for="(file, idx) in state.files" :key="idx" ui-flex="col cm" class="flex-fixed bg-fore n-sl w-ms h-ms b-solid bk-case b-xs r-sm pr" v-bind="itemAttrs">
4
4
  <!-- 删除按钮 -->
5
5
  <div class="pa ot-ss or-ss bg-fore co-risk lh-xs r-xl parent-hover:show" @click="remove(idx)">
6
6
  <svg class="block" viewBox="0 0 1024 1024" style="width: 1em; height: 1em">
@@ -147,7 +147,7 @@
147
147
  </svg>
148
148
  </template>
149
149
 
150
- <!-- 上传状态显示 -->
150
+ <!-- 上传状态 -->
151
151
  <div v-if="file.status === 'uploading'" class="upload-status">
152
152
  <div class="loading-spinner"></div>
153
153
  <span class="status-text">上传中...</span>
@@ -155,15 +155,12 @@
155
155
  <div v-else-if="file.status === 'error'" class="upload-status error">
156
156
  <span class="status-text">上传失败</span>
157
157
  </div>
158
-
159
- <!-- 文件名 -->
160
- <!-- <p ui-omit="1" class="fs-ss ac">{{ file.name }}</p> -->
161
158
  </div>
162
159
 
163
160
  <!-- 上传按钮 -->
164
161
  <div v-if="showUploadButton">
165
- <slot>
166
- <label ui-flex="row cm" ui-form="@a type:upload tips:hover" :class="`upload w-ms h-ms n-sl bg-fore b-solid bk-line b-xs r-sm ${uploadClass}`" :style="uploadStyle">
162
+ <slot name="btn">
163
+ <label ui-flex="row cm" ui-form="@a type:upload tips:hover" :class="`upload w-ms h-ms n-sl bg-fore b-solid bk-case b-xs r-sm ${uploadClass}`" :style="uploadStyle">
167
164
  <div v-if="tips" ui-form-tips>
168
165
  {{ tips }}
169
166
  </div>
@@ -179,53 +176,40 @@
179
176
  </label>
180
177
  </slot>
181
178
  </div>
182
- <Layer :id="layerId" am="as"></Layer>
183
179
  </div>
184
180
  </template>
185
181
  <script setup lang="ts">
186
- import { reactive, ref, watch, onUnmounted, computed } from 'vue';
187
- import { Image } from '../display';
188
- import draggable from 'vuedraggable';
182
+ import { reactive, watch, onUnmounted, computed } from 'vue';
189
183
  import Compressor from 'compressorjs';
190
- import { Layer, LayerById } from '../basic';
191
- import { isArray, isFunction } from '@fekit/utils';
192
-
193
- const layerId = Math.random().toString(36).slice(-6);
194
184
 
195
185
  interface Props {
196
186
  modelValue?: string | any[];
197
- // 文件数量限制
198
187
  max?: number;
199
188
  attrs?: object;
189
+ itemAttrs?: object;
200
190
  uploadClass?: string;
201
191
  uploadStyle?: string;
202
- id?: string;
203
- tips?: string; // 提示文案
204
- // 文件类型
192
+ tips?: string;
205
193
  accept?: string | 'image/*' | 'video/*' | 'audio/*';
206
- // 是否需要抠图处理
207
- crop?: boolean;
208
- // 请求上传接口
209
194
  upload?: (formData: FormData) => Promise<any>;
210
- // 文件大小限制(MB)
211
195
  maxSize?: number;
212
- // 是否启用图片压缩
213
196
  compress?: boolean;
197
+ disabled?: boolean;
214
198
  }
215
199
 
216
200
  const props = withDefaults(defineProps<Props>(), {
217
201
  modelValue: '',
218
202
  uploadClass: '',
219
203
  uploadStyle: '',
220
- id: '',
221
204
  attrs: () => ({}),
205
+ itemAttrs: () => ({}),
222
206
  tips: '',
223
207
  accept: '*',
224
- crop: false,
225
208
  upload: async () => null,
226
- maxSize: 10, // 默认10MB
209
+ maxSize: 10,
227
210
  compress: true,
228
- max: 0
211
+ max: 0,
212
+ disabled: false,
229
213
  });
230
214
 
231
215
  interface UploadFile {
@@ -243,7 +227,7 @@
243
227
  isInternalUpdate: boolean; // 标记是否为内部更新
244
228
  }>({
245
229
  files: [],
246
- isInternalUpdate: false
230
+ isInternalUpdate: false,
247
231
  });
248
232
 
249
233
  const emit = defineEmits(['update:modelValue', 'change', 'error']);
@@ -331,7 +315,7 @@
331
315
  success: (result) => {
332
316
  resolve(new File([result], file.name, { type: result.type }));
333
317
  },
334
- error: reject
318
+ error: reject,
335
319
  });
336
320
  });
337
321
  };
@@ -412,7 +396,7 @@
412
396
  type: getFileType(file),
413
397
  path: previewUrl,
414
398
  status: 'uploading',
415
- size: file.size
399
+ size: file.size,
416
400
  };
417
401
  state.files.push(uploadFile);
418
402
  validFiles.push({ file, uploadFile });
@@ -492,7 +476,7 @@
492
476
  type: 'image',
493
477
  path: item,
494
478
  status: 'success' as const,
495
- result: item
479
+ result: item,
496
480
  };
497
481
  } else {
498
482
  // 如果是对象
@@ -501,7 +485,7 @@
501
485
  type: item.type || 'file',
502
486
  path: item.url || item.path || '',
503
487
  status: 'success' as const,
504
- result: item
488
+ result: item,
505
489
  };
506
490
  }
507
491
  });
@@ -512,8 +496,8 @@
512
496
  type: 'image',
513
497
  path: newValue,
514
498
  status: 'success',
515
- result: newValue
516
- }
499
+ result: newValue,
500
+ },
517
501
  ];
518
502
  } else if (newValue && typeof newValue === 'object') {
519
503
  const objValue = newValue as any;
@@ -523,14 +507,14 @@
523
507
  type: objValue.type || 'file',
524
508
  path: objValue.url || objValue.path || '',
525
509
  status: 'success',
526
- result: newValue
527
- }
510
+ result: newValue,
511
+ },
528
512
  ];
529
513
  } else {
530
514
  state.files = [];
531
515
  }
532
516
  },
533
- { immediate: true, deep: true }
517
+ { immediate: true, deep: true },
534
518
  );
535
519
 
536
520
  // 组件销毁时清理资源
@@ -1,186 +1,184 @@
1
1
  <template>
2
- <slot>
3
- <div :ui-row="rows" class="n-sl-sub">
4
- <template v-for="(col, idx) in state.form" :key="idx">
5
- <div v-if="!col.clear" :id="col._fid" :ui-col="col.media" :ui-flex="`${vertical ? 'col xm' : 'row lt'}`">
6
- <template v-if="col.label">
7
- <h6 v-if="vertical" :style="`${col.rules?.some((validator: any) => validator.required) ? 'text-indent: -1em' : ''}`" class="o-ls">
8
- <template v-if="col.label == '*'"></template>
9
- <template v-else>
10
- <b v-if="col.rules?.some((validator: any) => validator.required)" class="co-risk pr" style="left: 0.1em">﹡</b>
11
- <span>{{ col.label ? col.label + ':' : '' }}</span>
12
- </template>
13
- </h6>
14
- <p v-else :class="`${clh({ ...(col.model[1] || {}) })}`" :style="`width:${state._lw + 'em'}; max-width:50%;`" class="ar nr-sm flex-fixed o-ls">
15
- <template v-if="col.label == '*'"></template>
16
- <template v-else>
17
- <b v-if="col.rules?.some((validator: any) => validator.required)" class="co-risk">﹡</b>
18
- <span>{{ col.label ? col.label + ':' : '' }}</span>
19
- </template>
20
- </p>
21
- </template>
22
- <Input v-if="col.model[0] === 'Input'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" class="full-w" v-bind="{ ...(col.model[1] || {}) }" />
23
- <Select
24
- v-else-if="col.model[0] === 'Select'"
25
- v-model="col._data[col.field]"
26
- :interacted="interacted"
27
- :options="col.enums"
28
- :rules="col?.rules"
29
- class="full-w"
30
- v-bind="{ ...(col.model[1] || {}) }"
31
- ></Select>
32
- <SelectList
33
- v-else-if="col.model[0] === 'SelectList'"
34
- v-model="col._data[col.field]"
35
- :interacted="interacted"
36
- :options="col.enums"
37
- :rules="col?.rules"
38
- class="full-w"
39
- v-bind="{ ...(col.model[1] || {}) }"
40
- />
41
- <SelectTags
42
- v-else-if="col.model[0] === 'SelectTags'"
43
- v-model="col._data[col.field]"
44
- :interacted="interacted"
45
- :options="col.enums"
46
- :rules="col?.rules"
47
- class="full-w"
48
- v-bind="{ ...(col.model[1] || {}) }"
49
- />
50
- <SelectPicker
51
- v-else-if="col.model[0] === 'SelectPicker'"
52
- v-model="col._data[col.field]"
53
- :interacted="interacted"
54
- :options="col.enums"
55
- :rules="col?.rules"
56
- class="full-w"
57
- v-bind="{ ...(col.model[1] || {}) }"
58
- />
59
- <SearchSelect
60
- v-else-if="col.model[0] === 'SearchSelect'"
61
- v-model="col._data[col.field]"
62
- :interacted="interacted"
63
- :options="col.enums"
64
- :rules="col.rules"
65
- class="full-w"
66
- v-bind="{ ...(col.model[1] || {}) }"
67
- />
68
- <MoreSelect
69
- v-else-if="col.model[0] === 'MoreSelect'"
70
- v-model="col._data[col.field]"
71
- :interacted="interacted"
72
- :options="col.enums"
73
- :rules="col.rules"
74
- class="full-w"
75
- v-bind="{ ...(col.model[1] || {}) }"
76
- />
77
- <MoreSelectList
78
- v-else-if="col.model[0] === 'MoreSelectList'"
79
- v-model="col._data[col.field]"
80
- :interacted="interacted"
81
- :options="col.enums"
82
- :rules="col.rules"
83
- class="full-w"
84
- v-bind="{ ...(col.model[1] || {}) }"
85
- />
86
- <MoreSelectTags
87
- v-else-if="col.model[0] === 'MoreSelectTags'"
88
- v-model="col._data[col.field]"
89
- :interacted="interacted"
90
- :options="col.enums"
91
- :rules="col.rules"
92
- class="full-w"
93
- v-bind="{ ...(col.model[1] || {}) }"
94
- />
95
- <MoreSelectPicker
96
- v-else-if="col.model[0] === 'MoreSelectPicker'"
97
- v-model="col._data[col.field]"
98
- :interacted="interacted"
99
- :options="col.enums"
100
- :rules="col.rules"
101
- class="full-w"
102
- v-bind="{ ...(col.model[1] || {}) }"
103
- />
104
- <SearchMoreSelect
105
- v-else-if="col.model[0] === 'SearchMoreSelect'"
106
- v-model="col._data[col.field]"
107
- :interacted="interacted"
108
- :options="col.enums"
109
- :rules="col.rules"
110
- class="full-w"
111
- v-bind="{ ...(col.model[1] || {}) }"
112
- />
113
- <PageSelect
114
- v-else-if="col.model[0] === 'PageSelect'"
115
- v-model="col._data[col.field]"
116
- :interacted="interacted"
117
- :options="col.enums"
118
- :rules="col.rules"
119
- class="full-w"
120
- v-bind="{ ...(col.model[1] || {}) }"
121
- />
122
- <SelectTree
123
- v-else-if="col.model[0] === 'SelectTree'"
124
- v-model="col._data[col.field]"
125
- :interacted="interacted"
126
- :options="col.enums"
127
- :rules="col.rules"
128
- class="full-w"
129
- v-bind="{ ...(col.model[1] || {}) }"
130
- />
131
- <DatePicker
132
- v-else-if="col.model[0] === 'DatePicker'"
133
- v-model="col._data[col.field]"
134
- :interacted="interacted"
135
- :rules="col.rules"
136
- class="full-w"
137
- v-bind="{ ...(col.model[1] || {}) }"
138
- />
139
- <DateRangePicker
140
- v-else-if="col.model[0] === 'DateRangePicker'"
141
- v-model="col._data[col.field]"
142
- :interacted="interacted"
143
- :rules="col.rules"
144
- class="full-w"
145
- v-bind="{ ...(col.model[1] || {}) }"
146
- />
147
- <ImgUpload v-else-if="col.model[0] === 'ImgUpload'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
148
- <Upload v-else-if="col.model[0] === 'Upload'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
149
- <TextArea
150
- v-else-if="col.model[0] === 'TextArea'"
151
- v-model="col._data[col.field]"
152
- :interacted="interacted"
153
- :rules="col.rules"
154
- v-bind="{ ...(col.model[1] || {}) }"
155
- ></TextArea>
156
- <Switch v-else-if="col.model[0] === 'Switch'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
157
- <Tree v-else-if="col.model[0] === 'Tree'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" class="mt-sm" v-bind="{ ...(col.model[1] || {}) }" />
158
- <Cascader
159
- v-else-if="col.model[0] === 'Cascader'"
160
- v-model="col._data[col.field]"
161
- :interacted="interacted"
162
- :options="col.enums"
163
- :rules="col.rules"
164
- class="full-w"
165
- v-bind="{ ...(col.model[1] || {}) }"
166
- />
167
- <template v-else>
168
- <slot v-if="col.slot" :name="col.slot" :col="col"></slot>
169
- <slot v-else-if="col.model[0]" :name="col.model[0]" :col="col"></slot>
170
- <slot v-else :name="col.field" :col="col"></slot>
171
- </template>
172
- </div>
173
- </template>
174
- <div :ui-flex="`${vertical ? 'col xm' : 'row xt'}`">
175
- <div v-if="vertical">
176
- <slot name="submit"></slot>
177
- </div>
178
- <div v-else :style="`padding-left:${state._lw + 'em'};`">
179
- <slot name="submit"></slot>
180
- </div>
2
+ <div :ui-row="rows" class="n-sl-sub">
3
+ <template v-for="(col, idx) in state.form" :key="idx">
4
+ <div v-if="!col.clear" :id="col._fid" :ui-col="col.media" :ui-flex="`${vertical ? 'col xm' : 'row lt'}`">
5
+ <template v-if="col.label">
6
+ <h6 v-if="vertical" :style="`${col.rules?.some((validator: any) => validator.required) ? 'text-indent: -1em' : ''}`" class="o-ls">
7
+ <template v-if="col.label == '*'"></template>
8
+ <template v-else>
9
+ <b v-if="col.rules?.some((validator: any) => validator.required)" class="co-risk pr" style="left: 0.1em">﹡</b>
10
+ <span>{{ col.label ? col.label + ':' : '' }}</span>
11
+ </template>
12
+ </h6>
13
+ <p v-else :class="`${clh({ ...(col.model[1] || {}) })}`" :style="`width:${state._lw + 'em'}; max-width:50%;`" class="ar nr-sm flex-fixed o-ls">
14
+ <template v-if="col.label == '*'"></template>
15
+ <template v-else>
16
+ <b v-if="col.rules?.some((validator: any) => validator.required)" class="co-risk">﹡</b>
17
+ <span>{{ col.label ? col.label + ':' : '' }}</span>
18
+ </template>
19
+ </p>
20
+ </template>
21
+ <Input v-if="col.model[0] === 'Input'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" class="full-w" v-bind="{ ...(col.model[1] || {}) }" />
22
+ <Select
23
+ v-else-if="col.model[0] === 'Select'"
24
+ v-model="col._data[col.field]"
25
+ :interacted="interacted"
26
+ :options="col.enums"
27
+ :rules="col?.rules"
28
+ class="full-w"
29
+ v-bind="{ ...(col.model[1] || {}) }"
30
+ ></Select>
31
+ <SelectList
32
+ v-else-if="col.model[0] === 'SelectList'"
33
+ v-model="col._data[col.field]"
34
+ :interacted="interacted"
35
+ :options="col.enums"
36
+ :rules="col?.rules"
37
+ class="full-w"
38
+ v-bind="{ ...(col.model[1] || {}) }"
39
+ />
40
+ <SelectTags
41
+ v-else-if="col.model[0] === 'SelectTags'"
42
+ v-model="col._data[col.field]"
43
+ :interacted="interacted"
44
+ :options="col.enums"
45
+ :rules="col?.rules"
46
+ class="full-w"
47
+ v-bind="{ ...(col.model[1] || {}) }"
48
+ />
49
+ <SelectPicker
50
+ v-else-if="col.model[0] === 'SelectPicker'"
51
+ v-model="col._data[col.field]"
52
+ :interacted="interacted"
53
+ :options="col.enums"
54
+ :rules="col?.rules"
55
+ class="full-w"
56
+ v-bind="{ ...(col.model[1] || {}) }"
57
+ />
58
+ <SearchSelect
59
+ v-else-if="col.model[0] === 'SearchSelect'"
60
+ v-model="col._data[col.field]"
61
+ :interacted="interacted"
62
+ :options="col.enums"
63
+ :rules="col.rules"
64
+ class="full-w"
65
+ v-bind="{ ...(col.model[1] || {}) }"
66
+ />
67
+ <MoreSelect
68
+ v-else-if="col.model[0] === 'MoreSelect'"
69
+ v-model="col._data[col.field]"
70
+ :interacted="interacted"
71
+ :options="col.enums"
72
+ :rules="col.rules"
73
+ class="full-w"
74
+ v-bind="{ ...(col.model[1] || {}) }"
75
+ />
76
+ <MoreSelectList
77
+ v-else-if="col.model[0] === 'MoreSelectList'"
78
+ v-model="col._data[col.field]"
79
+ :interacted="interacted"
80
+ :options="col.enums"
81
+ :rules="col.rules"
82
+ class="full-w"
83
+ v-bind="{ ...(col.model[1] || {}) }"
84
+ />
85
+ <MoreSelectTags
86
+ v-else-if="col.model[0] === 'MoreSelectTags'"
87
+ v-model="col._data[col.field]"
88
+ :interacted="interacted"
89
+ :options="col.enums"
90
+ :rules="col.rules"
91
+ class="full-w"
92
+ v-bind="{ ...(col.model[1] || {}) }"
93
+ />
94
+ <MoreSelectPicker
95
+ v-else-if="col.model[0] === 'MoreSelectPicker'"
96
+ v-model="col._data[col.field]"
97
+ :interacted="interacted"
98
+ :options="col.enums"
99
+ :rules="col.rules"
100
+ class="full-w"
101
+ v-bind="{ ...(col.model[1] || {}) }"
102
+ />
103
+ <SearchMoreSelect
104
+ v-else-if="col.model[0] === 'SearchMoreSelect'"
105
+ v-model="col._data[col.field]"
106
+ :interacted="interacted"
107
+ :options="col.enums"
108
+ :rules="col.rules"
109
+ class="full-w"
110
+ v-bind="{ ...(col.model[1] || {}) }"
111
+ />
112
+ <PageSelect
113
+ v-else-if="col.model[0] === 'PageSelect'"
114
+ v-model="col._data[col.field]"
115
+ :interacted="interacted"
116
+ :options="col.enums"
117
+ :rules="col.rules"
118
+ class="full-w"
119
+ v-bind="{ ...(col.model[1] || {}) }"
120
+ />
121
+ <SelectTree
122
+ v-else-if="col.model[0] === 'SelectTree'"
123
+ v-model="col._data[col.field]"
124
+ :interacted="interacted"
125
+ :options="col.enums"
126
+ :rules="col.rules"
127
+ class="full-w"
128
+ v-bind="{ ...(col.model[1] || {}) }"
129
+ />
130
+ <DatePicker
131
+ v-else-if="col.model[0] === 'DatePicker'"
132
+ v-model="col._data[col.field]"
133
+ :interacted="interacted"
134
+ :rules="col.rules"
135
+ class="full-w"
136
+ v-bind="{ ...(col.model[1] || {}) }"
137
+ />
138
+ <DateRangePicker
139
+ v-else-if="col.model[0] === 'DateRangePicker'"
140
+ v-model="col._data[col.field]"
141
+ :interacted="interacted"
142
+ :rules="col.rules"
143
+ class="full-w"
144
+ v-bind="{ ...(col.model[1] || {}) }"
145
+ />
146
+ <ImgUpload v-else-if="col.model[0] === 'ImgUpload'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
147
+ <Upload v-else-if="col.model[0] === 'Upload'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
148
+ <TextArea
149
+ v-else-if="col.model[0] === 'TextArea'"
150
+ v-model="col._data[col.field]"
151
+ :interacted="interacted"
152
+ :rules="col.rules"
153
+ v-bind="{ ...(col.model[1] || {}) }"
154
+ ></TextArea>
155
+ <Switch v-else-if="col.model[0] === 'Switch'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
156
+ <Tree v-else-if="col.model[0] === 'Tree'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" class="mt-sm" v-bind="{ ...(col.model[1] || {}) }" />
157
+ <Cascader
158
+ v-else-if="col.model[0] === 'Cascader'"
159
+ v-model="col._data[col.field]"
160
+ :interacted="interacted"
161
+ :options="col.enums"
162
+ :rules="col.rules"
163
+ class="full-w"
164
+ v-bind="{ ...(col.model[1] || {}) }"
165
+ />
166
+ <template v-else>
167
+ <slot v-if="col.slot" :name="col.slot" :col="col"></slot>
168
+ <slot v-else-if="col.model[0]" :name="col.model[0]" :col="col"></slot>
169
+ <slot v-else :name="col.field" :col="col"></slot>
170
+ </template>
171
+ </div>
172
+ </template>
173
+ <div :ui-flex="`${vertical ? 'col xm' : 'row xt'}`">
174
+ <div v-if="vertical">
175
+ <slot name="submit"></slot>
176
+ </div>
177
+ <div v-else :style="`padding-left:${state._lw + 'em'};`">
178
+ <slot name="submit"></slot>
181
179
  </div>
182
180
  </div>
183
- </slot>
181
+ </div>
184
182
  </template>
185
183
  <script lang="ts" setup>
186
184
  import { watch, reactive, onBeforeUnmount, nextTick } from 'vue';
@@ -43,6 +43,7 @@
43
43
  </template>
44
44
  </IFollowView>
45
45
  <button class="bg-fore" ui-btn="@a s case :border" @click="fResetLimit">{{ words['common.reset'] || '重置' }}</button>
46
+ <button class="bg-fore" ui-btn="@a s case :border" @click="fSearch">{{ words['common.search'] || '搜索' }}</button>
46
47
  </div>
47
48
  </div>
48
49
  </template>
@@ -91,6 +92,11 @@
91
92
  tools: false
92
93
  });
93
94
 
95
+ // 定义emit事件
96
+ const emits = defineEmits<{
97
+ search: [param: any];
98
+ }>();
99
+
94
100
  // 内部数据
95
101
  const state: any = reactive({
96
102
  name: computed(() => 'confs' + route.path?.replace(/\//g, '_')),
@@ -173,5 +179,16 @@
173
179
  delete state.param[key];
174
180
  }
175
181
  }
182
+ // 触发搜索事件
183
+ emits('search', state.param);
184
+ };
185
+
186
+ // 搜索功能
187
+ const fSearch = () => {
188
+ // 重置页码
189
+ if (state.param.page) {
190
+ state.param.page = 1;
191
+ }
192
+ emits('search', state.param);
176
193
  };
177
194
  </script>
@@ -1,2 +1,49 @@
1
+ import { isFunction } from '@fekit/utils';
1
2
  import TableLoader from './TableLoader.vue';
3
+
4
+ // 类型定义
5
+ interface ListItem {
6
+ __selected?: boolean;
7
+ [key: string]: any;
8
+ }
9
+
10
+ type KeyExtractor<T = ListItem> = string | ((item: T) => any);
11
+
2
12
  export default TableLoader;
13
+
14
+ // 是否有选中记录
15
+ export const hasSelected = (lists: ListItem[] = []): boolean => {
16
+ return lists.some((item) => item.__selected === true);
17
+ };
18
+
19
+ // 获取选中记录的数量
20
+ export const getSelectedCount = (lists: ListItem[] = []): number => {
21
+ return lists.filter((item) => item.__selected === true).length;
22
+ };
23
+
24
+ // 获取选中的记录
25
+ export const getSelected = <T extends Record<string, any> = ListItem>(lists: T[] = [], key: KeyExtractor<T> = ''): any[] => {
26
+ return (
27
+ lists
28
+ ?.filter((item: T) => {
29
+ return item.__selected === true;
30
+ })
31
+ ?.map((item: T) => {
32
+ return isFunction(key) ? (key as (item: T) => any)(item) : key ? item[key as string] : item;
33
+ }) || []
34
+ );
35
+ };
36
+
37
+ // 全选计算属性
38
+ export function allSelected(lists: ListItem[] = []) {
39
+ return {
40
+ get(): boolean {
41
+ return lists.length > 0 && lists.every((item) => item.__selected === true);
42
+ },
43
+ set(value: boolean): void {
44
+ lists.forEach((item) => {
45
+ item.__selected = value;
46
+ });
47
+ }
48
+ };
49
+ }