@cicctencent/formily 0.0.0-stage → 0.0.1

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.
Files changed (308) hide show
  1. package/.eslintrc.js +40 -0
  2. package/.prettierrc.js +28 -0
  3. package/.vscode/launch.json +23 -0
  4. package/CHANGELOG.md +8 -0
  5. package/README.md +22 -2
  6. package/babel.config.js +3 -0
  7. package/build/build.js +96 -0
  8. package/build/rollup.config.bundle.js +63 -0
  9. package/build/update.version.js +35 -0
  10. package/dist/index.css +2516 -0
  11. package/dist/index.js +8377 -0
  12. package/dist/schema-field/index.css +2466 -0
  13. package/dist/schema-field/index.js +8213 -0
  14. package/dist/transform/index.js +551 -0
  15. package/dist/validate/index.js +1104 -0
  16. package/dist/vue-form/index.css +2516 -0
  17. package/dist/vue-form/index.js +8359 -0
  18. package/docs/.vuepress/client.ts +15 -0
  19. package/docs/.vuepress/components/code-preview.vue +203 -0
  20. package/docs/.vuepress/components/highlight.ts +53 -0
  21. package/docs/.vuepress/config.ts +39 -0
  22. package/docs/.vuepress/util.ts +7 -0
  23. package/docs/README.md +30 -0
  24. package/docs/components/anyOf.md +26 -0
  25. package/docs/components/array-cards.md +12 -0
  26. package/docs/components/array-collapse.md +12 -0
  27. package/docs/components/array-items.md +12 -0
  28. package/docs/components/array-table.md +12 -0
  29. package/docs/components/array-tabs.md +16 -0
  30. package/docs/components/cascader.md +12 -0
  31. package/docs/components/checkbox.md +12 -0
  32. package/docs/components/event.md +27 -0
  33. package/docs/components/hotArea.md +25 -0
  34. package/docs/components/index.md +4 -0
  35. package/docs/components/input.md +30 -0
  36. package/docs/components/layout-group.md +19 -0
  37. package/docs/components/link.md +22 -0
  38. package/docs/components/oneOf.md +8 -0
  39. package/docs/components/radio.md +12 -0
  40. package/docs/components/rich-editor.md +21 -0
  41. package/docs/components/select.md +34 -0
  42. package/docs/components/space.md +11 -0
  43. package/docs/components/switch.md +20 -0
  44. package/docs/components/text.md +21 -0
  45. package/docs/components/time-picker.md +11 -0
  46. package/docs/components/transfer.md +14 -0
  47. package/docs/components/upload-button.md +28 -0
  48. package/docs/demos/any-of/index.vue +66 -0
  49. package/docs/demos/array-cards/index.vue +105 -0
  50. package/docs/demos/array-collapse/index.vue +155 -0
  51. package/docs/demos/array-items/index.vue +171 -0
  52. package/docs/demos/array-table/index.vue +115 -0
  53. package/docs/demos/array-tabs/index.vue +56 -0
  54. package/docs/demos/cascader/index.vue +297 -0
  55. package/docs/demos/checkbox/index.vue +39 -0
  56. package/docs/demos/event/index.vue +436 -0
  57. package/docs/demos/hotArea/index.vue +240 -0
  58. package/docs/demos/index.vue +36 -0
  59. package/docs/demos/input/index.vue +46 -0
  60. package/docs/demos/layout-group/index.vue +51 -0
  61. package/docs/demos/link/index.vue +32 -0
  62. package/docs/demos/radio/index.vue +36 -0
  63. package/docs/demos/rich-editor/index.vue +22 -0
  64. package/docs/demos/select/index.vue +76 -0
  65. package/docs/demos/space/index.vue +93 -0
  66. package/docs/demos/switch/index.vue +31 -0
  67. package/docs/demos/text/index.vue +38 -0
  68. package/docs/demos/time-picker/index.vue +39 -0
  69. package/docs/demos/transfer/index.vue +26 -0
  70. package/docs/demos/upload-button/index.vue +27 -0
  71. package/docs/demos/x-data/add-button-title.vue +101 -0
  72. package/docs/demos/x-data/send-message.vue +33 -0
  73. package/docs/demos/x-reactions/any-of.vue +40 -0
  74. package/docs/demos/x-reactions/x-reactions.vue +39 -0
  75. package/docs/guide/component-extend.md +23 -0
  76. package/docs/guide/extra-bury-report.md +64 -0
  77. package/docs/guide/schema.md +22 -0
  78. package/docs/guide/x-data.md +41 -0
  79. package/docs/guide/x-reactions.md +20 -0
  80. package/example/.browserslistrc +4 -0
  81. package/example/.eslintrc.js +20 -0
  82. package/example/README.md +24 -0
  83. package/example/index.html +15 -0
  84. package/example/jest.config.js +26 -0
  85. package/example/package.json +50 -0
  86. package/example/public/favicon.ico +0 -0
  87. package/example/public/index.html +17 -0
  88. package/example/src/App.vue +25 -0
  89. package/example/src/assets/logo.png +0 -0
  90. package/example/src/components/HelloWorld.vue +148 -0
  91. package/example/src/components/group/index.vue +16 -0
  92. package/example/src/components/uploadBtn/index.vue +44 -0
  93. package/example/src/components/uploadBtn/upload-icon.svg +6 -0
  94. package/example/src/layout/component/Aside.vue +39 -0
  95. package/example/src/layout/component/Main.vue +9 -0
  96. package/example/src/layout/index.vue +15 -0
  97. package/example/src/main.ts +17 -0
  98. package/example/src/router/index.ts +99 -0
  99. package/example/src/shims-vue.d.ts +6 -0
  100. package/example/src/store/index.ts +9 -0
  101. package/example/src/views/demo/any-of/data.ts +38 -0
  102. package/example/src/views/demo/any-of/index.vue +35 -0
  103. package/example/src/views/demo/array-cards/data.ts +65 -0
  104. package/example/src/views/demo/array-cards/index.vue +33 -0
  105. package/example/src/views/demo/array-items/data.ts +29 -0
  106. package/example/src/views/demo/array-items/index.vue +33 -0
  107. package/example/src/views/demo/cloud-button/data.ts +14 -0
  108. package/example/src/views/demo/cloud-button/index.vue +22 -0
  109. package/example/src/views/demo/color-group/data.ts +15 -0
  110. package/example/src/views/demo/color-group/index.vue +22 -0
  111. package/example/src/views/demo/color-picker/data.ts +21 -0
  112. package/example/src/views/demo/color-picker/index.vue +33 -0
  113. package/example/src/views/demo/datatime/data.ts +62 -0
  114. package/example/src/views/demo/datatime/index.vue +22 -0
  115. package/example/src/views/demo/event/data.ts +184 -0
  116. package/example/src/views/demo/event/index.vue +33 -0
  117. package/example/src/views/demo/hot-area/data.ts +196 -0
  118. package/example/src/views/demo/hot-area/index.vue +33 -0
  119. package/example/src/views/demo/input/data.ts +39 -0
  120. package/example/src/views/demo/input/index.vue +33 -0
  121. package/example/src/views/demo/link/data.ts +23 -0
  122. package/example/src/views/demo/link/index.vue +22 -0
  123. package/example/src/views/demo/one-of/data.ts +139 -0
  124. package/example/src/views/demo/one-of/index.vue +33 -0
  125. package/example/src/views/demo/rich-editor/data.ts +12 -0
  126. package/example/src/views/demo/rich-editor/index.vue +24 -0
  127. package/example/src/views/demo/select/data.ts +38 -0
  128. package/example/src/views/demo/select/index.vue +33 -0
  129. package/example/src/views/demo/upload-button/data1.ts +11 -0
  130. package/example/src/views/demo/upload-button/data2.ts +12 -0
  131. package/example/src/views/demo/upload-button/index.vue +42 -0
  132. package/example/tsconfig.json +32 -0
  133. package/example/vite.config.ts +12 -0
  134. package/jest.config.js +4 -0
  135. package/package.json +92 -4
  136. package/src/components/__builtins__/configs/index.ts +1 -0
  137. package/src/components/__builtins__/index.ts +2 -0
  138. package/src/components/__builtins__/shared/create-context.ts +54 -0
  139. package/src/components/__builtins__/shared/index.ts +7 -0
  140. package/src/components/__builtins__/shared/loading.ts +20 -0
  141. package/src/components/__builtins__/shared/portal.ts +43 -0
  142. package/src/components/__builtins__/shared/resolve-component.ts +22 -0
  143. package/src/components/__builtins__/shared/transform-component.ts +33 -0
  144. package/src/components/__builtins__/shared/types.ts +8 -0
  145. package/src/components/__builtins__/shared/utils.ts +35 -0
  146. package/src/components/__builtins__/styles/common.scss +22 -0
  147. package/src/components/__builtins__/styles/element-plus.scss +1094 -0
  148. package/src/components/any-of/index.ts +20 -0
  149. package/src/components/array-base/index.ts +413 -0
  150. package/src/components/array-base/style.scss +53 -0
  151. package/src/components/array-base/style.ts +2 -0
  152. package/src/components/array-cards/index.ts +253 -0
  153. package/src/components/array-cards/style.scss +33 -0
  154. package/src/components/array-cards/style.ts +7 -0
  155. package/src/components/array-collapse/index.ts +381 -0
  156. package/src/components/array-collapse/style.scss +38 -0
  157. package/src/components/array-collapse/style.ts +10 -0
  158. package/src/components/array-items/index.ts +213 -0
  159. package/src/components/array-items/style.scss +82 -0
  160. package/src/components/array-items/style.ts +4 -0
  161. package/src/components/array-table/index.ts +567 -0
  162. package/src/components/array-table/style.scss +84 -0
  163. package/src/components/array-table/style.ts +11 -0
  164. package/src/components/array-tabs/index.ts +141 -0
  165. package/src/components/array-tabs/style.scss +16 -0
  166. package/src/components/array-tabs/style.ts +5 -0
  167. package/src/components/base/AnyOf/index.ts +155 -0
  168. package/src/components/base/AnyOf/index.vue +111 -0
  169. package/src/components/base/CloudButton/index.ts +46 -0
  170. package/src/components/base/CloudButton/index.vue +128 -0
  171. package/src/components/base/ColorGroup/index.ts +57 -0
  172. package/src/components/base/ColorGroup/index.vue +30 -0
  173. package/src/components/base/ColorPicker/index.ts +45 -0
  174. package/src/components/base/ColorPicker/index.vue +50 -0
  175. package/src/components/base/Container/index.vue +39 -0
  176. package/src/components/base/HotArea/index.ts +698 -0
  177. package/src/components/base/HotArea/index.vue +565 -0
  178. package/src/components/base/HotArea/type.ts +18 -0
  179. package/src/components/base/Input/index.vue +150 -0
  180. package/src/components/base/LayoutGroup/index.vue +40 -0
  181. package/src/components/base/Link/index.vue +43 -0
  182. package/src/components/base/Middleware/index.ts +235 -0
  183. package/src/components/base/Middleware/index.vue +169 -0
  184. package/src/components/base/OneOf/index.ts +105 -0
  185. package/src/components/base/OneOf/index.vue +85 -0
  186. package/src/components/base/RichEditor/index.ts +13 -0
  187. package/src/components/base/RichEditor/index.vue +120 -0
  188. package/src/components/base/RichEditor/options.ts +24 -0
  189. package/src/components/base/Select/index.ts +106 -0
  190. package/src/components/base/Select/index.vue +161 -0
  191. package/src/components/base/Switch/index.vue +42 -0
  192. package/src/components/base/Text/index.vue +47 -0
  193. package/src/components/base/event/index.ts +226 -0
  194. package/src/components/base/event/index.vue +356 -0
  195. package/src/components/cascader/index.ts +48 -0
  196. package/src/components/cascader/style.ts +5 -0
  197. package/src/components/checkbox/index.ts +166 -0
  198. package/src/components/checkbox/style.ts +5 -0
  199. package/src/components/cloud-button/index.ts +21 -0
  200. package/src/components/color-group/index.ts +18 -0
  201. package/src/components/color-picker/index.ts +18 -0
  202. package/src/components/container/index.ts +18 -0
  203. package/src/components/data-time/index.ts +24 -0
  204. package/src/components/date-picker/index.ts +51 -0
  205. package/src/components/date-picker/style.ts +4 -0
  206. package/src/components/editable/index.ts +261 -0
  207. package/src/components/editable/style.scss +55 -0
  208. package/src/components/editable/style.ts +5 -0
  209. package/src/components/el-form-item/index.ts +21 -0
  210. package/src/components/el-form-item/style.ts +3 -0
  211. package/src/components/event/index.ts +16 -0
  212. package/src/components/form/index.ts +94 -0
  213. package/src/components/form/style.scss +0 -0
  214. package/src/components/form/style.ts +3 -0
  215. package/src/components/form-button-group/index.ts +74 -0
  216. package/src/components/form-button-group/style.scss +0 -0
  217. package/src/components/form-button-group/style.ts +4 -0
  218. package/src/components/form-collapse/index.ts +215 -0
  219. package/src/components/form-collapse/style.scss +6 -0
  220. package/src/components/form-collapse/style.ts +4 -0
  221. package/src/components/form-dialog/index.ts +433 -0
  222. package/src/components/form-dialog/style.ts +4 -0
  223. package/src/components/form-drawer/index.ts +424 -0
  224. package/src/components/form-drawer/style.scss +21 -0
  225. package/src/components/form-drawer/style.ts +4 -0
  226. package/src/components/form-grid/index.ts +181 -0
  227. package/src/components/form-grid/style.scss +5 -0
  228. package/src/components/form-grid/style.ts +1 -0
  229. package/src/components/form-item/animation.scss +72 -0
  230. package/src/components/form-item/grid.scss +171 -0
  231. package/src/components/form-item/index.ts +642 -0
  232. package/src/components/form-item/style.scss +521 -0
  233. package/src/components/form-item/style.ts +2 -0
  234. package/src/components/form-item/var.scss +17 -0
  235. package/src/components/form-layout/index.ts +161 -0
  236. package/src/components/form-layout/style.scss +6 -0
  237. package/src/components/form-layout/style.ts +1 -0
  238. package/src/components/form-layout/useResponsiveFormLayout.ts +118 -0
  239. package/src/components/form-step/index.ts +210 -0
  240. package/src/components/form-step/style.ts +2 -0
  241. package/src/components/form-tab/index.ts +172 -0
  242. package/src/components/form-tab/style.scss +6 -0
  243. package/src/components/form-tab/style.ts +4 -0
  244. package/src/components/hot-area/index.ts +20 -0
  245. package/src/components/index.ts +49 -0
  246. package/src/components/input/index.ts +35 -0
  247. package/src/components/input/style.ts +4 -0
  248. package/src/components/input-number/index.ts +36 -0
  249. package/src/components/input-number/style.ts +3 -0
  250. package/src/components/layout-group/index.ts +18 -0
  251. package/src/components/link/index.ts +18 -0
  252. package/src/components/middleware/index.ts +21 -0
  253. package/src/components/one-of/index.ts +20 -0
  254. package/src/components/placeholder-input/index.ts +21 -0
  255. package/src/components/placeholder-input/index.vue +87 -0
  256. package/src/components/preview-text/index.ts +303 -0
  257. package/src/components/preview-text/style.ts +9 -0
  258. package/src/components/radio/index.ts +109 -0
  259. package/src/components/radio/style.ts +3 -0
  260. package/src/components/reset/index.ts +52 -0
  261. package/src/components/reset/style.ts +1 -0
  262. package/src/components/rich-editor/index.ts +18 -0
  263. package/src/components/row/index.ts +20 -0
  264. package/src/components/row/index.vue +53 -0
  265. package/src/components/select/index.ts +19 -0
  266. package/src/components/select/style.ts +5 -0
  267. package/src/components/space/index.ts +98 -0
  268. package/src/components/space/style.scss +24 -0
  269. package/src/components/space/style.ts +1 -0
  270. package/src/components/submit/index.ts +59 -0
  271. package/src/components/submit/style.ts +1 -0
  272. package/src/components/switch/index.ts +9 -0
  273. package/src/components/switch/style.ts +1 -0
  274. package/src/components/text/index.ts +21 -0
  275. package/src/components/time-picker/index.ts +21 -0
  276. package/src/components/time-picker/style.ts +4 -0
  277. package/src/components/transfer/index.ts +12 -0
  278. package/src/components/transfer/style.ts +1 -0
  279. package/src/components/upload/index.ts +136 -0
  280. package/src/components/upload/style.ts +2 -0
  281. package/src/components/upload-button/index.ts +21 -0
  282. package/src/components/upload-button/index.vue +199 -0
  283. package/src/createScemaField.ts +14 -0
  284. package/src/index.ts +41 -0
  285. package/src/schema-field/index.ts +11 -0
  286. package/src/scope.ts +29 -0
  287. package/src/transform/core.ts +404 -0
  288. package/src/transform/index.ts +11 -0
  289. package/src/transform/transform.ts +59 -0
  290. package/src/transform/utils/constant.ts +79 -0
  291. package/src/transform/utils/func.ts +145 -0
  292. package/src/typings/common.ts +37 -0
  293. package/src/typings/vue-shim.d.ts +7 -0
  294. package/src/typings/vue-test-utils.d.ts +11 -0
  295. package/src/utils/anyof_utils.ts +95 -0
  296. package/src/utils/common.ts +15 -0
  297. package/src/validate/index.ts +648 -0
  298. package/src/validate/utils/constant.ts +2 -0
  299. package/src/vue-form/index.ts +18 -0
  300. package/src/vue-form/index.vue +106 -0
  301. package/src/vue-form/useForm.ts +112 -0
  302. package/tests/data/anyof.ts +40 -0
  303. package/tests/data/image.ts +472 -0
  304. package/tests/fixtures/validate.ts +63 -0
  305. package/tests/unit/transform.spec.ts +253 -0
  306. package/tests/unit/utils.spec.ts +10 -0
  307. package/tests/unit/validate.spec.ts +256 -0
  308. package/tsconfig.json +23 -0
@@ -0,0 +1,648 @@
1
+ import { WHITE_COMPONENTS } from './utils/constant';
2
+ import { log, cloneDeep, isEqual, get, set } from '@cicctencent/ark-util';
3
+ import { FormilyValidateState, ValidateError } from '../typings/common';
4
+ import { transformSchema } from '../transform/transform';
5
+ import { getAnyOfConfig } from '../utils/anyof_utils';
6
+ import { UniqueType } from '../transform/utils/constant';
7
+ import { isSchemaConfig } from '../utils/common';
8
+ const infoLog = log('[jt-formily validate]')
9
+ /**
10
+ * Formily 校验
11
+ * 注意: 目前只满足现有组件配置的校验,如果存在特殊的校验逻辑需要更改逻辑
12
+ */
13
+ export class FormilyValidate {
14
+ state: FormilyValidateState
15
+ errList: ValidateError[] //错误列表
16
+ pathList: string[] // 属性路径 ['a','b','c','d','e','0'] => a.b.c.d.e[1]
17
+
18
+ constructor(state: FormilyValidateState) {
19
+ this.init(state);
20
+ }
21
+
22
+ init(state: FormilyValidateState){
23
+ const { data, schema, validateData } = state;
24
+ this.state = {
25
+ data: data ?? {},
26
+ schema: transformSchema({ schema: schema ?? {}, ctx: null }),
27
+ validateData: validateData ?? [],
28
+ };
29
+ // 初始化错误列表
30
+ this.errList = [];
31
+ this.pathList = [];
32
+ }
33
+
34
+ validate() {
35
+ this.validateProperties(this.state.schema?.properties ?? {});
36
+ infoLog('validate',this.errList);
37
+ return this.errList;
38
+ }
39
+ /**
40
+ * 判断属性
41
+ * @param data
42
+ * @returns
43
+ */
44
+ validateItem(data: any): boolean {
45
+ // 判断是否是自定义组建
46
+ if ('x-component' in data && WHITE_COMPONENTS.includes(data['x-component'])) {
47
+ // todo 自定义组件的校验(x)
48
+ switch(data['x-component']){
49
+ case 'Middleware':{
50
+ // 中间件不是必填的,不需要校验name,只需要校验options
51
+ const { parametersDescriptor } = data['x-component-props'] ?? { parametersDescriptor: '{}' };
52
+ // 根据中间件的内容 组装Schema
53
+ const result:any = this.getMiddlewareSchema(typeof parametersDescriptor === 'string'? JSON.parse(parametersDescriptor): parametersDescriptor, get(this.state.data, this.path))
54
+ this.validateMiddleware(result);
55
+ return false;
56
+ }
57
+ case 'AnyOf':
58
+ { // anyOf校验
59
+ const { list } = data['x-component-props'] ?? { list: [] };
60
+ let result = null;
61
+ if(typeof list === 'string'){
62
+ result = getAnyOfConfig(JSON.parse(list), get(this.state.data, this.path));
63
+ }else{
64
+ result = getAnyOfConfig(list, get(this.state.data, this.path));
65
+ }
66
+ infoLog('anyOf Config',result);
67
+ this.validateProperties(result?.properties??{})
68
+ return false;
69
+ }
70
+ case 'OneOf':
71
+ {
72
+ const { list } = data['x-component-props'] ?? { list: [] };
73
+ let result = null;
74
+ if(typeof list === 'string'){
75
+ result = this.getOneOfProperties(JSON.parse(list));
76
+ }else{
77
+ result = this.getOneOfProperties(list);
78
+ }
79
+ this.validateProperties(result?.properties)
80
+ return false;
81
+ }
82
+ case 'Event':{
83
+ const result = this.getEventProperties(data)
84
+ this.validateProperties(result.properties)
85
+ return false;
86
+ }
87
+ case 'HotArea':{
88
+ // todo 热区埋点校验 (热区比较特殊,不走正常的校验流程)
89
+ const result = this.getHotAreaProperties(data);
90
+ this.validateHotArea(result);
91
+ }
92
+ }
93
+ return false;
94
+ }
95
+ else if ('items' in data) {
96
+ // items里面存在必填项,再执行items校验逻辑
97
+ if (this.isRequiredByArray(data['items'])) {
98
+ this.validateArray(data['items']);
99
+ }
100
+ return false;
101
+ }
102
+ // 是否存在属性描述 properties items存在的情况,properties只是一个操作按钮
103
+ else if ('properties' in data) {
104
+ // 判断当前对象是否依赖其他属性
105
+ if(this.validateReactions(data)){
106
+ const dependencies = data['x-reactions']?.['dependencies'] ?? []
107
+ let depValue = undefined;
108
+ // 如果当前是void分组
109
+ if(data?.type === 'void'){
110
+ depValue = this.parseExpression(dependencies, null, data['x-reactions']?.fulfill?.schema?.['x-visible'], this.path, this.state.data, true);
111
+ }else{
112
+ depValue = this.parseExpression(dependencies, null, data['x-reactions']?.fulfill?.schema?.['x-visible'], this.path, this.state.data);
113
+ }
114
+ let expression:string = data['x-reactions']?.fulfill?.schema?.['x-visible'] ?? ''
115
+ // 判断表达式是否成立,不成立不继续校验该对象下面的属性
116
+ if(!this.calculateReactions(expression.replace(/\$deps\[\d\]/,typeof depValue === 'string' ? `'${depValue}'`: depValue))){
117
+ return
118
+ }
119
+ }
120
+ this.validateProperties(data['properties'])
121
+ return false;
122
+ }
123
+ // 判断是否存在必填属性 required
124
+ else if ('required' in data && data['required']) {
125
+ // 判断当前属性的渲染是否有依赖别的属性
126
+ if(this.validateReactions(data)){
127
+ const dependencies = data['x-reactions']?.['dependencies']
128
+ // 解析 {{$deps[0]}}表达式的结果
129
+ const depValue = this.parseExpression(dependencies, null, data['x-reactions']?.fulfill?.schema?.['x-visible'], this.path, this.state.data);
130
+ let expression:string = data['x-reactions']?.fulfill?.schema?.['x-visible'] ?? ''
131
+ // 判断表达式是否成立,成立返回true 不成立返回false 不进行必填项校验
132
+ return this.calculateReactions(expression.replace(/\$deps\[\d\]/,typeof depValue === 'string'? `'${depValue}'`: depValue))
133
+ }
134
+ return true;
135
+ }
136
+ }
137
+ /**
138
+ * 是否需要校验联动依赖
139
+ * @param data
140
+ * @returns
141
+ */
142
+ validateReactions(data){
143
+ if(!('x-reactions' in data)){
144
+ return false;
145
+ }
146
+ if(data['x-reactions']?.fulfill?.schema?.['x-visible']){
147
+ //存在依赖
148
+ return true
149
+ }
150
+ return false;
151
+ }
152
+
153
+ calculateReactions(expression: string):boolean{
154
+ const ExpRE = /^\s*\{\{([\s\S]*)\}\}\s*$/
155
+ const matchData = expression.match(ExpRE)
156
+ if(matchData.length === 2){
157
+ return this.calc(matchData[1])? true : false
158
+ }
159
+ return false
160
+ }
161
+ /**
162
+ * 计算方法
163
+ * @param expr
164
+ * @returns
165
+ */
166
+ calc (expr){
167
+ return Function('return (' + expr + ')')()
168
+ }
169
+
170
+ /** 校验热区 */
171
+ validateHotArea(data){
172
+ const hotData = get(this.state.data,this.path);
173
+ if(Array.isArray(hotData)&& hotData.length > 0 ){
174
+ hotData.forEach((item,index)=>{
175
+ this.insertPath(`${index}`);
176
+ this.insertPath('event');
177
+ if('properties' in data){
178
+ this.validateProperties(data.properties);
179
+ }
180
+ this.deletePath();
181
+ this.deletePath();
182
+ })
183
+ }
184
+
185
+ }
186
+
187
+ // 获取热区schema
188
+ getHotAreaProperties(data){
189
+ const { event } = data['x-component-props'] ?? { event:{} }
190
+ return transformSchema(typeof event === 'string' ? {schema: JSON.parse(event),ctx: null}: { schema: event, ctx: null });
191
+ }
192
+ getEventProperties(data){
193
+ const { schema } = data['x-component-props'] ?? { schema:{ }}
194
+ const result = {
195
+ type:'object',
196
+ properties:{
197
+ list: typeof schema === 'string'? JSON.parse(schema)?.list ?? []: schema?.list ?? []
198
+ }
199
+ }
200
+ return transformSchema({ schema: result, ctx: null });
201
+ }
202
+
203
+
204
+
205
+ /**
206
+ * 校验 Properties
207
+ * @param data
208
+ */
209
+ validateProperties(data) {
210
+ for (const key in data) {
211
+ // 添加路径 key = input 只是修饰写法 不是字段名 type= void 功能型按钮 或者 非对象
212
+ if(key !== 'input' && data[key]?.type !== 'void'){
213
+ this.insertPath(key);
214
+ }
215
+
216
+ /**
217
+ * 处理以下这种非schema场景
218
+ * options:{
219
+ * a:{},
220
+ * b:{},
221
+ * c:{}
222
+ * }
223
+ *
224
+ */
225
+ if(!isSchemaConfig(data[key])){
226
+ const tmpPath = cloneDeep(this.pathList);
227
+ tmpPath.pop()
228
+ const validateKey = get(this.state.data,tmpPath.join(".")+'._validateKey');
229
+ // 不存在validate_key就不需要修改配置
230
+ if(validateKey){
231
+ data[key]= data[key][validateKey]
232
+ }
233
+ }
234
+
235
+ // 校验每一个属性(字段) return true 必填的字段 false 非必填字段
236
+ if (this.validateItem(data[key])) {
237
+ // 判断目标路径是否有值
238
+ const checkData = get(this.state.data, this.path);
239
+
240
+ // 0 也当作成功处理
241
+ if (this.checkResult(checkData)) {
242
+ // todo 这里可以做额外的校验,例如: 0 or '' 校验失败
243
+ // 唯一校验 unique值 页面级唯一性校验为1 组件级唯一性校验为2
244
+ const values = Object.values(UniqueType).filter(v => !isNaN(Number(v)))
245
+ if('unique' in data[key] && values.includes(data[key]['unique'])){
246
+ if(this.isUnique(checkData, data[key]['unique'])){
247
+ // 是唯一的
248
+ }else{
249
+ //不是唯一的
250
+ const schemaData = {};
251
+ schemaData[key] = {
252
+ ...data[key],
253
+ default: checkData,
254
+ 'x-validator': {
255
+ buryReportName: true, // 开启buryReportName规则校验 这个校验是判断当前重复值是否发生修改了
256
+ value: checkData, // 当前重复值 作为rule对象的属性传入buryReportName规则
257
+ ...data[key]['x-validator'], // 配置文件内的其他校验配置 作为rule对象的属性传入buryReportName规则
258
+ }
259
+ };
260
+ if (this.pathList.includes('buryReportName')) {
261
+ // 区分事件埋点名
262
+ schemaData[key]['title'] = this.path.indexOf('event') !== -1 ? '事件埋点' : data[key]['title']
263
+ // schemaData[key]['x-validator'].message = '埋点名重复'
264
+ }
265
+ if('x-reactions' in schemaData[key]){
266
+ this.parseReactions(schemaData[key],['x-visible']);
267
+ // 处理完 删掉x-reactions 不然受到联动影响无法显示
268
+ delete schemaData[key]['x-reactions']
269
+ }
270
+ this.errList.push({
271
+ path: this.path,
272
+ schema: schemaData,
273
+ });
274
+ }
275
+ }
276
+ } else {
277
+ //不存在值,说明该字段未填写
278
+ const schemaData = {};
279
+ schemaData[key] = data[key];
280
+ if('x-reactions' in schemaData[key]){
281
+ this.parseReactions(schemaData[key],['x-visible']);
282
+ // 处理完 删掉x-reactions 不然受到联动影响无法显示
283
+ delete schemaData[key]['x-reactions']
284
+ }
285
+ this.errList.push({
286
+ path: this.path,
287
+ schema: schemaData,
288
+ });
289
+ }
290
+ }
291
+
292
+ if(key !== 'input' && data[key]?.type !== 'void'){
293
+ this.deletePath();
294
+ }
295
+ }
296
+ }
297
+ /**
298
+ * 校验items
299
+ * @param data
300
+ */
301
+ validateArray(data) {
302
+ // 获取目标路径的内容
303
+ const itemData: any[] = get(this.state.data, this.path);
304
+ // itemData必然是一个数组类型,如果不是那就是错的
305
+ if (Array.isArray(itemData)) {
306
+
307
+ // length = 5 说明有五条数据需要校验
308
+ for (let i = 0; i < itemData.length; ++i) {
309
+ this.insertPath(`${i}`)
310
+ // todo 处理校验逻辑
311
+ if ('properties' in data) {
312
+ const properties = cloneDeep(data['properties'])
313
+ // 这里是对象数组
314
+ for (const key in properties) {
315
+ if ((data['required'] && data['required'].includes(key)) || 'unique' in properties[key] || ('required' in properties[key] && properties[key]['required'])) {
316
+ // 处理标题 携带数组下标 便于定位是哪一项
317
+ properties[key].title = `${i + 1}.${properties[key].title || ''}`
318
+ }
319
+ }
320
+ this.validateProperties(properties)
321
+ } else {
322
+ // 非对象数组
323
+ if('required' in data){
324
+ // 存在必填项
325
+ if(!get(this.state.data, this.path)){
326
+ const schemaData={}
327
+ const key=((Math.random()*100000).toFixed(0)+Date.now()).toString()
328
+ schemaData[key] = data
329
+ this.errList.push({
330
+ path: this.path,
331
+ schema: schemaData,
332
+ });
333
+ }
334
+ }
335
+ }
336
+ this.deletePath();
337
+ }
338
+ }
339
+
340
+
341
+
342
+ }
343
+ /**
344
+ * 添加路径
345
+ * @param path
346
+ */
347
+ insertPath(path: string) {
348
+ this.pathList.push(path);
349
+ }
350
+ /**
351
+ * 删除路径,相当于(回退)的操作
352
+ * path=['a','b','c']
353
+ * deletePath() => path=['a','b']
354
+ */
355
+ deletePath() {
356
+ this.pathList.pop();
357
+ }
358
+ // items 是否存在必填项(这里只校验配置)
359
+ isRequiredByArray(items): boolean {
360
+ infoLog('isRequiredByArray',items)
361
+ if ('properties' in items) {
362
+ // 对象数组
363
+ return true;
364
+ } else {
365
+ // 非对象数组
366
+ if ('required' in items) {
367
+ return true;
368
+ }
369
+ }
370
+ return false;
371
+ }
372
+
373
+ /**
374
+ * 校验数据唯一
375
+ * @param value
376
+ */
377
+ isUnique(value, unique){
378
+ // 检索每一个组件的数据
379
+ if(value === ''){
380
+ return false;
381
+ }
382
+ //出现的次数统计
383
+ let countFlag = 0;
384
+ if (unique === UniqueType.page) {
385
+ for(let i = 0; i < this.state.validateData.length ; i++){
386
+ // const values = Object.values(this.state.validateData[i]);
387
+ const values: any[] = this.getObjectValues(this.state.validateData[i] ?? {})
388
+ countFlag += values.filter(v => v === value).length
389
+ }
390
+ } else if (unique === UniqueType.component) {
391
+ const values = this.getObjectValues(this.state.data ?? {})
392
+ countFlag += values.filter(v => v === value).length
393
+ }
394
+ // 出现超过2次
395
+ if(countFlag > 1){
396
+ return false;
397
+ }
398
+ return true;
399
+ }
400
+
401
+ /** getter */
402
+
403
+ /* 获取路径(解析好的路径,可以直接使用于dot) */
404
+ get path() {
405
+ return this.pathList.join('.')
406
+ }
407
+
408
+ /** 获取anyOf */
409
+ getAnyOfProperties(configList: any[], data :any){
410
+ // 没有属性的情况
411
+ if(Object.keys(data || {}).length === 0){
412
+ return {}
413
+ }
414
+ // 获取最匹配的anyOf元素
415
+ const config = configList.map(item => ({
416
+ item,
417
+ score: this.checkAnyOfProperties(item),
418
+ })).sort((a, b)=> b.score -a.score)[0];
419
+
420
+ return config.item?.properties ?? {};
421
+ }
422
+ /** 是否是anyOf */
423
+ isAnyOf(data){
424
+ return data['x-component'] === 'AnyOf'
425
+ }
426
+
427
+ checkAnyOfProperties(data){
428
+ const value = get(this.state.data, this.path) ?? {}
429
+ const dataKeys=Object.keys(value);
430
+ const propertiesKeys = Object.keys(data?.properties ?? {});
431
+ // 最匹配评分
432
+ let count = -1;
433
+ for(let j = 0; j < dataKeys.length; j++){
434
+ if(!propertiesKeys.includes(dataKeys[j])){
435
+ return -1;
436
+ } else {
437
+ count ++;
438
+ }
439
+
440
+ }
441
+ return count;
442
+ }
443
+
444
+ getOneOfProperties(configList:any[]){
445
+ const data = get(this.state.data, this.path +'.name')
446
+ for(let i = 0;i < configList.length; i++){
447
+ if(configList[i]?.properties?.name?.default === data){
448
+ const keys = Object.keys(configList[i]?.properties).filter(key=>key !== 'name')
449
+ const result = { properties:{ } }
450
+ keys.forEach(k=>{
451
+ result['properties'][k] = configList[i]?.properties[k] ?? {};
452
+ })
453
+ return transformSchema({ schema: result, ctx: null });
454
+ }
455
+ }
456
+ return { properties:{}}
457
+ }
458
+
459
+ getObjectValues(data) {
460
+ let valueArr = [];
461
+ // null undefine
462
+ if(!data){
463
+ return valueArr;
464
+ }
465
+ Object.keys(data).forEach(it => {
466
+ // 不是null,是对象、且不是数组
467
+ if (data[it] && typeof data[it] === 'object' && !Array.isArray(data[it])) {
468
+ valueArr = valueArr.concat(this.getObjectValues(data[it]));
469
+ } else if (data[it] && Array.isArray(data[it])) {
470
+ for (let i = 0; i < data[it].length; i++) {
471
+ valueArr = valueArr.concat(this.getObjectValues(data[it][i]));
472
+ }
473
+ }
474
+ // 值的类型是字符串 并且 key 等于当前配置unique的属性key(通过pathList最后一个元素获取)
475
+ else if (typeof data[it] === 'string' ) {
476
+ // 如果存在路径,则要判断当前的value是否等于要校验唯一性的那个值
477
+ if(this.pathList.length ){
478
+ if(it === this.pathList[this.pathList.length - 1]){
479
+ // 存在历史路径, 判断当前的内容是否是等于 唯一性的值
480
+ valueArr.push(data[it]);
481
+ }
482
+ }else{
483
+ // 如果没有历史路径,直接返回
484
+ valueArr.push(data[it]);
485
+ }
486
+ }
487
+ });
488
+ return valueArr;
489
+ }
490
+ /**
491
+ * 解析 x-reactions
492
+ * 目前只支持 schema 表达式
493
+ * @param schema
494
+ * @param data
495
+ * @returns
496
+ */
497
+ parseReactions(schema, ignore: string[] = []){
498
+
499
+ try {
500
+ if(!schema){
501
+ return schema;
502
+ }
503
+ if('x-reactions' in schema){
504
+ if('dependencies' in schema['x-reactions']){
505
+ // 获取依赖信息 []
506
+ const dependencies = schema['x-reactions']['dependencies']
507
+ const reactionsSchema = schema['x-reactions']?.fulfill?.schema;
508
+ // 是否存在schema 表达式
509
+ if(reactionsSchema){
510
+ Object.keys(reactionsSchema || {}).forEach((key: string) => {
511
+ // 如果不是字符串类型,就不可能是一个表达式可以不用管
512
+ if(ignore.includes(key) || typeof reactionsSchema[key] !== 'string'
513
+ ){
514
+ return
515
+ }
516
+ const value = this.parseExpression(dependencies, null,reactionsSchema[key], this.path,this.state.data);
517
+
518
+ if(value){
519
+ set(schema, key, value);
520
+ }else{
521
+ infoLog('parseReactions','解析失败')
522
+ }
523
+ })
524
+ }
525
+ }else if('target' in schema['x-reactions']){
526
+ // todo 主动联动
527
+ }
528
+ }
529
+ } catch (error) {
530
+ infoLog(`parseReactions error, schema: ${typeof schema !== 'string'?JSON.stringify(schema):schema} error: ${typeof error !== 'string'? JSON.stringify(error):error}` )
531
+ }
532
+ }
533
+ /**
534
+ * 解析表达式
535
+ * @param dependencies 被动联动依赖
536
+ * @param target 主动联动target
537
+ * @param exp 表达式字符串
538
+ * @param path 路径
539
+ * @param data 数据
540
+ * @returns
541
+ */
542
+ parseExpression(dependencies: string[] | null, target: string | null, exp: string, path: string, data: any, isVoid: boolean = false): any{
543
+ // const ExpRE = /^\s*\{\{([\s\S]*)\}\}\s*$/
544
+ // {{$deps[0]===???}} 获取中括号里面的数值
545
+ const indexStr = exp.match(/\$deps\[(\d+)\]/)
546
+ if(indexStr){
547
+ const index = parseInt(indexStr[1]);
548
+ const depString = dependencies[index]
549
+ // 获取依赖层级
550
+ let hierarchy = depString.match(/\.+/i) ? depString.match(/\.+/i)[0].length: 0
551
+
552
+ // 获取path
553
+ const newPath = path.split('.')
554
+
555
+ // 判断路径
556
+ if(hierarchy){
557
+ if(isVoid){
558
+ hierarchy--
559
+ }
560
+ for(let i =0 ; i < hierarchy ;i++){
561
+ newPath.pop();
562
+ }
563
+ newPath.push(depString.replace(/\.+/,''));
564
+ const value = get(data,newPath.join('.'));
565
+ return value;
566
+ }
567
+ }
568
+ return null;
569
+ }
570
+
571
+ checkResult(value:any):boolean{
572
+ // null undefine '' 失败
573
+ if(!value){
574
+ // 数字0
575
+ if(value === 0){
576
+ return true;
577
+ }
578
+ return false
579
+ }
580
+ if(Array.isArray(value)){
581
+ // 数组长度必须大于0
582
+ if(!value.length){
583
+ return false
584
+ }
585
+ // 对象数组的情况
586
+ if(typeof value[0] === 'object'){
587
+ for(let i = 0; i< value.length; i++){
588
+ // 这里防止value[i] 是null 或者 undefine 使用 in 导致异常
589
+ if(value[i] && '__validate_value__' in value[i]){
590
+ if(!value[i]['__validate_value__']){
591
+ return false;
592
+ }
593
+ }
594
+ }
595
+ }
596
+ return true;
597
+ }
598
+
599
+ if(typeof value === 'object'){
600
+ // 对象存在__validate_value__属性的情况下,校验结果由 __validate_value__ 控制
601
+ if('__validate_value__' in value){
602
+ // 包含__validate_value__的对象
603
+ return !!value['__validate_value__']
604
+ }
605
+ else if(isEqual(value, {})){
606
+ // 空对象
607
+ return false;
608
+ }
609
+ return true
610
+ }
611
+ return true;
612
+ }
613
+ /**
614
+ * 获取中间件Schema
615
+ */
616
+ getMiddlewareSchema(parametersDescriptor, data){
617
+ const schemaList = [];
618
+ data.forEach(it =>{
619
+ schemaList.push({
620
+ properties:{
621
+ options:{
622
+ type: 'object',
623
+ properties: {...parametersDescriptor[it.name]}
624
+ }
625
+ }
626
+ })
627
+ })
628
+
629
+ // 有多少条数据,就有多少个Schema
630
+ return schemaList;
631
+ }
632
+ /**
633
+ * 校验中间件
634
+ * @param data
635
+ */
636
+ validateMiddleware(data: any[]) {
637
+ // 获取中间件数据
638
+ const middlewareData: any[] = get(this.state.data, this.path);
639
+ // itemData必然是一个数组类型,如果不是那就是错的
640
+ if (Array.isArray(middlewareData)) {
641
+ for (let i = 0; i < middlewareData.length; ++i) {
642
+ this.insertPath(`${i}`)
643
+ this.validateProperties(data[i]['properties'])
644
+ this.deletePath();
645
+ }
646
+ }
647
+ }
648
+ }
@@ -0,0 +1,2 @@
1
+ /* 不校验的组件 */
2
+ export const WHITE_COMPONENTS = ['HotArea', 'Event', 'AnyOf', 'OneOf','Middleware'];
@@ -0,0 +1,18 @@
1
+ import VueForm from './index.vue';
2
+ declare global {
3
+ interface Window{
4
+ __jt_formily_components: any;
5
+ JT_VERSION: Record<string, string>;
6
+ }
7
+ }
8
+
9
+
10
+
11
+ window.JT_VERSION = Object.assign(
12
+ {
13
+ '__rollup__name__@__rollup__version__': '__rollup__date__',
14
+ },
15
+ window.JT_VERSION
16
+ );
17
+ export { VueForm };
18
+ export default VueForm;