@longhongguo/form-create-ant-design-vue 3.2.44 → 3.2.46

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,439 @@
1
+ <template>
2
+ <!-- 单个展示模式 -->
3
+ <div
4
+ v-if="!multiple"
5
+ class="fc-cus-select fc-cus-select-single"
6
+ :class="{
7
+ 'fc-cus-select-disabled': disabled
8
+ }"
9
+ :style="style"
10
+ :tabindex="disabled ? -1 : 0"
11
+ >
12
+ <div
13
+ class="fc-cus-select-selector"
14
+ :class="{
15
+ 'fc-cus-select-selector-borderless': !bordered
16
+ }"
17
+ >
18
+ <span
19
+ v-if="displayValue"
20
+ class="fc-cus-select-selection-item"
21
+ :title="displayLabel"
22
+ >
23
+ {{ displayLabel }}
24
+ </span>
25
+ <span v-else class="fc-cus-select-selection-placeholder">
26
+ {{ placeholder || '请选择' }}
27
+ </span>
28
+ <span v-if="showClear" class="fc-cus-select-clear" @click="clearValue">
29
+ <span
30
+ role="img"
31
+ aria-label="close-circle"
32
+ class="anticon anticon-close-circle"
33
+ >
34
+ <svg
35
+ focusable="false"
36
+ data-icon="close-circle"
37
+ width="1em"
38
+ height="1em"
39
+ fill="currentColor"
40
+ aria-hidden="true"
41
+ fill-rule="evenodd"
42
+ viewBox="64 64 896 896"
43
+ >
44
+ <path
45
+ d="M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm127.98 274.82h-.04l-.08.06L512 466.75 384.14 338.88c-.04-.05-.06-.06-.08-.06a.12.12 0 00-.07 0c-.03 0-.05.01-.09.05l-45.02 45.02a.2.2 0 00-.05.09.12.12 0 000 .07v.02a.27.27 0 00.06.06L466.75 512 338.88 639.86c-.05.04-.06.06-.06.08a.12.12 0 000 .07c0 .03.01.05.05.09l45.02 45.02a.2.2 0 00.09.05.12.12 0 00.07 0c.02 0 .04-.01.08-.05L512 557.25l127.86 127.87c.04.04.06.05.08.05a.12.12 0 00.07 0c.03 0 .05-.01.09-.05l45.02-45.02a.2.2 0 00.05-.09.12.12 0 000-.07v-.02a.27.27 0 00-.05-.06L557.25 512l127.87-127.86c.04-.04.05-.06.05-.08a.12.12 0 000-.07c0-.03-.01-.05-.05-.09l-45.02-45.02a.2.2 0 00-.09-.05.12.12 0 00-.07 0z"
46
+ ></path>
47
+ </svg>
48
+ </span>
49
+ </span>
50
+ <span v-else class="fc-cus-select-arrow">
51
+ <span role="img" aria-label="down" class="anticon anticon-down">
52
+ <svg
53
+ focusable="false"
54
+ data-icon="down"
55
+ width="1em"
56
+ height="1em"
57
+ fill="currentColor"
58
+ aria-hidden="true"
59
+ viewBox="64 64 896 896"
60
+ >
61
+ <path
62
+ d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
63
+ ></path>
64
+ </svg>
65
+ </span>
66
+ </span>
67
+ </div>
68
+ </div>
69
+
70
+ <!-- 多个展示模式 -->
71
+ <div
72
+ v-else
73
+ class="fc-cus-select fc-cus-select-multiple"
74
+ :class="{
75
+ 'fc-cus-select-disabled': disabled
76
+ }"
77
+ :style="style"
78
+ :tabindex="disabled ? -1 : 0"
79
+ >
80
+ <div
81
+ class="fc-cus-select-selector"
82
+ :class="{
83
+ 'fc-cus-select-selector-borderless': !bordered
84
+ }"
85
+ >
86
+ <div class="fc-cus-select-selection-overflow">
87
+ <!-- 显示的 tags -->
88
+ <div
89
+ v-for="(item, index) in displayItems"
90
+ :key="index"
91
+ class="fc-cus-select-selection-overflow-item"
92
+ style="opacity: 1"
93
+ >
94
+ <span class="fc-cus-select-selection-item" :title="item.label">
95
+ <span class="fc-cus-select-selection-item-content">{{
96
+ item.label
97
+ }}</span>
98
+ <span
99
+ class="fc-cus-select-selection-item-remove"
100
+ @click="removeItem(item.value, $event)"
101
+ >
102
+ <span role="img" aria-label="close" class="anticon anticon-close">
103
+ <svg
104
+ focusable="false"
105
+ data-icon="close"
106
+ width="1em"
107
+ height="1em"
108
+ fill="currentColor"
109
+ aria-hidden="true"
110
+ fill-rule="evenodd"
111
+ viewBox="64 64 896 896"
112
+ >
113
+ <path
114
+ d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"
115
+ ></path>
116
+ </svg>
117
+ </span>
118
+ </span>
119
+ </span>
120
+ </div>
121
+
122
+ <!-- 折叠提示 -->
123
+ <div
124
+ v-if="remainingCount > 0"
125
+ class="fc-cus-select-selection-overflow-item"
126
+ style="opacity: 1"
127
+ >
128
+ <span class="fc-cus-select-selection-item">
129
+ <span class="fc-cus-select-selection-item-content">
130
+ +{{ remainingCount }}
131
+ </span>
132
+ </span>
133
+ </div>
134
+
135
+ <!-- 空值占位符 -->
136
+ <span v-if="!hasValue" class="fc-cus-select-selection-placeholder">
137
+ {{ placeholder || '请选择' }}
138
+ </span>
139
+ </div>
140
+ <span v-if="showClear" class="fc-cus-select-clear" @click="clearValue">
141
+ <span
142
+ role="img"
143
+ aria-label="close-circle"
144
+ class="anticon anticon-close-circle"
145
+ >
146
+ <svg
147
+ focusable="false"
148
+ data-icon="close-circle"
149
+ width="1em"
150
+ height="1em"
151
+ fill="currentColor"
152
+ aria-hidden="true"
153
+ fill-rule="evenodd"
154
+ viewBox="64 64 896 896"
155
+ >
156
+ <path
157
+ d="M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm127.98 274.82h-.04l-.08.06L512 466.75 384.14 338.88c-.04-.05-.06-.06-.08-.06a.12.12 0 00-.07 0c-.03 0-.05.01-.09.05l-45.02 45.02a.2.2 0 00-.05.09.12.12 0 000 .07v.02a.27.27 0 00.06.06L466.75 512 338.88 639.86c-.05.04-.06.06-.06.08a.12.12 0 000 .07c0 .03.01.05.05.09l45.02 45.02a.2.2 0 00.09.05.12.12 0 00.07 0c.02 0 .04-.01.08-.05L512 557.25l127.86 127.87c.04.04.06.05.08.05a.12.12 0 00.07 0c.03 0 .05-.01.09-.05l45.02-45.02a.2.2 0 00.05-.09.12.12 0 000-.07v-.02a.27.27 0 00-.05-.06L557.25 512l127.87-127.86c.04-.04.05-.06.05-.08a.12.12 0 000-.07c0-.03-.01-.05-.05-.09l-45.02-45.02a.2.2 0 00-.09-.05.12.12 0 00-.07 0z"
158
+ ></path>
159
+ </svg>
160
+ </span>
161
+ </span>
162
+ <span v-else class="fc-cus-select-arrow">
163
+ <span role="img" aria-label="down" class="anticon anticon-down">
164
+ <svg
165
+ focusable="false"
166
+ data-icon="down"
167
+ width="1em"
168
+ height="1em"
169
+ fill="currentColor"
170
+ aria-hidden="true"
171
+ viewBox="64 64 896 896"
172
+ >
173
+ <path
174
+ d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
175
+ ></path>
176
+ </svg>
177
+ </span>
178
+ </span>
179
+ </div>
180
+ </div>
181
+ </template>
182
+
183
+ <script>
184
+ import { defineComponent } from 'vue'
185
+
186
+ export default defineComponent({
187
+ name: 'CusSelect',
188
+ props: {
189
+ // 当前值:统一使用对象数组格式(支持 v-model)
190
+ // 单选时:[{value: '1001', label: '门店1'}] 或 []
191
+ // 多选时:[{value: '1001', label: '门店1'}, {value: '1002', label: '门店2'}] 或 []
192
+ modelValue: {
193
+ type: Array,
194
+ default: () => []
195
+ },
196
+ // 选项列表
197
+ options: {
198
+ type: Array,
199
+ default: () => []
200
+ },
201
+ // 是否多选
202
+ multiple: {
203
+ type: Boolean,
204
+ default: false
205
+ },
206
+ // 最多显示的 tag 数量(多选模式下有效)
207
+ maxTagCount: {
208
+ type: Number,
209
+ default: undefined // 不限制时显示所有
210
+ },
211
+ // 占位符
212
+ placeholder: {
213
+ type: String,
214
+ default: ''
215
+ },
216
+ // 是否禁用样式
217
+ disabled: {
218
+ type: Boolean,
219
+ default: false
220
+ },
221
+ // 自定义样式
222
+ style: {
223
+ type: [String, Object],
224
+ default: () => ({ width: '100%' })
225
+ },
226
+ // 选项的 value 字段名
227
+ valueKey: {
228
+ type: String,
229
+ default: 'value'
230
+ },
231
+ // 选项的 label 字段名
232
+ labelKey: {
233
+ type: String,
234
+ default: 'label'
235
+ },
236
+ // 是否允许清除
237
+ allowClear: {
238
+ type: Boolean,
239
+ default: false
240
+ },
241
+ // 是否有边框
242
+ bordered: {
243
+ type: Boolean,
244
+ default: true
245
+ }
246
+ },
247
+ emits: ['update:modelValue', 'change'],
248
+ computed: {
249
+ // 统一处理值(支持 v-model),确保始终是对象数组格式
250
+ currentValue: {
251
+ get() {
252
+ // 确保返回对象数组格式
253
+ if (!Array.isArray(this.modelValue)) {
254
+ // 如果是 null、undefined 或空字符串,返回空数组
255
+ if (
256
+ this.modelValue === null ||
257
+ this.modelValue === undefined ||
258
+ this.modelValue === ''
259
+ ) {
260
+ return []
261
+ }
262
+ // 如果是单个对象,转换为数组
263
+ if (typeof this.modelValue === 'object') {
264
+ return [this.modelValue]
265
+ }
266
+ // 如果是单个值,转换为对象数组格式
267
+ return [
268
+ { value: this.modelValue, label: this.getLabel(this.modelValue) }
269
+ ]
270
+ }
271
+ // 确保数组中的每个元素都是对象格式
272
+ return this.modelValue.map((item) => {
273
+ if (
274
+ typeof item === 'object' &&
275
+ item !== null &&
276
+ (item.value !== undefined || item[this.valueKey] !== undefined)
277
+ ) {
278
+ // 已经是对象格式
279
+ return item
280
+ }
281
+ // 如果是单个值,转换为对象格式
282
+ return {
283
+ [this.valueKey]: item,
284
+ [this.labelKey]: this.getLabel(item)
285
+ }
286
+ })
287
+ },
288
+ set(val) {
289
+ // 确保设置的值是对象数组格式
290
+ let arrayValue = []
291
+ if (val !== null && val !== undefined) {
292
+ if (Array.isArray(val)) {
293
+ // 确保数组中的每个元素都是对象格式
294
+ arrayValue = val.map((item) => {
295
+ if (
296
+ typeof item === 'object' &&
297
+ item !== null &&
298
+ (item.value !== undefined || item[this.valueKey] !== undefined)
299
+ ) {
300
+ return item
301
+ }
302
+ // 单个值转换为对象格式
303
+ return {
304
+ [this.valueKey]: item,
305
+ [this.labelKey]: this.getLabel(item)
306
+ }
307
+ })
308
+ } else if (typeof val === 'object' && val !== null) {
309
+ // 单个对象转换为数组
310
+ arrayValue = [val]
311
+ } else {
312
+ // 单个值转换为对象数组格式
313
+ arrayValue = [
314
+ { [this.valueKey]: val, [this.labelKey]: this.getLabel(val) }
315
+ ]
316
+ }
317
+ }
318
+ // 触发 update:modelValue 事件以支持 v-model
319
+ this.$emit('update:modelValue', arrayValue)
320
+ this.$emit('change', arrayValue)
321
+ }
322
+ },
323
+ // 是否有值(统一判断数组长度)
324
+ hasValue() {
325
+ const val = this.currentValue
326
+ return Array.isArray(val) && val.length > 0
327
+ },
328
+ // 单个模式:显示的值对象(从数组中取第一个)
329
+ displayValue() {
330
+ if (!this.hasValue) return null
331
+ return this.currentValue[0]
332
+ },
333
+ // 单个模式:显示的标签
334
+ displayLabel() {
335
+ if (!this.displayValue) return ''
336
+ const item = this.displayValue
337
+ // 如果是对象格式,直接取 label
338
+ if (typeof item === 'object' && item !== null) {
339
+ return (
340
+ item[this.labelKey] ||
341
+ item.label ||
342
+ String(item[this.valueKey] || item.value || '')
343
+ )
344
+ }
345
+ // 如果不是对象,使用 getLabel 方法查找
346
+ return this.getLabel(item)
347
+ },
348
+ // 多个模式:所有选中的项(直接使用对象数组)
349
+ allSelectedItems() {
350
+ const val = this.currentValue
351
+ if (!Array.isArray(val) || val.length === 0) {
352
+ return []
353
+ }
354
+ // 直接返回对象数组,确保格式统一
355
+ return val.map((item) => {
356
+ if (typeof item === 'object' && item !== null) {
357
+ return {
358
+ value: item[this.valueKey] || item.value,
359
+ label:
360
+ item[this.labelKey] ||
361
+ item.label ||
362
+ String(item[this.valueKey] || item.value || '')
363
+ }
364
+ }
365
+ // 如果不是对象,转换为对象格式
366
+ return {
367
+ value: item,
368
+ label: this.getLabel(item)
369
+ }
370
+ })
371
+ },
372
+ // 多个模式:显示的项(根据 maxTagCount 限制)
373
+ displayItems() {
374
+ const items = this.allSelectedItems
375
+ if (this.maxTagCount === undefined || this.maxTagCount === null) {
376
+ return items
377
+ }
378
+ return items.slice(0, this.maxTagCount)
379
+ },
380
+ // 多个模式:剩余未显示的项数量
381
+ remainingCount() {
382
+ if (this.maxTagCount === undefined || this.maxTagCount === null) {
383
+ return 0
384
+ }
385
+ const total = this.allSelectedItems.length
386
+ return Math.max(0, total - this.maxTagCount)
387
+ },
388
+ // 是否显示清除图标
389
+ showClear() {
390
+ return this.allowClear && this.hasValue && !this.disabled
391
+ }
392
+ },
393
+ methods: {
394
+ // 根据 value 查找对应的 option
395
+ findOptionByValue(val) {
396
+ if (!this.options || this.options.length === 0) {
397
+ return null
398
+ }
399
+ return this.options.find((opt) => {
400
+ const optValue = typeof opt === 'object' ? opt[this.valueKey] : opt
401
+ return optValue === val || String(optValue) === String(val)
402
+ })
403
+ },
404
+ // 获取显示的 label
405
+ getLabel(val) {
406
+ const option = this.findOptionByValue(val)
407
+ if (option) {
408
+ return typeof option === 'object' ? option[this.labelKey] : option
409
+ }
410
+ // 如果找不到对应的 option,直接显示 value
411
+ return String(val)
412
+ },
413
+ // 删除单个项
414
+ removeItem(itemValue, event) {
415
+ if (this.disabled) return
416
+ event.stopPropagation()
417
+ const val = this.currentValue
418
+ if (Array.isArray(val)) {
419
+ // 根据 value 来过滤,支持对象数组格式
420
+ const newValue = val.filter((item) => {
421
+ if (typeof item === 'object' && item !== null) {
422
+ const currentItemValue = item[this.valueKey] || item.value
423
+ return currentItemValue !== itemValue
424
+ }
425
+ return item !== itemValue
426
+ })
427
+ this.currentValue = newValue
428
+ }
429
+ },
430
+ // 清除所有值
431
+ clearValue(event) {
432
+ if (this.disabled) return
433
+ event.stopPropagation()
434
+ // 统一设置为空数组
435
+ this.currentValue = []
436
+ }
437
+ }
438
+ })
439
+ </script>