@ebiz/designer-components 0.0.37 → 0.0.39
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/dist/designer-components.css +1 -1
- package/dist/index.mjs +5022 -4936
- package/package.json +1 -1
- package/src/components/senior/EbizSData/index.vue +16 -1
- package/src/components/senior/EbizSDialog/index.vue +29 -220
- package/src/components/senior/EbizSForm/README.md +158 -0
- package/src/components/senior/EbizSForm/index.vue +326 -41
- package/src/index.js +2 -2
- package/src/router/index.js +2 -2
- package/src/views/EbizSForm/index.vue +360 -0
- package/src/views/EbizSFormDemo.vue +100 -6
@@ -1,7 +1,8 @@
|
|
1
1
|
<template>
|
2
|
-
<t-form ref="formRef" :class="['ebiz-s-form', className]" :colon="colon" :data="
|
2
|
+
<t-form ref="formRef" :class="['ebiz-s-form', className]" :colon="colon" :data="localFormData" :disabled="disabled"
|
3
3
|
:label-align="labelAlign" :label-width="labelWidth" :layout="layout" :reset-type="resetType"
|
4
|
-
:reset-on-semi-controlled="resetOnSemiControlled" :rules="
|
4
|
+
:reset-on-semi-controlled="resetOnSemiControlled" :rules="formRules"
|
5
|
+
:scroll-to-first-error="scrollToFirstError"
|
5
6
|
:show-error-message="showErrorMessage" :status-icon="statusIcon" :style="customStyle" @reset="handleReset"
|
6
7
|
@submit="handleSubmit" @validate="handleValidate">
|
7
8
|
|
@@ -10,10 +11,12 @@
|
|
10
11
|
<ebiz-s-form-item label=" ">
|
11
12
|
<slot name="buttons">
|
12
13
|
<t-space>
|
13
|
-
<t-button v-if="showCancelButton" :theme="cancelButtonTheme" type="reset"
|
14
|
-
|
14
|
+
<t-button v-if="showCancelButton" :theme="cancelButtonTheme" type="reset" :loading="loading"
|
15
|
+
:disabled="loading || disabled">{{
|
16
|
+
cancelButtonText
|
15
17
|
}}</t-button>
|
16
|
-
<t-button v-if="showSubmitButton" theme="primary" :loading="loading"
|
18
|
+
<t-button v-if="showSubmitButton" theme="primary" :loading="loading"
|
19
|
+
:disabled="loading || disabled" type="submit">{{
|
17
20
|
submitButtonText }}</t-button>
|
18
21
|
</t-space>
|
19
22
|
</slot>
|
@@ -34,7 +37,7 @@ export default {
|
|
34
37
|
</script>
|
35
38
|
|
36
39
|
<script setup>
|
37
|
-
import { defineProps, defineEmits, ref } from 'vue';
|
40
|
+
import { defineProps, defineEmits, ref, watch, computed } from 'vue';
|
38
41
|
import { Form as TForm, Button as TButton, Space as TSpace, MessagePlugin } from 'tdesign-vue-next';
|
39
42
|
import dataService from "../../../apiService/simpleDataService";
|
40
43
|
import EbizSFormItem from './item.vue';
|
@@ -193,6 +196,13 @@ const props = defineProps({
|
|
193
196
|
type: Object,
|
194
197
|
default: () => null
|
195
198
|
},
|
199
|
+
/**
|
200
|
+
* 详情API配置,用于加载表单数据
|
201
|
+
*/
|
202
|
+
detailApiConfig: {
|
203
|
+
type: Object,
|
204
|
+
default: () => null
|
205
|
+
},
|
196
206
|
/**
|
197
207
|
* 是否在提交成功后重置表单
|
198
208
|
*/
|
@@ -229,13 +239,61 @@ const emit = defineEmits([
|
|
229
239
|
'validate',
|
230
240
|
'success',
|
231
241
|
'error',
|
232
|
-
'cancel'
|
242
|
+
'cancel',
|
243
|
+
'detail-loaded',
|
244
|
+
'detail-error'
|
233
245
|
]);
|
234
246
|
|
235
247
|
const formRef = ref(null);
|
236
248
|
const loading = ref(false);
|
249
|
+
const currentId = ref(null);
|
250
|
+
const currentParams = ref({});
|
251
|
+
const localFormData = ref({...props.data});
|
237
252
|
|
253
|
+
// 合并rules并监听变化
|
254
|
+
const formRules = computed(() => props.rules);
|
238
255
|
|
256
|
+
// 监听props的data变化,同步到localFormData
|
257
|
+
watch(() => props.data, (newData) => {
|
258
|
+
// 如果外部传入的数据变化,同步更新本地数据
|
259
|
+
if (newData && typeof newData === 'object') {
|
260
|
+
// 清空现有数据
|
261
|
+
Object.keys(localFormData.value).forEach(key => {
|
262
|
+
delete localFormData.value[key];
|
263
|
+
});
|
264
|
+
|
265
|
+
// 写入新数据
|
266
|
+
Object.keys(newData).forEach(key => {
|
267
|
+
localFormData.value[key] = newData[key];
|
268
|
+
});
|
269
|
+
}
|
270
|
+
}, { deep: true });
|
271
|
+
|
272
|
+
/**
|
273
|
+
* 获取错误消息
|
274
|
+
* @param {Error} error 错误对象
|
275
|
+
* @param {string} prefix 错误消息前缀
|
276
|
+
* @returns {string} 格式化后的错误消息
|
277
|
+
*/
|
278
|
+
const getErrorMessage = (error, prefix = '提交失败') => {
|
279
|
+
if (!error) return `${prefix}: 未知错误`;
|
280
|
+
|
281
|
+
// 处理不同类型的错误
|
282
|
+
if (error.response && error.response.data) {
|
283
|
+
// API返回的详细错误信息
|
284
|
+
const { message, msg, error: errMsg } = error.response.data;
|
285
|
+
return `${prefix}: ${message || msg || errMsg || error.message || '服务器返回错误'}`;
|
286
|
+
} else if (error.message) {
|
287
|
+
// 标准错误对象
|
288
|
+
return `${prefix}: ${error.message}`;
|
289
|
+
} else if (typeof error === 'string') {
|
290
|
+
// 字符串错误
|
291
|
+
return `${prefix}: ${error}`;
|
292
|
+
} else {
|
293
|
+
// 未知错误
|
294
|
+
return `${prefix}: 未知错误`;
|
295
|
+
}
|
296
|
+
};
|
239
297
|
|
240
298
|
/**
|
241
299
|
* 调用API提交表单数据
|
@@ -243,30 +301,42 @@ const loading = ref(false);
|
|
243
301
|
const submitFormData = async () => {
|
244
302
|
try {
|
245
303
|
loading.value = true;
|
304
|
+
|
305
|
+
// 准备请求参数
|
306
|
+
const fetchParams = { ...localFormData.value };
|
307
|
+
|
308
|
+
// 如果有ID,添加到请求参数中
|
309
|
+
if (currentId.value) {
|
310
|
+
fetchParams.id = currentId.value;
|
311
|
+
}
|
312
|
+
|
313
|
+
// 合并当前参数到请求参数
|
314
|
+
if (currentParams.value && Object.keys(currentParams.value).length > 0) {
|
315
|
+
Object.keys(currentParams.value).forEach(key => {
|
316
|
+
fetchParams[key] = currentParams.value[key];
|
317
|
+
});
|
318
|
+
}
|
246
319
|
|
247
|
-
|
248
|
-
const response = await dataService.
|
249
|
-
url,
|
250
|
-
method: method || 'post',
|
251
|
-
data: props.data,
|
252
|
-
params,
|
253
|
-
headers
|
254
|
-
});
|
320
|
+
// 直接使用 apiConfig,不做任何处理
|
321
|
+
const response = await dataService.fetch(fetchParams, props.apiConfig);
|
255
322
|
|
323
|
+
// 直接使用响应数据
|
324
|
+
const transformedResponse = response;
|
256
325
|
if (props.showResultMessage) {
|
257
326
|
MessagePlugin.success(props.successMessage);
|
258
327
|
}
|
259
328
|
|
260
|
-
emit('success',
|
329
|
+
emit('success', transformedResponse);
|
261
330
|
|
262
331
|
if (props.resetOnSuccess && formRef.value) {
|
263
332
|
formRef.value.reset();
|
264
333
|
}
|
265
334
|
|
266
|
-
return
|
335
|
+
return transformedResponse;
|
267
336
|
} catch (error) {
|
268
337
|
if (props.showResultMessage) {
|
269
|
-
|
338
|
+
const errorMsg = getErrorMessage(error, props.errorMessage);
|
339
|
+
MessagePlugin.error(errorMsg);
|
270
340
|
}
|
271
341
|
|
272
342
|
emit('error', error);
|
@@ -277,46 +347,225 @@ const submitFormData = async () => {
|
|
277
347
|
};
|
278
348
|
|
279
349
|
/**
|
280
|
-
*
|
350
|
+
* 表单重置
|
351
|
+
* @param {Object} context 表单上下文
|
281
352
|
*/
|
282
|
-
const
|
283
|
-
|
353
|
+
const handleReset = (context) => {
|
354
|
+
emit('reset', context);
|
355
|
+
};
|
284
356
|
|
285
|
-
|
286
|
-
|
287
|
-
|
357
|
+
/**
|
358
|
+
* 表单验证
|
359
|
+
* @param {Object} result 表单验证结果
|
360
|
+
*/
|
361
|
+
const handleValidate = (result) => {
|
362
|
+
emit('validate', result);
|
363
|
+
};
|
364
|
+
|
365
|
+
/**
|
366
|
+
* 表单提交事件,由内置的HTML表单submit事件触发
|
367
|
+
*/
|
368
|
+
const handleSubmit = async (_context) => {
|
369
|
+
try {
|
370
|
+
if (!formRef.value) return;
|
371
|
+
|
372
|
+
const validateResult = await formRef.value.validate();
|
373
|
+
|
374
|
+
if (validateResult !== true) {
|
375
|
+
// 获取第一个错误信息
|
376
|
+
const firstKey = Object.keys(validateResult)[0];
|
377
|
+
const firstError = validateResult[firstKey]?.[0]?.message || '表单验证失败';
|
378
|
+
|
379
|
+
if (props.showResultMessage) {
|
380
|
+
MessagePlugin.error(firstError);
|
381
|
+
}
|
382
|
+
|
383
|
+
emit('error', { validateResult, firstError });
|
384
|
+
return;
|
385
|
+
}
|
386
|
+
|
387
|
+
emit('submit', localFormData.value);
|
388
|
+
|
389
|
+
if (props.apiConfig) {
|
390
|
+
await submitFormData();
|
391
|
+
}
|
392
|
+
} catch (error) {
|
393
|
+
if (props.showResultMessage) {
|
394
|
+
MessagePlugin.error(getErrorMessage(error));
|
395
|
+
}
|
396
|
+
|
397
|
+
emit('error', error);
|
288
398
|
}
|
399
|
+
};
|
289
400
|
|
290
|
-
|
401
|
+
/**
|
402
|
+
* 加载详情数据
|
403
|
+
* @param {string|number} id 记录ID
|
404
|
+
*/
|
405
|
+
const loadDetailData = async (id) => {
|
406
|
+
if (!props.detailApiConfig || !id) return null;
|
407
|
+
|
408
|
+
try {
|
409
|
+
loading.value = true;
|
410
|
+
|
411
|
+
// 准备请求参数
|
412
|
+
const fetchParams = { id };
|
413
|
+
|
414
|
+
// 合并当前参数到请求参数
|
415
|
+
if (currentParams.value && Object.keys(currentParams.value).length > 0) {
|
416
|
+
Object.keys(currentParams.value).forEach(key => {
|
417
|
+
fetchParams[key] = currentParams.value[key];
|
418
|
+
});
|
419
|
+
}
|
420
|
+
|
421
|
+
// 调用dataService进行API请求,直接使用detailApiConfig
|
422
|
+
const response = await dataService.fetch(fetchParams, props.detailApiConfig);
|
423
|
+
|
424
|
+
// 获取详情数据
|
425
|
+
const detailData = response.data || response;
|
426
|
+
|
427
|
+
// 更新本地表单数据
|
428
|
+
if (detailData) {
|
429
|
+
// 清空现有数据
|
430
|
+
Object.keys(localFormData.value).forEach(key => {
|
431
|
+
delete localFormData.value[key];
|
432
|
+
});
|
433
|
+
|
434
|
+
// 写入新数据
|
435
|
+
Object.keys(detailData).forEach(key => {
|
436
|
+
localFormData.value[key] = detailData[key];
|
437
|
+
});
|
438
|
+
}
|
439
|
+
|
440
|
+
// 触发加载成功事件
|
441
|
+
emit('detail-loaded', detailData);
|
442
|
+
return detailData;
|
443
|
+
} catch (error) {
|
444
|
+
// 显示错误消息
|
445
|
+
if (props.showResultMessage) {
|
446
|
+
MessagePlugin.error(`加载详情失败: ${error.message || '未知错误'}`);
|
447
|
+
}
|
448
|
+
|
449
|
+
// 触发错误事件
|
450
|
+
emit('detail-error', error);
|
451
|
+
return null;
|
452
|
+
} finally {
|
453
|
+
loading.value = false;
|
454
|
+
}
|
455
|
+
};
|
291
456
|
|
292
|
-
|
293
|
-
|
457
|
+
/**
|
458
|
+
* 打开表单并设置参数
|
459
|
+
* @param {Object} params 表单参数
|
460
|
+
*/
|
461
|
+
const openForm = (params = {}) => {
|
462
|
+
// 处理传入的表单数据
|
463
|
+
if (params.formData) {
|
464
|
+
// 清空现有数据
|
465
|
+
Object.keys(localFormData.value).forEach(key => {
|
466
|
+
delete localFormData.value[key];
|
467
|
+
});
|
468
|
+
|
469
|
+
// 写入新数据
|
470
|
+
Object.keys(params.formData).forEach(key => {
|
471
|
+
localFormData.value[key] = params.formData[key];
|
472
|
+
});
|
473
|
+
} else if (params.clearData) {
|
474
|
+
// 如果要求清空数据
|
475
|
+
Object.keys(localFormData.value).forEach(key => {
|
476
|
+
delete localFormData.value[key];
|
477
|
+
});
|
478
|
+
}
|
479
|
+
|
480
|
+
// 存储ID
|
481
|
+
if (params.id) {
|
482
|
+
currentId.value = params.id;
|
483
|
+
|
484
|
+
// 如果有detailApiConfig,加载详情数据
|
485
|
+
if (props.detailApiConfig) {
|
486
|
+
loadDetailData(params.id);
|
487
|
+
}
|
488
|
+
} else {
|
489
|
+
currentId.value = null;
|
294
490
|
}
|
491
|
+
|
492
|
+
// 存储其他自定义参数
|
493
|
+
currentParams.value = params;
|
494
|
+
|
495
|
+
return { success: true, message: '表单已准备就绪' };
|
295
496
|
};
|
296
497
|
|
297
498
|
/**
|
298
|
-
*
|
299
|
-
* @param {Object}
|
499
|
+
* 设置表单数据
|
500
|
+
* @param {Object} data 要设置的数据
|
300
501
|
*/
|
301
|
-
const
|
302
|
-
|
502
|
+
const setFormData = (data) => {
|
503
|
+
if (!data || typeof data !== 'object') {
|
504
|
+
return { success: false, message: '数据必须是一个对象' };
|
505
|
+
}
|
506
|
+
|
507
|
+
// 清空现有数据
|
508
|
+
Object.keys(localFormData.value).forEach(key => {
|
509
|
+
delete localFormData.value[key];
|
510
|
+
});
|
511
|
+
|
512
|
+
// 写入新数据
|
513
|
+
Object.keys(data).forEach(key => {
|
514
|
+
localFormData.value[key] = data[key];
|
515
|
+
});
|
516
|
+
|
517
|
+
return { success: true, message: '表单数据已设置' };
|
303
518
|
};
|
304
519
|
|
305
520
|
/**
|
306
|
-
*
|
307
|
-
* @param {Object} result 表单验证结果
|
521
|
+
* 获取表单数据
|
308
522
|
*/
|
309
|
-
const
|
310
|
-
|
523
|
+
const getFormData = () => {
|
524
|
+
return { ...localFormData.value };
|
311
525
|
};
|
312
526
|
|
527
|
+
/**
|
528
|
+
* 清空表单数据
|
529
|
+
*/
|
530
|
+
const clearFormData = () => {
|
531
|
+
// 清空表单数据
|
532
|
+
Object.keys(localFormData.value).forEach(key => {
|
533
|
+
delete localFormData.value[key];
|
534
|
+
});
|
535
|
+
|
536
|
+
emit('reset', { type: 'clear' });
|
537
|
+
return { success: true, message: '表单数据已清空' };
|
538
|
+
};
|
313
539
|
|
314
540
|
/**
|
315
|
-
*
|
541
|
+
* 提交表单
|
316
542
|
*/
|
317
|
-
const
|
318
|
-
|
319
|
-
|
543
|
+
const submitForm = async () => {
|
544
|
+
try {
|
545
|
+
if (!formRef.value) {
|
546
|
+
return { success: false, message: '表单引用不存在' };
|
547
|
+
}
|
548
|
+
|
549
|
+
const validateResult = await formRef.value.validate();
|
550
|
+
|
551
|
+
if (validateResult === true) {
|
552
|
+
emit('submit', localFormData.value);
|
553
|
+
|
554
|
+
if (props.apiConfig) {
|
555
|
+
const response = await submitFormData();
|
556
|
+
return { success: true, message: '提交成功', data: response };
|
557
|
+
}
|
558
|
+
|
559
|
+
return { success: true, message: '表单验证通过', data: { ...localFormData.value } };
|
560
|
+
} else {
|
561
|
+
const firstKey = Object.keys(validateResult)[0];
|
562
|
+
const firstError = validateResult[firstKey]?.[0]?.message || '表单验证失败';
|
563
|
+
|
564
|
+
return { success: false, message: firstError, errors: validateResult };
|
565
|
+
}
|
566
|
+
} catch (error) {
|
567
|
+
return { success: false, message: error.message || '提交过程中发生错误' };
|
568
|
+
}
|
320
569
|
};
|
321
570
|
|
322
571
|
// 暴露方法给父组件
|
@@ -324,7 +573,7 @@ defineExpose({
|
|
324
573
|
/**
|
325
574
|
* 提交表单方法
|
326
575
|
*/
|
327
|
-
submit:
|
576
|
+
submit: submitForm,
|
328
577
|
/**
|
329
578
|
* 重置表单方法
|
330
579
|
*/
|
@@ -366,7 +615,43 @@ defineExpose({
|
|
366
615
|
/**
|
367
616
|
* 获取表单DOM元素
|
368
617
|
*/
|
369
|
-
getFormElement: () => formRef.value
|
618
|
+
getFormElement: () => formRef.value,
|
619
|
+
/**
|
620
|
+
* 打开表单并设置参数
|
621
|
+
*/
|
622
|
+
openForm,
|
623
|
+
/**
|
624
|
+
* 加载详情数据
|
625
|
+
*/
|
626
|
+
loadDetailData,
|
627
|
+
/**
|
628
|
+
* 清空表单数据
|
629
|
+
*/
|
630
|
+
clearFormData,
|
631
|
+
/**
|
632
|
+
* 设置表单数据
|
633
|
+
*/
|
634
|
+
setFormData,
|
635
|
+
/**
|
636
|
+
* 获取表单数据
|
637
|
+
*/
|
638
|
+
getFormData,
|
639
|
+
/**
|
640
|
+
* 表单引用
|
641
|
+
*/
|
642
|
+
formRef,
|
643
|
+
/**
|
644
|
+
* 当前ID
|
645
|
+
*/
|
646
|
+
currentId,
|
647
|
+
/**
|
648
|
+
* 当前参数
|
649
|
+
*/
|
650
|
+
currentParams,
|
651
|
+
/**
|
652
|
+
* 本地表单数据
|
653
|
+
*/
|
654
|
+
localFormData
|
370
655
|
});
|
371
656
|
</script>
|
372
657
|
|
package/src/index.js
CHANGED
@@ -85,8 +85,8 @@ import { LoadingPlugin as EbizLoading } from 'tdesign-vue-next';
|
|
85
85
|
import EbizMap from './components/EbizMap.vue';
|
86
86
|
|
87
87
|
import EbizSData from './components/senior/EbizSData/index.vue';
|
88
|
-
import EbizSForm from
|
89
|
-
import EbizSFormItem from
|
88
|
+
import EbizSForm from './components/senior/EbizSForm/index.vue';
|
89
|
+
import EbizSFormItem from './components/senior/EbizSForm/item.vue';
|
90
90
|
import EbizSDialog from "./components/senior/EbizSDialog/index.vue";
|
91
91
|
|
92
92
|
// 导出组件
|
package/src/router/index.js
CHANGED
@@ -3,6 +3,7 @@ import Home from '../views/Home.vue'
|
|
3
3
|
import ButtonView from '../views/Button.vue'
|
4
4
|
import TableView from '../views/TableView.vue'
|
5
5
|
import TdesignDescriptions from '../views/TdesignDescriptions.vue'
|
6
|
+
import EbizSForm from '../views/EbizSForm/index.vue'
|
6
7
|
|
7
8
|
const routes = [
|
8
9
|
{
|
@@ -320,8 +321,7 @@ const routes = [
|
|
320
321
|
{
|
321
322
|
path: '/ebiz-s-form',
|
322
323
|
name: 'EbizSForm',
|
323
|
-
component:
|
324
|
-
meta: { title: 'PC端表单组件示例' }
|
324
|
+
component: EbizSForm
|
325
325
|
},
|
326
326
|
{
|
327
327
|
path: '/ebiz-map',
|