resolver-egretimp-plus 0.1.139 → 0.1.141

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,1216 @@
1
+ # Sa-Egretimp Resolver 使用手册
2
+
3
+ ## 目录
4
+
5
+ - [项目概述](#项目概述)
6
+ - [核心架构](#核心架构)
7
+ - [快速开始](#快速开始)
8
+ - [配置结构](#配置结构)
9
+ - [组件系统](#组件系统)
10
+ - [规则引擎](#规则引擎)
11
+ - [事件编排](#事件编排)
12
+ - [API 参考](#api-参考)
13
+ - [高级用法](#高级用法)
14
+ - [最佳实践](#最佳实践)
15
+
16
+ ---
17
+
18
+ ## 项目概述
19
+
20
+ **Sa-Egretimp Resolver** 是一个基于 JSON 配置驱动的动态表单/页面渲染器,基于 Vue 3 + Element Plus 构建。它支持:
21
+
22
+ - 🎯 **配置驱动**:通过 JSON 配置动态渲染页面,无需编写模板代码
23
+ - 🔄 **双向数据绑定**:通过 `v-model` 实现数据同步
24
+ - 📐 **响应式布局**:支持栅格系统、Tab、折叠面板、循环列表等复杂布局
25
+ - 🎭 **多模式支持**:操作模式(operate)、详情模式(detail)、日志模式(log)
26
+ - 🌐 **国际化**:支持中英文切换
27
+ - ⚡ **规则引擎**:支持字段联动、显隐控制、禁用控制等动态规则
28
+ - 🔌 **事件编排**:支持按钮点击后的服务调用、弹窗打开、数据校验等复杂交互
29
+ - 📱 **双端支持**:同时支持 PC 端(Web)和移动端(H5)
30
+
31
+ ### 技术栈
32
+
33
+ - **框架**:Vue 3 (Composition API)
34
+ - **UI 库**:Element Plus (PC端) / 自定义 H5 组件库 (移动端)
35
+ - **构建工具**:Webpack + Babel
36
+ - **规则引擎**:json-rules-engine
37
+ - **工具库**:lodash-es, dayjs, axios
38
+
39
+ ---
40
+
41
+ ## 核心架构
42
+
43
+ ### 渲染流程
44
+
45
+ ```
46
+ 配置数据 (JSON)
47
+
48
+ parsePageConfig() - 解析配置,构建组件树
49
+
50
+ normalConfig() - 标准化配置,处理嵌套、循环、规则
51
+
52
+ Renderer - 遍历配置,动态渲染组件
53
+
54
+ AnalysisComponent - 分析单个配置项,绑定属性、事件、规则
55
+
56
+ 实际组件 (Element Plus / 自定义组件)
57
+ ```
58
+
59
+ ### 核心模块
60
+
61
+ | 模块 | 路径 | 职责 |
62
+ |------|------|------|
63
+ | **Resolver** | `src/index.jsx` | 根组件,提供全局依赖注入 |
64
+ | **Renderer** | `src/renderer.jsx` | 配置渲染器,遍历配置生成 VNode |
65
+ | **AnalysisComponent** | `src/analysisComponent.jsx` | 组件分析器,处理属性绑定、事件、规则 |
66
+ | **pageConfig Hook** | `src/hooks/pageConfig.js` | 配置加载与解析 |
67
+ | **rulesDriver** | `src/rules/rulesDriver.js` | 规则驱动引擎 |
68
+ | **eventOrchestration** | `src/components/helper/eventOrchestration.js` | 事件编排(服务调用、弹窗等) |
69
+
70
+ ### 数据流
71
+
72
+ ```
73
+ props.config (配置) ──────────────────────────────┐
74
+
75
+ props.modelValue (数据) ─→ dynamicMapComp (配置-数据映射) ←→ 组件 refValue
76
+
77
+ rulesDriver (规则监听)
78
+
79
+ 触发显隐/禁用/赋值等操作
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 快速开始
85
+
86
+ ### 1. 安装
87
+
88
+ ```bash
89
+ npm install resolver-egretimp-plus
90
+ ```
91
+
92
+ ### 2. 基础使用
93
+
94
+ ```vue
95
+ <template>
96
+ <Resolver
97
+ :config="pageConfig"
98
+ v-model="formData"
99
+ :mode="mode"
100
+ :selects="selectOptions"
101
+ :axiosInstance="axios"
102
+ :messageInstance="ElMessage"
103
+ :loadingInstance="loadingService"
104
+ />
105
+ </template>
106
+
107
+ <script setup>
108
+ import { ref } from 'vue'
109
+ import Resolver from 'resolver-egretimp-plus/web'
110
+ import { ElMessage } from 'element-plus'
111
+ import axios from 'axios'
112
+
113
+ // 页面配置(从后端获取或本地定义)
114
+ const pageConfig = ref({
115
+ metaType: 'CustomComponentFormLayout',
116
+ pmPageMetaList: [
117
+ {
118
+ metaCode: 'username',
119
+ metaNameZh: '用户名',
120
+ metaType: 'input',
121
+ requiredFlag: '1',
122
+ span: 12
123
+ },
124
+ {
125
+ metaCode: 'status',
126
+ metaNameZh: '状态',
127
+ metaType: 'select',
128
+ selectKey: 'status_options',
129
+ span: 12
130
+ }
131
+ ]
132
+ })
133
+
134
+ // 表单数据
135
+ const formData = ref({})
136
+
137
+ // 模式:operate | detail | log
138
+ const mode = ref('operate')
139
+
140
+ // 下拉选项
141
+ const selectOptions = ref({
142
+ status_options: [
143
+ { label: '启用', value: '1' },
144
+ { label: '禁用', value: '0' }
145
+ ]
146
+ })
147
+
148
+ // 获取 ref
149
+ import { ref } from 'vue'
150
+ const resolverRef = ref(null)
151
+
152
+ // 校验表单
153
+ const handleSubmit = async () => {
154
+ await resolverRef.value.validate((valid) => {
155
+ if (valid) {
156
+ console.log('表单数据:', formData.value)
157
+ }
158
+ })
159
+ }
160
+ </script>
161
+ ```
162
+
163
+ ### 3. 从后端加载配置
164
+
165
+ ```vue
166
+ <script setup>
167
+ import { useBuildInData } from 'resolver-egretimp-plus/web'
168
+ import { ElMessage, ElLoading } from 'element-plus'
169
+
170
+ const { pageConfig, getPageConfig, selects } = useBuildInData({
171
+ messageInstance: ElMessage,
172
+ loadingInstance: ElLoading.service(),
173
+ requestTraceId: 'unique-trace-id',
174
+ lang: 'zh',
175
+ isH5: false
176
+ })
177
+
178
+ // 获取配置
179
+ getPageConfig({
180
+ busiIdentityId: 'your-business-id',
181
+ pageId: 'your-page-id'
182
+ }, {
183
+ configCb: (config) => {
184
+ console.log('配置加载完成', config)
185
+ },
186
+ selectsCb: (selects) => {
187
+ console.log('枚举加载完成', selects)
188
+ }
189
+ })
190
+ </script>
191
+
192
+ <template>
193
+ <Resolver
194
+ :config="pageConfig"
195
+ v-model="formData"
196
+ :selects="selects"
197
+ />
198
+ </template>
199
+ ```
200
+
201
+ ---
202
+
203
+ ## 配置结构
204
+
205
+ ### 配置层级
206
+
207
+ ```json
208
+ {
209
+ "metaType": "CustomComponentFormLayout", // 根节点类型
210
+ "span": 24, // 栅格占位
211
+ "labelWidth": "84px", // 标签宽度
212
+ "pmPageMetaList": [ // 子组件配置列表
213
+ {
214
+ "metaCode": "field_code", // 字段编码(唯一标识)
215
+ "metaNameZh": "字段名称", // 中文名称
216
+ "metaType": "input", // 组件类型
217
+ "requiredFlag": "1", // 是否必填:1-是,0-否
218
+ "editFlag": "1", // 是否可编辑:1-是,0-否
219
+ "span": 12, // 栅格占位(1-24)
220
+ "seqNo": 1, // 排序号
221
+ "displayType": "1", // 显示类型:1-显示,0-隐藏
222
+ "hireRelat": [] // 关联规则
223
+ }
224
+ ]
225
+ }
226
+ ```
227
+
228
+ ### 常用 metaType 映射
229
+
230
+ | metaType | 渲染组件 | 说明 |
231
+ |----------|----------|------|
232
+ | `input` | `ElInput` | 文本输入框 |
233
+ | `textarea` | `ElInput` (type="textarea") | 多行文本 |
234
+ | `select` | `ElSelect` | 下拉选择 |
235
+ | `radio` | `ElRadio` | 单选框 |
236
+ | `checkbox` | `ElCheckbox` | 复选框 |
237
+ | `date` | `ElDatePicker` | 日期选择 |
238
+ | `date-range` | `ElDatePicker` (type="daterange") | 日期范围 |
239
+ | `time` | `ElTimePicker` | 时间选择 |
240
+ | `number` | `ElInputNumber` | 数字输入 |
241
+ | `button` | `ElButton` | 按钮 |
242
+ | `static-text` | `ElText` | 静态文本 |
243
+ | `table` | `CustomComponentTable` | 表格 |
244
+ | `tab` | `CustomComponentTabs` | 标签页 |
245
+ | `tab-pane` | `CustomComponentTabPane` | 标签页面板 |
246
+ | `collapse` | `CustomComponentCollapse` | 折叠面板 |
247
+ | `card` | `CustomComponentCollapse` | 卡片 |
248
+ | `cycle` | `CustomComponentCycle` | 循环列表 |
249
+ | `tree` | `CustomComponentTree` | 树形控件 |
250
+ | `pagination` | `ElPagination` | 分页 |
251
+
252
+ ### 布局配置
253
+
254
+ #### 栅格布局
255
+
256
+ ```json
257
+ {
258
+ "metaType": "CustomComponentFormLayout",
259
+ "inline": true,
260
+ "pmPageMetaList": [
261
+ {
262
+ "metaCode": "field1",
263
+ "span": 12, // 占用 12/24 = 50% 宽度
264
+ "offset": 0 // 左侧偏移格数
265
+ },
266
+ {
267
+ "metaCode": "field2",
268
+ "md": 8, // 中等屏幕占 8 格
269
+ "lg": 6 // 大屏幕占 6 格
270
+ }
271
+ ]
272
+ }
273
+ ```
274
+
275
+ #### Tab 标签页
276
+
277
+ ```json
278
+ {
279
+ "metaCode": "main_tabs",
280
+ "metaType": "tab",
281
+ "pmPageMetaList": [
282
+ {
283
+ "metaCode": "tab1",
284
+ "metaType": "tab-pane",
285
+ "labelZh": "基本信息",
286
+ "defaultShowFlag": "1", // 默认显示
287
+ "pmPageMetaList": [
288
+ // tab1 的字段配置
289
+ ]
290
+ },
291
+ {
292
+ "metaCode": "tab2",
293
+ "metaType": "tab-pane",
294
+ "labelZh": "详细信息",
295
+ "pmPageMetaList": [
296
+ // tab2 的字段配置
297
+ ]
298
+ }
299
+ ]
300
+ }
301
+ ```
302
+
303
+ #### 折叠面板
304
+
305
+ ```json
306
+ {
307
+ "metaCode": "collapse_panel",
308
+ "metaType": "collapse",
309
+ "needWrap": "1", // 是否使用卡片包裹
310
+ "pmPageMetaList": [
311
+ {
312
+ "metaCode": "section1",
313
+ "metaType": "tab-pane",
314
+ "labelZh": "基本信息",
315
+ "pmPageMetaList": []
316
+ }
317
+ ]
318
+ }
319
+ ```
320
+
321
+ #### 循环列表(动态表格行)
322
+
323
+ ```json
324
+ {
325
+ "metaCode": "detail_list",
326
+ "metaType": "cycle",
327
+ "pmPageMetaList": [
328
+ {
329
+ "metaCode": "item_name",
330
+ "metaType": "input",
331
+ "labelZh": "项目名称"
332
+ },
333
+ {
334
+ "metaCode": "item_amount",
335
+ "metaType": "number",
336
+ "labelZh": "金额"
337
+ }
338
+ ]
339
+ }
340
+ ```
341
+
342
+ 对应的数据结构:
343
+
344
+ ```javascript
345
+ {
346
+ detail_list: [
347
+ { item_name: '项目A', item_amount: 1000 },
348
+ { item_name: '项目B', item_amount: 2000 }
349
+ ]
350
+ }
351
+ ```
352
+
353
+ ---
354
+
355
+ ## 组件系统
356
+
357
+ ### 内置组件
358
+
359
+ 项目提供了丰富的内置组件,位于 `src/components/packages-web/` 目录:
360
+
361
+ #### 表单组件
362
+
363
+ - **ElInput** - 文本输入框(支持文本、密码、多行等)
364
+ - **ElSelect** - 下拉选择框
365
+ - **ElRadio** - 单选框组
366
+ - **ElCheckbox** - 复选框组
367
+ - **ElDatePicker** - 日期选择器
368
+ - **ElTimePicker** - 时间选择器
369
+ - **ElInputNumber** - 数字输入框
370
+ - **ElButton** - 按钮
371
+
372
+ #### 布局组件
373
+
374
+ - **CustomComponentFormLayout** - 表单布局容器
375
+ - **CustomComponentRow** - 行布局
376
+ - **CustomComponentCol** - 列布局
377
+ - **CustomComponentTabs** - 标签页容器
378
+ - **CustomComponentTabPane** - 标签页面板
379
+ - **CustomComponentCollapse** - 折叠面板
380
+ - **CustomComponentCycle** - 循环列表
381
+ - **CustomComponentDialog** - 对话框
382
+
383
+ #### 数据展示组件
384
+
385
+ - **CustomComponentTable** - 数据表格(支持分页、选择、排序)
386
+ - **CustomComponentTree** - 树形控件
387
+ - **CustomComponentSteps** - 步骤条
388
+ - **CustomComponentPlain** - 纯文本展示(用于表格列禁用时)
389
+
390
+ #### 功能组件
391
+
392
+ - **CustomComponentSelectEmployees** - 员工选择器
393
+ - **CustomComponentEditor** - 富文本编辑器
394
+ - **CustomComponentSendMail** - 邮件发送
395
+ - **CustomComponentFileImport** - 文件导入
396
+ - **CustomComponentFileExport** - 文件导出
397
+ - **CustomComponentInputDialog** - 输入对话框
398
+
399
+ ### 自定义组件
400
+
401
+ #### 注册自定义组件
402
+
403
+ ```vue
404
+ <script setup>
405
+ import MyCustomComponent from './MyCustomComponent.vue'
406
+
407
+ const customComponents = {
408
+ MyCustomComponent
409
+ }
410
+ </script>
411
+
412
+ <template>
413
+ <Resolver
414
+ :components="customComponents"
415
+ :config="pageConfig"
416
+ v-model="formData"
417
+ />
418
+ </template>
419
+ ```
420
+
421
+ #### 配置中使用自定义组件
422
+
423
+ ```json
424
+ {
425
+ "metaCode": "custom_field",
426
+ "metaType": "MyCustomComponent",
427
+ "labelZh": "自定义字段",
428
+ "customProp1": "value1",
429
+ "customProp2": "value2"
430
+ }
431
+ ```
432
+
433
+ #### 自定义组件规范
434
+
435
+ 自定义组件需要遵循以下规范:
436
+
437
+ ```vue
438
+ <template>
439
+ <div>
440
+ <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
441
+ </div>
442
+ </template>
443
+
444
+ <script setup>
445
+ defineProps({
446
+ modelValue: [String, Number, Object, Array],
447
+ config: Object, // 配置对象
448
+ disabled: Boolean, // 是否禁用
449
+ required: Boolean, // 是否必填
450
+ options: Array, // 选项数据
451
+ placeholder: String, // 占位符
452
+ mode: String // 当前模式:operate | detail | log
453
+ })
454
+
455
+ defineEmits(['update:modelValue'])
456
+ </script>
457
+ ```
458
+
459
+ ### 组件通用 Props
460
+
461
+ 所有组件都会自动注入以下属性:
462
+
463
+ | 属性 | 类型 | 说明 |
464
+ |------|------|------|
465
+ | `modelValue` | Any | 绑定值(v-model) |
466
+ | `config` | Object | 配置对象 |
467
+ | `disabled` | Boolean | 是否禁用 |
468
+ | `required` | Boolean | 是否必填 |
469
+ | `clearable` | Boolean | 是否可清除 |
470
+ | `options` | Array | 下拉选项 |
471
+ | `placeholder` | String | 占位符 |
472
+ | `mode` | String | 当前模式 |
473
+ | `width` | String | 组件宽度 |
474
+ | `tableData` | Array | 表格数据(表格列中) |
475
+ | `rowScope` | Object | 行数据(表格列中)`{row, $index}` |
476
+
477
+ ---
478
+
479
+ ## 规则引擎
480
+
481
+ 规则引擎用于实现字段联动、显隐控制、禁用控制、值变更等功能。
482
+
483
+ ### 规则结构
484
+
485
+ ```json
486
+ {
487
+ "relId": "rule_001",
488
+ "pmRuleConditionVOList": [
489
+ {
490
+ "conditionType": "1", // 条件类型:1-字段值
491
+ "leftCode": "status", // 左侧字段
492
+ "operator": "==", // 操作符
493
+ "rightVal": "1" // 右侧值
494
+ }
495
+ ],
496
+ "pmRuleTargetEventVoList": [
497
+ {
498
+ "eventType": "1", // 事件类型:1-显示/隐藏,2-禁用/启用,3-必填/非必填
499
+ "targetCode": "detail_field", // 目标字段
500
+ "eventValue": "1" // 事件值
501
+ }
502
+ ]
503
+ }
504
+ ```
505
+
506
+ ### 条件操作符
507
+
508
+ | 操作符 | 说明 |
509
+ |--------|------|
510
+ | `==` | 等于 |
511
+ | `!=` | 不等于 |
512
+ | `>` | 大于 |
513
+ | `<` | 小于 |
514
+ | `>=` | 大于等于 |
515
+ | `<=` | 小于等于 |
516
+ | `in` | 包含于 |
517
+ | `not in` | 不包含于 |
518
+ | `like` | 模糊匹配 |
519
+
520
+ ### 事件类型
521
+
522
+ | eventType | 说明 | eventValue |
523
+ |-----------|------|------------|
524
+ | `1` | 显示/隐藏 | `1`-显示,`0`-隐藏 |
525
+ | `2` | 禁用/启用 | `1`-禁用,`0`-启用 |
526
+ | `3` | 必填/非必填 | `1`-必填,`0`-非必填 |
527
+ | `4` | 赋值 | 目标值 |
528
+
529
+ ### 规则示例
530
+
531
+ #### 示例 1:字段显隐联动
532
+
533
+ 当 `status` 为 `1` 时,显示 `detail_field`:
534
+
535
+ ```json
536
+ {
537
+ "relId": "rule_show_detail",
538
+ "pmRuleConditionVOList": [
539
+ {
540
+ "leftCode": "status",
541
+ "operator": "==",
542
+ "rightVal": "1"
543
+ }
544
+ ],
545
+ "pmRuleTargetEventVoList": [
546
+ {
547
+ "eventType": "1",
548
+ "targetCode": "detail_field",
549
+ "eventValue": "1"
550
+ }
551
+ ]
552
+ }
553
+ ```
554
+
555
+ #### 示例 2:字段禁用联动
556
+
557
+ 当 `type` 为 `readonly` 时,禁用 `name` 字段:
558
+
559
+ ```json
560
+ {
561
+ "relId": "rule_disable_name",
562
+ "pmRuleConditionVOList": [
563
+ {
564
+ "leftCode": "type",
565
+ "operator": "==",
566
+ "rightVal": "readonly"
567
+ }
568
+ ],
569
+ "pmRuleTargetEventVoList": [
570
+ {
571
+ "eventType": "2",
572
+ "targetCode": "name",
573
+ "eventValue": "1"
574
+ }
575
+ ]
576
+ }
577
+ ```
578
+
579
+ ### 规则挂载位置
580
+
581
+ 规则配置在页面配置的 `_originConfig` 中:
582
+
583
+ ```json
584
+ {
585
+ "pmPageMetaList": [],
586
+ "_originConfig": {
587
+ "lcpPageRuleVOList": [
588
+ // 规则列表
589
+ ]
590
+ }
591
+ }
592
+ ```
593
+
594
+ ---
595
+
596
+ ## 事件编排
597
+
598
+ 事件编排用于处理按钮点击后的复杂交互,包括服务调用、弹窗打开、数据校验等。
599
+
600
+ ### 按钮配置
601
+
602
+ ```json
603
+ {
604
+ "metaCode": "submit_btn",
605
+ "metaType": "button",
606
+ "labelZh": "提交",
607
+ "lcpPageServiceMapVOList": [
608
+ {
609
+ "serviceCode": "/api/submit",
610
+ "httpMethod": "post",
611
+ "serviceType": "1", // 1-融合服务
612
+ "transactionType": "1", // 1-查询服务
613
+ "busiIdentityId": "biz_001",
614
+ "inParamMappingList": [ // 入参映射
615
+ {
616
+ "orignParam": "username",
617
+ "destParam": "userName"
618
+ }
619
+ ],
620
+ "outParamMappingList": [ // 出参映射
621
+ {
622
+ "orignParam": "result.data",
623
+ "destParam": "result_field"
624
+ }
625
+ ]
626
+ }
627
+ ]
628
+ }
629
+ ```
630
+
631
+ ### 服务调用流程
632
+
633
+ ```
634
+ 按钮点击
635
+
636
+ beforeRequestService (请求前钩子)
637
+
638
+ 发送 HTTP 请求
639
+
640
+ afterRequestService (请求后钩子)
641
+
642
+ 处理返回数据 (outParamMappingList)
643
+
644
+ 更新组件 refValue
645
+ ```
646
+
647
+ ### 钩子函数
648
+
649
+ #### beforeRequestService
650
+
651
+ 请求发送前执行,可用于修改请求参数或中断请求:
652
+
653
+ ```javascript
654
+ {
655
+ metaCode: 'submit_btn',
656
+ metaType: 'button',
657
+ beforeRequestService: ({ dynamicMapComp, reqData, service }) => {
658
+ // 修改请求参数
659
+ reqData.customField = 'custom_value'
660
+
661
+ // 返回 false 中断请求
662
+ if (!reqData.username) {
663
+ return false
664
+ }
665
+
666
+ // 返回 true 或 Promise 继续请求
667
+ return true
668
+ }
669
+ }
670
+ ```
671
+
672
+ #### afterRequestService
673
+
674
+ 请求成功后执行:
675
+
676
+ ```javascript
677
+ {
678
+ metaCode: 'submit_btn',
679
+ metaType: 'button',
680
+ afterRequestService: (response, { dynamicMapComp, reqData }) => {
681
+ console.log('请求成功', response.data)
682
+ // 可执行额外逻辑
683
+ }
684
+ }
685
+ ```
686
+
687
+ ### 弹窗配置
688
+
689
+ ```json
690
+ {
691
+ "metaCode": "open_dialog_btn",
692
+ "metaType": "button",
693
+ "labelZh": "打开弹窗",
694
+ "lcpPagePopupMapVO": {
695
+ "popupBusiIdentityId": "dialog_biz_001",
696
+ "objOrList": "0", // 0-对象,1-列表
697
+ "inParamMappingList": [ // 传入弹窗的参数
698
+ {
699
+ "orignParam": "id",
700
+ "destParam": "dialogId"
701
+ }
702
+ ],
703
+ "outParamMappingList": [ // 弹窗返回的参数
704
+ {
705
+ "orignParam": "selectedData",
706
+ "destParam": "selected_field"
707
+ }
708
+ ],
709
+ "outDisplayTrigger": "confirm_btn", // 弹窗中确认按钮编码
710
+ "closedTrigger": "close_btn" // 弹窗中关闭按钮编码
711
+ }
712
+ }
713
+ ```
714
+
715
+ ---
716
+
717
+ ## API 参考
718
+
719
+ ### Resolver 组件 Props
720
+
721
+ | 属性 | 类型 | 默认值 | 说明 |
722
+ |------|------|--------|------|
723
+ | `config` | Object | null | 页面配置对象 |
724
+ | `modelValue` | Object | null | 表单数据(v-model) |
725
+ | `mode` | String | 'operate' | 模式:operate/detail/log |
726
+ | `components` | Object | {} | 自定义组件映射 |
727
+ | `selects` | Object | {} | 下拉枚举数据 |
728
+ | `lang` | String | 'zh' | 语言:zh/en |
729
+ | `dataLoad` | Boolean | false | 是否开始执行规则初始化 |
730
+ | `buttonActions` | Object | {} | 按钮动作集合 |
731
+ | `axiosInstance` | Object/Function | null | Axios 实例 |
732
+ | `axiosConfig` | Object | null | Axios 配置(用于生成实例) |
733
+ | `messageInstance` | Object/Function | null | 消息提示实例 |
734
+ | `loadingInstance` | Object/Function | null | 加载实例 |
735
+ | `confirmInstance` | Object/Function | null | 确认框实例 |
736
+ | `openChildDialogInstance` | Function | null | 打开子弹窗实例 |
737
+ | `copyModal` | Object/Function | null | 复制弹窗实例 |
738
+ | `polyfillConfigs` | Object | {} | 配置补丁 |
739
+ | `dialogComponents` | Object | {} | 输入弹窗中的组件 |
740
+ | `loadEvnetsReq` | Object | {} | 加载服务请求参数 |
741
+ | `dialogReq` | Object | {} | 弹窗请求数据 |
742
+ | `parentRootValue` | Object | {} | 父页面根数据(弹窗中) |
743
+ | `parentDynamicMapComp` | Object | {} | 父页面配置映射(弹窗中) |
744
+ | `selectionsObj` | Object | {} | 父页面已选数据(弹窗中) |
745
+ | `requestTraceId` | String | - | 请求追踪 ID |
746
+ | `loadModuleCache` | Object | {} | 动态组件加载缓存 |
747
+ | `builtPolyfillReq` | Object | {} | 内置接口额外参数 |
748
+ | `openBpm` | Boolean | false | 是否开启 BPM 流程 |
749
+ | `bpmMessage` | Object/Function | {} | BPM 参数 |
750
+ | `bpmSubmitBtn` | String | '' | BPM 提交按钮编码 |
751
+ | `bpmActions` | Object | {} | BPM 动作配置 |
752
+ | `bpmConfigs` | Object | {} | BPM 配置 |
753
+ | `bpmBtns` | Array | [] | BPM 按钮列表 |
754
+ | `bpmBtnPosition` | Number | 3 | BPM 按钮位置 |
755
+ | `isH5` | Boolean | false | 是否 H5 模式 |
756
+ | `getNativeComps` | Function | - | 获取原生组件 |
757
+ | `loadEventsBefore` | Function | null | 加载服务前钩子 |
758
+ | `loadEventsAfter` | Function | null | 加载服务后钩子 |
759
+ | `messageCb` | Function | - | 消息回调 |
760
+
761
+ ### Resolver 组件 Events
762
+
763
+ | 事件名 | 参数 | 说明 |
764
+ |--------|------|------|
765
+ | `update:modelValue` | (value) | 数据变更 |
766
+ | `rootStoreChange` | (store) | 根存储变更 |
767
+ | `loadEvnetsCompleted` | (result) | 加载服务完成 |
768
+
769
+ ### Resolver 组件 Expose
770
+
771
+ 通过 `ref` 可访问的方法和属性:
772
+
773
+ | 属性/方法 | 类型 | 说明 |
774
+ |-----------|------|------|
775
+ | `validate` | Function | 表单校验 `(callback) => void` |
776
+ | `dynamicMapComp` | Object | 配置-数据映射对象 |
777
+ | `pageConfig` | Object | 页面配置 |
778
+ | `rootForm` | Ref | 根表单引用 |
779
+ | `getRequestTraceId` | Function | 获取请求追踪 ID |
780
+ | `toExecuteLoadServices` | Function | 执行加载服务 |
781
+ | `getBpmInstance` | Function | 获取 BPM 实例 |
782
+
783
+ ### useBuildInData Hook
784
+
785
+ 用于从后端加载配置和枚举数据:
786
+
787
+ ```javascript
788
+ import { useBuildInData } from 'resolver-egretimp-plus/web'
789
+
790
+ const {
791
+ pageConfig, // 页面配置 Ref
792
+ selects, // 枚举数据 Ref
793
+ getPageConfig, // 获取配置方法
794
+ getSelects // 获取枚举方法
795
+ } = useBuildInData({
796
+ messageInstance: ElMessage,
797
+ loadingInstance: loadingService,
798
+ requestTraceId: 'trace-id',
799
+ lang: 'zh',
800
+ isH5: false
801
+ })
802
+
803
+ // 获取配置
804
+ getPageConfig(reqData, {
805
+ configCb: (config) => {}, // 配置加载完成回调
806
+ selectsCb: (selects) => {}, // 枚举加载完成回调
807
+ selectPolyReq: {} // 枚举额外参数
808
+ })
809
+
810
+ // 获取枚举
811
+ getSelects(reqData, selectsCb)
812
+ ```
813
+
814
+ ---
815
+
816
+ ## 高级用法
817
+
818
+ ### 1. 动态组件加载
819
+
820
+ 支持从远程加载 Vue 组件:
821
+
822
+ ```json
823
+ {
824
+ "metaCode": "remote_component",
825
+ "metaType": "MyRemoteComponent",
826
+ "isCustom": true,
827
+ "fileId": "/path/to/component"
828
+ }
829
+ ```
830
+
831
+ ### 2. 配置补丁(polyfillConfigs)
832
+
833
+ 在运行时动态修改配置:
834
+
835
+ ```javascript
836
+ const polyfillConfigs = {
837
+ 'username': (config, instance, { parentRootValue, parentDynamicMapComp }) => {
838
+ return {
839
+ requiredFlag: '1',
840
+ labelZh: '用户名(必填)'
841
+ }
842
+ }
843
+ }
844
+
845
+ <Resolver :polyfillConfigs="polyfillConfigs" />
846
+ ```
847
+
848
+ ### 3. 生命周期钩子
849
+
850
+ 在配置中定义组件生命周期:
851
+
852
+ ```json
853
+ {
854
+ "metaCode": "field_with_hooks",
855
+ "metaType": "input",
856
+ "onMounted": "console.log('组件已挂载', props, selects)",
857
+ "onVnodeMounted": "function(props, vnode, selects) { ... }",
858
+ "onVnodeUnmounted": "function(props) { ... }"
859
+ }
860
+ ```
861
+
862
+ ### 4. 即时点击事件
863
+
864
+ 配置加载完成后自动执行按钮点击事件:
865
+
866
+ ```json
867
+ {
868
+ "metaCode": "auto_load_btn",
869
+ "metaType": "button",
870
+ "immediateClickEvent": true,
871
+ "hidden": "1",
872
+ "lcpPageServiceMapVOList": []
873
+ }
874
+ ```
875
+
876
+ ### 5. 表单校验
877
+
878
+ #### 基础校验
879
+
880
+ ```javascript
881
+ const resolverRef = ref(null)
882
+
883
+ resolverRef.value.validate((valid) => {
884
+ if (valid) {
885
+ console.log('校验通过')
886
+ } else {
887
+ console.log('校验失败')
888
+ }
889
+ })
890
+ ```
891
+
892
+ #### 自定义校验规则
893
+
894
+ ```json
895
+ {
896
+ "metaCode": "email",
897
+ "metaType": "input",
898
+ "validRegs": [
899
+ {
900
+ "pattern": "/^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$/",
901
+ "message": "邮箱格式不正确",
902
+ "messageEn": "Invalid email format"
903
+ }
904
+ ]
905
+ }
906
+ ```
907
+
908
+ #### 自定义校验函数
909
+
910
+ ```javascript
911
+ {
912
+ metaCode: 'custom_field',
913
+ metaType: 'input',
914
+ validators: [(rule, value, callback, config) => {
915
+ if (value === 'invalid') {
916
+ callback(new Error('值无效'))
917
+ } else {
918
+ callback()
919
+ }
920
+ }]
921
+ }
922
+ ```
923
+
924
+ ### 6. 数据驱动规则(OPEN_DATA_RULES)
925
+
926
+ 开启数据驱动规则后,规则引擎会实时监听数据变化:
927
+
928
+ ```javascript
929
+ <Resolver :open-data-rules="true" />
930
+ ```
931
+
932
+ 规则会基于 `dynamicHireRelat` 路径进行精确匹配和触发。
933
+
934
+ ### 7. 循环组件动态渲染
935
+
936
+ 循环组件(表格、cycle)会根据数据数组动态生成配置:
937
+
938
+ ```javascript
939
+ // 数据结构
940
+ {
941
+ items: [
942
+ { name: 'Item 1', price: 100 },
943
+ { name: 'Item 2', price: 200 }
944
+ ]
945
+ }
946
+
947
+ // 配置
948
+ {
949
+ metaCode: 'items',
950
+ metaType: 'cycle',
951
+ pmPageMetaList: [
952
+ { metaCode: 'name', metaType: 'input' },
953
+ { metaCode: 'price', metaType: 'number' }
954
+ ]
955
+ }
956
+ ```
957
+
958
+ 系统会为每个数组项创建独立的配置实例,路径为 `items[0]->name`, `items[1]->name` 等。
959
+
960
+ ### 8. 弹窗交互
961
+
962
+ #### 打开弹窗
963
+
964
+ ```javascript
965
+ // 父页面按钮配置
966
+ {
967
+ metaCode: 'open_select_btn',
968
+ metaType: 'button',
969
+ lcpPagePopupMapVO: {
970
+ popupBusiIdentityId: 'select_dialog',
971
+ inParamMappingList: [
972
+ { orignParam: 'type', destParam: 'dialogType' }
973
+ ],
974
+ outParamMappingList: [
975
+ { orignParam: 'selectedId', destParam: 'selected_id' }
976
+ ],
977
+ outDisplayTrigger: 'confirm_btn'
978
+ }
979
+ }
980
+ ```
981
+
982
+ #### 弹窗中间钩子
983
+
984
+ ```javascript
985
+ {
986
+ metaCode: 'open_select_btn',
987
+ metaType: 'button',
988
+ middleOpenDialog: ({ inputParams, outputParams, dynamicMapComp }) => {
989
+ // 处理弹窗返回数据
990
+ return {
991
+ selected_id: outputParams.selectedId + '_processed'
992
+ }
993
+ }
994
+ }
995
+ ```
996
+
997
+ ### 9. BPM 流程集成
998
+
999
+ 支持致远 BPM 流程集成:
1000
+
1001
+ ```vue
1002
+ <Resolver
1003
+ :open-bpm="true"
1004
+ :bpm-message="bpmMessage"
1005
+ :bpm-submit-btn="submit_btn"
1006
+ :bpm-actions="bpmActions"
1007
+ :bpm-configs="bpmConfigs"
1008
+ :bpm-btns="bpmBtns"
1009
+ />
1010
+ ```
1011
+
1012
+ ### 10. 懒加载优化
1013
+
1014
+ 组件支持懒加载,可提升首屏渲染性能:
1015
+
1016
+ ```json
1017
+ {
1018
+ "metaCode": "heavy_component",
1019
+ "metaType": "CustomComponentTable",
1020
+ "_notOpenLazy": false // 默认开启懒加载
1021
+ }
1022
+ ```
1023
+
1024
+ 自定义组件会禁用父级链路的懒加载。
1025
+
1026
+ ---
1027
+
1028
+ ## 最佳实践
1029
+
1030
+ ### 1. 配置管理
1031
+
1032
+ - **配置分离**:将不同业务场景的配置分开管理
1033
+ - **配置缓存**:使用 `requestTraceId` 标识请求,便于缓存和追踪
1034
+ - **配置校验**:加载配置后进行结构校验,避免渲染错误
1035
+
1036
+ ### 2. 性能优化
1037
+
1038
+ - **开启懒加载**:对复杂组件(表格、Tab)开启懒加载
1039
+ - **合理设置 span**:避免过度嵌套导致渲染性能下降
1040
+ - **使用 dataLoad 控制**:在数据准备好后再开启规则引擎
1041
+
1042
+ ```javascript
1043
+ const dataLoad = ref(false)
1044
+
1045
+ // 数据加载完成后
1046
+ onMounted(async () => {
1047
+ await loadData()
1048
+ dataLoad.value = true
1049
+ })
1050
+
1051
+ <Resolver :data-load="dataLoad" />
1052
+ ```
1053
+
1054
+ ### 3. 错误处理
1055
+
1056
+ - **全局错误捕获**:使用 `onErrorCaptured` 捕获组件渲染错误
1057
+ - **服务调用错误**:在 `afterRequestService` 中处理错误
1058
+ - **校验错误提示**:使用 `messageInstance` 统一错误提示
1059
+
1060
+ ### 4. 数据流管理
1061
+
1062
+ - **单向数据流**:通过 `v-model` 管理数据,避免直接修改
1063
+ - **使用 deepMerge**:合并数据时使用 `deepMerge` 避免覆盖
1064
+ - **监听 rootStoreChange**:实时监听数据变化
1065
+
1066
+ ```javascript
1067
+ <Resolver @root-store-change="handleDataChange" />
1068
+
1069
+ const handleDataChange = (data) => {
1070
+ console.log('数据变更', data)
1071
+ }
1072
+ ```
1073
+
1074
+ ### 5. 国际化
1075
+
1076
+ - **配置多语言**:在配置中使用 `labelZh` 和 `labelEn`
1077
+ - **动态切换**:通过 `lang` prop 切换语言
1078
+ - **提示语适配**:错误提示、占位符等都需要适配
1079
+
1080
+ ```json
1081
+ {
1082
+ "metaCode": "username",
1083
+ "labelZh": "用户名",
1084
+ "labelEn": "Username",
1085
+ "defPlacehold": "请输入用户名",
1086
+ "defPlaceholdEn": "Please enter username"
1087
+ }
1088
+ ```
1089
+
1090
+ ### 6. 移动端适配
1091
+
1092
+ - **使用 H5 组件**:设置 `isH5: true` 使用移动端组件
1093
+ - **简化布局**:移动端建议使用卡片、列表布局
1094
+ - **触摸优化**:按钮、输入框大小适合触摸操作
1095
+
1096
+ ```vue
1097
+ <Resolver :is-h5="true" />
1098
+ ```
1099
+
1100
+ ### 7. 调试技巧
1101
+
1102
+ - **查看 dynamicMapComp**:通过 `resolverRef.value.dynamicMapComp` 查看配置映射
1103
+ - **性能标记**:系统内置了 Performance API 标记,可在 DevTools 中查看
1104
+ - **规则调试**:在规则中添加 `console.log` 输出调试信息
1105
+
1106
+ ### 8. 安全注意事项
1107
+
1108
+ - **配置来源可信**:确保配置数据来自可信的后端接口
1109
+ - **XSS 防护**:避免在配置中使用未过滤的用户输入
1110
+ - **eval 使用**:配置中的 `onMounted` 等字符串函数会使用 eval,需谨慎使用
1111
+
1112
+ ---
1113
+
1114
+ ## 常见问题
1115
+
1116
+ ### Q1: 组件不渲染怎么办?
1117
+
1118
+ 检查以下几点:
1119
+ 1. `config` 是否正确传递
1120
+ 2. `metaType` 是否在组件库中存在
1121
+ 3. `displayType` 是否为 `1`(显示)
1122
+ 4. 是否被规则隐藏(`hidden == '1'`)
1123
+
1124
+ ### Q2: 规则不生效怎么办?
1125
+
1126
+ 检查以下几点:
1127
+ 1. `hireRelat` 是否正确配置
1128
+ 2. `dataLoad` 是否为 `true`
1129
+ 3. 规则条件是否匹配当前数据
1130
+ 4. 查看控制台是否有规则执行日志
1131
+
1132
+ ### Q3: 表单校验失败但无提示?
1133
+
1134
+ 检查以下几点:
1135
+ 1. `messageInstance` 是否正确传递
1136
+ 2. `requiredFlag` 是否为 `'1'`
1137
+ 3. `dynamicHireRelat` 路径是否正确
1138
+ 4. 自定义校验函数是否正确调用 `callback`
1139
+
1140
+ ### Q4: 弹窗数据无法回传?
1141
+
1142
+ 检查以下几点:
1143
+ 1. `outParamMappingList` 映射是否正确
1144
+ 2. `outDisplayTrigger` 按钮编码是否匹配
1145
+ 3. `middleOpenDialog` 是否正确返回数据
1146
+ 4. 弹窗中是否正确触发了确认按钮
1147
+
1148
+ ### Q5: 循环组件数据不同步?
1149
+
1150
+ 检查以下几点:
1151
+ 1. 数据路径是否正确(如 `items[0]->name`)
1152
+ 2. `rowIndex` 是否正确设置
1153
+ 3. 是否使用了正确的 `dynamicHireRelat`
1154
+
1155
+ ---
1156
+
1157
+ ## 附录
1158
+
1159
+ ### 配置字段完整参考
1160
+
1161
+ | 字段 | 类型 | 必填 | 说明 |
1162
+ |------|------|------|------|
1163
+ | `metaCode` | String | 是 | 字段编码(唯一标识) |
1164
+ | `metaNameZh` | String | 是 | 中文名称 |
1165
+ | `metaNameEn` | String | 否 | 英文名称 |
1166
+ | `metaType` | String | 是 | 组件类型 |
1167
+ | `renderby` | String | 否 | 渲染组件类型(覆盖 metaType) |
1168
+ | `requiredFlag` | String | 否 | 是否必填:1-是,0-否 |
1169
+ | `editFlag` | String | 否 | 是否可编辑:1-是,0-否 |
1170
+ | `displayType` | String | 否 | 显示类型:1-显示,0-隐藏 |
1171
+ | `hidden` | String/Boolean | 否 | 是否隐藏 |
1172
+ | `span` | Number | 否 | 栅格占位(1-24) |
1173
+ | `offset` | Number | 否 | 左侧偏移格数 |
1174
+ | `md` | Number/Object | 否 | 中等屏幕占位 |
1175
+ | `lg` | Number/Object | 否 | 大屏幕占位 |
1176
+ | `seqNo` | Number | 否 | 排序号 |
1177
+ | `labelZh` | String | 否 | 中文标签 |
1178
+ | `labelEn` | String | 否 | 英文标签 |
1179
+ | `labelWidth` | String | 否 | 标签宽度 |
1180
+ | `labelHidden` | String | 否 | 是否隐藏标签 |
1181
+ | `defPlacehold` | String | 否 | 中文占位符 |
1182
+ | `defPlaceholdEn` | String | 否 | 英文占位符 |
1183
+ | `clearableFlag` | String | 否 | 是否可清除 |
1184
+ | `selectKey` | String | 否 | 下拉选项 key |
1185
+ | `hireRelat` | Array | 否 | 关联规则 |
1186
+ | `pmPageMetaList` | Array | 否 | 子组件配置 |
1187
+ | `extendAttr` | String | 否 | 扩展属性(JSON 字符串) |
1188
+ | `defStyle` | String | 否 | 默认样式(JSON 字符串) |
1189
+ | `validRegs` | Array/Object | 否 | 校验规则 |
1190
+ | `validators` | Array | 否 | 自定义校验函数 |
1191
+ | `regexPattern` | String | 否 | 正则校验(JSON 字符串) |
1192
+ | `slots` | Object | 否 | 插槽配置 |
1193
+ | `class` | String | 否 | CSS 类名 |
1194
+ | `style` | Object | 否 | 内联样式 |
1195
+
1196
+ ### 版本历史
1197
+
1198
+ | 版本 | 日期 | 说明 |
1199
+ |------|------|------|
1200
+ | 0.1.140 | - | 当前版本 |
1201
+
1202
+ ### 相关资源
1203
+
1204
+ - **Element Plus 文档**:https://element-plus.org/
1205
+ - **Vue 3 文档**:https://cn.vuejs.org/
1206
+ - **json-rules-engine**:https://github.com/CacheControl/json-rules-engine
1207
+
1208
+ ---
1209
+
1210
+ ## 联系与支持
1211
+
1212
+ 如有问题或建议,请联系开发团队。
1213
+
1214
+ **包名**:`resolver-egretimp-plus`
1215
+ **作者**:caowb
1216
+ **许可证**:ISC