@form-create/component-elm-group 3.2.23 → 3.2.34

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/src/component.jsx CHANGED
@@ -3,6 +3,7 @@ import {defineComponent, markRaw, nextTick, watch} from 'vue';
3
3
  import deepExtend, {deepCopy} from '@form-create/utils/lib/deepextend';
4
4
  import extend from '@form-create/utils/lib/extend';
5
5
  import './style.css';
6
+ import {toPromise} from '@form-create/utils';
6
7
 
7
8
  const NAME = 'fcGroup';
8
9
 
@@ -43,6 +44,14 @@ export default defineComponent({
43
44
  type: Boolean,
44
45
  default: true
45
46
  },
47
+ title: {
48
+ type: [String, Function],
49
+ default: null
50
+ },
51
+ type: {
52
+ type: String,
53
+ default: 'default'
54
+ },
46
55
  onBeforeRemove: {
47
56
  type: Function,
48
57
  default: () => {
@@ -199,13 +208,20 @@ export default defineComponent({
199
208
  this.input(value);
200
209
  },
201
210
  del(index, key) {
202
- if (this.disabled || false === this.onBeforeRemove(this.modelValue, index)) {
211
+ if (this.disabled) {
203
212
  return;
204
213
  }
205
- this.removeRule(key, true);
206
- const value = [...this.modelValue];
207
- value.splice(index, 1);
208
- this.input(value);
214
+ const del = () => {
215
+ this.removeRule(key, true);
216
+ const value = [...this.modelValue];
217
+ value.splice(index, 1);
218
+ this.input(value);
219
+ }
220
+ toPromise(this.onBeforeRemove(this.modelValue, index)).then(res => {
221
+ if (false !== res) {
222
+ del();
223
+ }
224
+ })
209
225
  },
210
226
  addIcon(key) {
211
227
  return <div class="_fc-group-btn _fc-group-plus-minus" onClick={this.add}></div>;
@@ -231,6 +247,30 @@ export default defineComponent({
231
247
  }));
232
248
  this.formData(0);
233
249
  },
250
+ sortIcon(index, total) {
251
+ const canMoveUp = index > 0;
252
+ const canMoveDown = index < total - 1;
253
+
254
+ if (!canMoveUp && !canMoveDown) {
255
+ return null;
256
+ }
257
+
258
+ if (canMoveUp && canMoveDown) {
259
+ // 显示合并的上下箭头按钮
260
+ return <div class="_fc-group-btn _fc-group-sort">
261
+ <div class="_fc-group-sort-up" onClick={() => this.changeSort(index, -1)}></div>
262
+ <div class="_fc-group-sort-down" onClick={() => this.changeSort(index, 1)}></div>
263
+ </div>;
264
+ }
265
+
266
+ if (canMoveUp) {
267
+ return this.sortUpIcon(index);
268
+ }
269
+
270
+ if (canMoveDown) {
271
+ return this.sortDownIcon(index);
272
+ }
273
+ },
234
274
  makeIcon(total, index, key) {
235
275
  if (this.$slots.button) {
236
276
  return this.$slots.button({
@@ -249,21 +289,28 @@ export default defineComponent({
249
289
  if (total > this.min) {
250
290
  btn.push(this.delIcon(index, key));
251
291
  }
252
- if (this.sortBtn && index) {
253
- btn.push(this.sortUpIcon(index));
254
- }
255
- if (this.sortBtn && index !== total - 1) {
256
- btn.push(this.sortDownIcon(index));
292
+ if (this.sortBtn) {
293
+ const sortBtn = this.sortIcon(index, total);
294
+ if (sortBtn) {
295
+ btn.push(sortBtn);
296
+ }
257
297
  }
258
298
  return btn;
259
299
  },
260
- emitEvent(name, args, index, key) {
261
- this.$emit(name, ...args, this.cacheRule[key].$f, index);
262
- },
263
300
  expandRule(n) {
264
301
  for (let i = 0; i < n; i++) {
265
- this.addRule(i);
302
+ this.modelValue.push(this.field ? null : {});
266
303
  }
304
+ this.input([...this.modelValue]);
305
+ },
306
+ getTitle(index, key) {
307
+ if (typeof this.title === 'function') {
308
+ return this.title(index, this.modelValue[index], key);
309
+ }
310
+ if (typeof this.title === 'string') {
311
+ return this.title.replace('{index}', index + 1);
312
+ }
313
+ return false;
267
314
  }
268
315
  },
269
316
  created() {
@@ -280,6 +327,7 @@ export default defineComponent({
280
327
  const button = this.button;
281
328
  const Type = this.form;
282
329
  const disabled = this.disabled;
330
+ const isCardType = this.type === 'card';
283
331
 
284
332
  const children = keys.length === 0 ?
285
333
  (this.$slots.default ? (this.$slots.default({
@@ -289,25 +337,55 @@ export default defineComponent({
289
337
  onClick={this.add}/>) : keys.map((key, index) => {
290
338
  const {rule, options} = this.cacheRule[key];
291
339
  const btn = button && !disabled ? this.makeIcon(keys.length, index, key) : [];
292
- return <div class="_fc-group-container" key={key}>
293
- <Type
294
- key={key}
295
- {...{
296
- disabled,
297
- 'onUpdate:modelValue': (formData) => this.formData(key, formData),
298
- 'onEmit-event': (name, ...args) => this.emitEvent(name, args, index, key),
299
- 'onUpdate:api': ($f) => this.add$f(index, key, $f),
300
- inFor: true,
301
- modelValue: this.field ? {[this.field]: this._value(this.modelValue[index])} : this.modelValue[index],
302
- rule,
303
- option: options,
304
- extendOption: true
305
- }}
306
- />
307
- <div class="_fc-group-idx">{index + 1}</div>
308
- {(btn.length) ? <div class="_fc-group-handle fc-clock">{btn}</div> : null}
309
- </div>
340
+ const title = this.getTitle(index, key);
341
+
342
+ if (isCardType) {
343
+ return <div class="_fc-group-container" key={key}>
344
+ <div class="_fc-group-header">
345
+ {title === false ? <div class="_fc-group-idx">{index + 1}</div> : null}
346
+ {title !== false ? <div class="_fc-group-title">{title}</div> : null}
347
+ <div class="_fc-group-handle fc-clock">
348
+ {(btn.length) ? btn : null}
349
+ </div>
350
+ </div>
351
+ <div class="_fc-group-content">
352
+ <Type
353
+ key={key}
354
+ {...{
355
+ ...this.$attrs,
356
+ disabled,
357
+ 'onUpdate:modelValue': (formData) => this.formData(key, formData),
358
+ 'onUpdate:api': ($f) => this.add$f(index, key, $f),
359
+ inFor: true,
360
+ modelValue: this.field ? {[this.field]: this._value(this.modelValue[index])} : this.modelValue[index],
361
+ rule,
362
+ option: options,
363
+ extendOption: true
364
+ }}
365
+ />
366
+ </div>
367
+ </div>
368
+ } else {
369
+ return <div class="_fc-group-container" key={key}>
370
+ <Type
371
+ key={key}
372
+ {...{
373
+ ...this.$attrs,
374
+ disabled,
375
+ 'onUpdate:modelValue': (formData) => this.formData(key, formData),
376
+ 'onUpdate:api': ($f) => this.add$f(index, key, $f),
377
+ inFor: true,
378
+ modelValue: this.field ? {[this.field]: this._value(this.modelValue[index])} : this.modelValue[index],
379
+ rule,
380
+ option: options,
381
+ extendOption: true
382
+ }}
383
+ />
384
+ <div class="_fc-group-idx">{index + 1}</div>
385
+ {(btn.length) ? <div class="_fc-group-handle fc-clock">{btn}</div> : null}
386
+ </div>
387
+ }
310
388
  });
311
- return <div key={'con'} class={'_fc-group ' + (disabled ? '_fc-group-disabled' : '')}>{children}</div>
389
+ return <div key={'con'} class={'_fc-group ' + (disabled ? '_fc-group-disabled' : '') + (isCardType ? ' _fc-group-card' : '')}>{children}</div>
312
390
  }
313
391
  });
package/src/style.css CHANGED
@@ -49,11 +49,9 @@
49
49
  display: flex;
50
50
  flex-direction: column;
51
51
  border: 1px dashed #d9d9d9;
52
- padding: 20px;
53
- margin: 5px;
54
52
  border-radius: 5px;
55
- margin-bottom: 25px;
56
- padding-bottom: 25px;
53
+ margin: 5px 5px 25px;
54
+ padding: 20px 20px 25px;
57
55
  }
58
56
 
59
57
  ._fc-group-arrow {
@@ -123,3 +121,326 @@
123
121
  width: 50%;
124
122
  }
125
123
 
124
+ /* ===== Card 类型样式 ===== */
125
+ /* 主容器 */
126
+ ._fc-group._fc-group-card {
127
+ display: flex;
128
+ flex-direction: column;
129
+ width: 100%;
130
+ gap: 16px;
131
+ }
132
+
133
+ /* 禁用状态 */
134
+ ._fc-group._fc-group-card._fc-group-disabled ._fc-group-btn,
135
+ ._fc-group._fc-group-card._fc-group-disabled ._fc-group-add {
136
+ cursor: not-allowed;
137
+ opacity: 0.5;
138
+ }
139
+
140
+ /* 卡片容器 */
141
+ ._fc-group._fc-group-card ._fc-group-container {
142
+ position: relative;
143
+ background: #ffffff;
144
+ border: 1px solid #f0f0f0;
145
+ border-radius: 6px;
146
+ overflow: hidden;
147
+ display: flex;
148
+ flex-direction: column;
149
+ padding: 0;
150
+ margin: 0;
151
+ }
152
+
153
+ /* 卡片头部 */
154
+ ._fc-group._fc-group-card ._fc-group-header {
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: space-between;
158
+ padding: 16px 20px 12px;
159
+ border-bottom: 1px solid #f0f0f0;
160
+ background: #fafafa;
161
+ }
162
+
163
+ /* 序号标签 */
164
+ ._fc-group._fc-group-card ._fc-group-idx {
165
+ display: flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ width: 24px;
169
+ height: 24px;
170
+ background: #f0f0f0;
171
+ color: #666666;
172
+ border-radius: 6px;
173
+ font-size: 12px;
174
+ font-weight: 500;
175
+ margin-right: 12px;
176
+ flex-shrink: 0;
177
+ position: static;
178
+ border: 1px solid #e0e0e0;
179
+ }
180
+
181
+ /* 标题区域 */
182
+ ._fc-group._fc-group-card ._fc-group-title {
183
+ flex: 1;
184
+ font-size: 14px;
185
+ font-weight: 500;
186
+ color: rgba(0, 0, 0, 0.85);
187
+ line-height: 1.5715;
188
+ }
189
+
190
+ /* 操作按钮组 - 头部右侧 */
191
+ ._fc-group._fc-group-card ._fc-group-handle {
192
+ display: flex;
193
+ align-items: center;
194
+ gap: 4px;
195
+ margin-left: 12px;
196
+ flex-shrink: 0;
197
+ position: static;
198
+ bottom: auto;
199
+ right: auto;
200
+ border-radius: 0;
201
+ border: none;
202
+ padding: 0;
203
+ background-color: transparent;
204
+ flex-direction: row;
205
+ }
206
+
207
+ /* 按钮基础样式 */
208
+ ._fc-group._fc-group-card ._fc-group-btn {
209
+ cursor: pointer;
210
+ display: flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ width: 24px;
214
+ height: 24px;
215
+ border-radius: 4px;
216
+ border: 1px solid #d9d9d9;
217
+ background: #ffffff;
218
+ color: rgba(0, 0, 0, 0.45);
219
+ position: relative;
220
+ }
221
+
222
+ ._fc-group._fc-group-card ._fc-group-btn:hover {
223
+ color: #1890ff;
224
+ border-color: #1890ff;
225
+ background: #f0f8ff;
226
+ }
227
+
228
+ ._fc-group._fc-group-card ._fc-group-btn:active {
229
+ color: #096dd9;
230
+ border-color: #096dd9;
231
+ }
232
+
233
+ /* 箭头按钮 */
234
+ ._fc-group._fc-group-card ._fc-group-arrow {
235
+ position: relative;
236
+ }
237
+
238
+ ._fc-group._fc-group-card ._fc-group-arrow::before {
239
+ content: "";
240
+ position: absolute;
241
+ top: 50%;
242
+ left: 50%;
243
+ width: 5px;
244
+ height: 5px;
245
+ border-left: 1px solid currentColor;
246
+ border-top: 1px solid currentColor;
247
+ transform: translate(-50%, -50%) rotate(45deg);
248
+ }
249
+
250
+ ._fc-group._fc-group-card ._fc-group-arrow._fc-group-down {
251
+ transform: rotate(180deg);
252
+ }
253
+
254
+ /* 合并的上下箭头按钮 */
255
+ ._fc-group._fc-group-card ._fc-group-sort {
256
+ position: relative;
257
+ display: flex;
258
+ flex-direction: column;
259
+ align-items: center;
260
+ justify-content: center;
261
+ height: 24px;
262
+ }
263
+
264
+ ._fc-group._fc-group-card ._fc-group-sort::before,
265
+ ._fc-group._fc-group-card ._fc-group-sort::after {
266
+ content: "";
267
+ position: absolute;
268
+ left: 50%;
269
+ width: 4px;
270
+ height: 4px;
271
+ border-left: 1px solid currentColor;
272
+ border-top: 1px solid currentColor;
273
+ transform: translateX(-50%);
274
+ }
275
+
276
+ ._fc-group._fc-group-card ._fc-group-sort::before {
277
+ top: 6px;
278
+ transform: translateX(-50%) rotate(45deg);
279
+ }
280
+
281
+ ._fc-group._fc-group-card ._fc-group-sort::after {
282
+ bottom: 6px;
283
+ transform: translateX(-50%) rotate(225deg);
284
+ }
285
+
286
+ /* 合并按钮的上下点击区域 */
287
+ ._fc-group._fc-group-card ._fc-group-sort-up,
288
+ ._fc-group._fc-group-card ._fc-group-sort-down {
289
+ position: absolute;
290
+ left: 0;
291
+ right: 0;
292
+ height: 12px;
293
+ cursor: pointer;
294
+ z-index: 1;
295
+ }
296
+
297
+ ._fc-group._fc-group-card ._fc-group-sort-up {
298
+ top: 0;
299
+ }
300
+
301
+ ._fc-group._fc-group-card ._fc-group-sort-down {
302
+ bottom: 0;
303
+ }
304
+
305
+ ._fc-group._fc-group-card ._fc-group-sort-up:hover,
306
+ ._fc-group._fc-group-card ._fc-group-sort-down:hover {
307
+ background: rgba(24, 144, 255, 0.1);
308
+ }
309
+
310
+ /* 加减按钮 */
311
+ ._fc-group._fc-group-card ._fc-group-plus-minus {
312
+ position: relative;
313
+ background: #409eff;
314
+ border-color: #409eff;
315
+ color: white;
316
+ width: 24px;
317
+ height: 24px;
318
+ }
319
+
320
+ ._fc-group._fc-group-card ._fc-group-plus-minus:hover {
321
+ background: #66b1ff;
322
+ border-color: #66b1ff;
323
+ color: white;
324
+ }
325
+
326
+ ._fc-group._fc-group-card ._fc-group-plus-minus::before,
327
+ ._fc-group._fc-group-card ._fc-group-plus-minus::after {
328
+ content: "";
329
+ position: absolute;
330
+ top: 50%;
331
+ left: 50%;
332
+ width: 8px;
333
+ height: 1px;
334
+ background-color: currentColor;
335
+ transform: translate(-50%, -50%);
336
+ }
337
+
338
+ ._fc-group._fc-group-card ._fc-group-plus-minus::before {
339
+ transform: translate(-50%, -50%) rotate(90deg);
340
+ }
341
+
342
+ ._fc-group._fc-group-card ._fc-group-plus-minus._fc-group-minus {
343
+ background: #f56c6c;
344
+ border-color: #f56c6c;
345
+ }
346
+
347
+ ._fc-group._fc-group-card ._fc-group-plus-minus._fc-group-minus:hover {
348
+ background: #f78989;
349
+ border-color: #f78989;
350
+ }
351
+
352
+ ._fc-group._fc-group-card ._fc-group-plus-minus._fc-group-minus::before {
353
+ display: none;
354
+ }
355
+
356
+ /* 表单内容区域 */
357
+ ._fc-group._fc-group-card ._fc-group-content {
358
+ padding: 20px;
359
+ }
360
+
361
+ /* 添加按钮 - 与加号按钮风格一致 */
362
+ ._fc-group._fc-group-card ._fc-group-add {
363
+ cursor: pointer;
364
+ display: flex;
365
+ align-items: center;
366
+ justify-content: center;
367
+ width: 24px;
368
+ height: 24px;
369
+ border-radius: 4px;
370
+ border: 1px solid #d9d9d9;
371
+ background: #ffffff;
372
+ color: rgba(0, 0, 0, 0.45);
373
+ position: relative;
374
+ }
375
+
376
+ ._fc-group._fc-group-card ._fc-group-add:hover {
377
+ color: #409eff;
378
+ border-color: #409eff;
379
+ background: #f0f8ff;
380
+ }
381
+
382
+ ._fc-group._fc-group-card ._fc-group-add._fc-group-plus-minus {
383
+ width: 24px;
384
+ height: 24px;
385
+ background: #409eff;
386
+ border-color: #409eff;
387
+ color: white;
388
+ }
389
+
390
+ ._fc-group._fc-group-card ._fc-group-add._fc-group-plus-minus:hover {
391
+ background: #66b1ff;
392
+ border-color: #66b1ff;
393
+ color: white;
394
+ }
395
+
396
+ ._fc-group._fc-group-card ._fc-group-add._fc-group-plus-minus::before,
397
+ ._fc-group._fc-group-card ._fc-group-add._fc-group-plus-minus::after {
398
+ content: "";
399
+ position: absolute;
400
+ top: 50%;
401
+ left: 50%;
402
+ width: 8px;
403
+ height: 1px;
404
+ background-color: currentColor;
405
+ transform: translate(-50%, -50%);
406
+ }
407
+
408
+ ._fc-group._fc-group-card ._fc-group-add._fc-group-plus-minus::before {
409
+ transform: translate(-50%, -50%) rotate(90deg);
410
+ }
411
+
412
+ /* 空状态样式 */
413
+ ._fc-group._fc-group-card ._fc-group-empty {
414
+ text-align: center;
415
+ padding: 40px 20px;
416
+ color: rgba(0, 0, 0, 0.45);
417
+ font-size: 14px;
418
+ }
419
+
420
+ /* 响应式设计 */
421
+ @media (max-width: 768px) {
422
+ ._fc-group._fc-group-card ._fc-group-container {
423
+ margin: 0 -8px;
424
+ border-radius: 0;
425
+ border-left: none;
426
+ border-right: none;
427
+ }
428
+
429
+ ._fc-group._fc-group-card ._fc-group-header {
430
+ padding: 12px 16px 8px;
431
+ }
432
+
433
+ ._fc-group._fc-group-card ._fc-group-content {
434
+ padding: 16px;
435
+ }
436
+
437
+ ._fc-group._fc-group-card ._fc-group-handle {
438
+ gap: 2px;
439
+ }
440
+
441
+ ._fc-group._fc-group-card ._fc-group-btn {
442
+ width: 22px;
443
+ height: 22px;
444
+ }
445
+ }
446
+