@truenewx/tnxvue3 3.4.0 → 3.4.2

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 (65) hide show
  1. package/package.json +23 -42
  2. package/src/bootstrap-vue/alert/Alert.vue +79 -79
  3. package/src/bootstrap-vue/button/Button.vue +40 -40
  4. package/src/bootstrap-vue/enum-select/EnumSelect.vue +4 -5
  5. package/src/bootstrap-vue/form/Form.vue +320 -320
  6. package/src/bootstrap-vue/form/FormGroup.vue +73 -73
  7. package/src/bootstrap-vue/loading-icon/LoadingIcon.vue +46 -46
  8. package/src/bootstrap-vue/paged/Paged.vue +119 -119
  9. package/src/bootstrap-vue/progress/Progress.vue +58 -58
  10. package/src/bootstrap-vue/query-table/QueryTable.vue +84 -84
  11. package/src/bootstrap-vue/region-cascader/RegionCascader.vue +119 -119
  12. package/src/bootstrap-vue/select/Select.vue +375 -375
  13. package/src/bootstrap-vue/submit-form/SubmitForm.vue +180 -176
  14. package/src/bootstrap-vue/tags-input/TagsInput.vue +64 -64
  15. package/src/bootstrap-vue/tnxbsv.css +107 -107
  16. package/src/bootstrap-vue/tnxbsv.js +109 -92
  17. package/src/bootstrap-vue/upload/Upload.vue +173 -173
  18. package/src/{aj-captcha → element-plus/aj-captcha}/Verify/VerifySlide.vue +9 -9
  19. package/src/element-plus/aj-captcha/api/index.js +11 -0
  20. package/src/{aj-captcha → element-plus/aj-captcha}/utils/ase.js +1 -1
  21. package/src/element-plus/avatar/Avatar.vue +8 -9
  22. package/src/element-plus/button/Button.vue +2 -2
  23. package/src/element-plus/curd/Curd.vue +20 -23
  24. package/src/element-plus/date-picker/DatePicker.vue +1 -1
  25. package/src/element-plus/detail-form/DetailForm.vue +1 -1
  26. package/src/element-plus/dialog/Dialog.vue +18 -20
  27. package/src/element-plus/drawer/Drawer.vue +10 -9
  28. package/src/element-plus/edit-table/EditTable.vue +3 -3
  29. package/src/element-plus/enum-select/EnumSelect.vue +3 -3
  30. package/src/element-plus/enum-view/EnumView.vue +41 -41
  31. package/src/element-plus/fetch-cascader/FetchCascader.vue +3 -4
  32. package/src/element-plus/fetch-select/FetchSelect.vue +10 -11
  33. package/src/element-plus/fetch-tags/FetchTags.vue +4 -5
  34. package/src/element-plus/fss-upload/FssUpload.vue +18 -18
  35. package/src/element-plus/fss-view/FssView.vue +1 -1
  36. package/src/element-plus/icon/Icon.vue +6 -0
  37. package/src/element-plus/input-dropdown/InputDropdown.vue +74 -74
  38. package/src/element-plus/query-form/QueryForm.vue +1 -1
  39. package/src/element-plus/query-table/QueryTable.vue +22 -26
  40. package/src/element-plus/region-cascader/RegionCascader.vue +4 -5
  41. package/src/element-plus/select/Select.vue +11 -7
  42. package/src/element-plus/steps-nav/StepsNav.vue +3 -4
  43. package/src/element-plus/submit-form/SubmitForm.vue +36 -39
  44. package/src/element-plus/tabs/Tabs.vue +1 -1
  45. package/src/element-plus/tnxel.css +22 -0
  46. package/src/element-plus/tnxel.js +105 -94
  47. package/src/element-plus/toolbar/ToolBarItem.js +15 -15
  48. package/src/element-plus/toolbar/Toolbar.vue +56 -56
  49. package/src/element-plus/transfer/Transfer.vue +8 -9
  50. package/src/element-plus/upload/Upload.vue +24 -24
  51. package/src/tdesign/desktop/tnxtdd.ts +16 -0
  52. package/src/tdesign/foundation/validator.ts +367 -0
  53. package/src/tdesign/mobile/tnxtdm.ts +16 -0
  54. package/src/tdesign/tnxtd.ts +20 -0
  55. package/src/{tnxvue-router.js → tnxvue-router.ts} +66 -48
  56. package/src/tnxvue.ts +248 -0
  57. package/src/types.d.ts +7 -0
  58. package/tsconfig.json +21 -0
  59. package/index.html +0 -12
  60. package/src/aj-captcha/api/index.js +0 -19
  61. package/src/tnxvue-validator.js +0 -365
  62. package/src/tnxvue.js +0 -439
  63. /package/src/{aj-captcha → element-plus/aj-captcha}/Verify/VerifyPoints.vue +0 -0
  64. /package/src/{aj-captcha → element-plus/aj-captcha}/Verify.vue +0 -0
  65. /package/src/{aj-captcha → element-plus/aj-captcha}/utils/util.js +0 -0
@@ -1,56 +1,56 @@
1
- <template>
2
- <div class="tnxel-toolbar">
3
- <div class="flex-v-center">
4
- <template v-for="(item, index) of leftItems" :key="index">
5
- <el-divider direction="vertical" v-if="item === '|'"/>
6
- <ToolBarItem
7
- :item="Item.from(item)"
8
- :size="size"
9
- v-else-if="typeof item ==='object'"/>
10
- </template>
11
- </div>
12
- <div class="flex-v-center">
13
- <ToolBarItem v-for="(item, index) of rightItems" :key="index"
14
- :item="Item.from(item)"
15
- :size="size"/>
16
- </div>
17
- </div>
18
- </template>
19
-
20
- <script>
21
- import ToolBarItem from './ToolBarItem.vue';
22
- import Item from './ToolBarItem.js';
23
-
24
- export default {
25
- name: 'TnxelToolbar',
26
- components: {ToolBarItem},
27
- props: {
28
- leftItems: Array,
29
- rightItems: Array,
30
- size: {
31
- type: String,
32
- validator: value => {
33
- return ['small', 'default', 'large'].includes(value);
34
- },
35
- default: 'default',
36
- },
37
- },
38
- data() {
39
- return {
40
- Item,
41
- model: {},
42
- };
43
- },
44
- methods: {}
45
- }
46
- </script>
47
-
48
- <style>
49
- .tnxel-toolbar {
50
- height: 32px;
51
- padding: 0 8px;
52
- display: flex;
53
- align-items: center;
54
- justify-content: space-between;
55
- }
56
- </style>
1
+ <template>
2
+ <div class="tnxel-toolbar">
3
+ <div class="flex-v-center">
4
+ <template v-for="(item, index) of leftItems" :key="index">
5
+ <el-divider direction="vertical" v-if="item === '|'"/>
6
+ <ToolBarItem
7
+ :item="Item.from(item)"
8
+ :size="size"
9
+ v-else-if="typeof item ==='object'"/>
10
+ </template>
11
+ </div>
12
+ <div class="flex-v-center">
13
+ <ToolBarItem v-for="(item, index) of rightItems" :key="index"
14
+ :item="Item.from(item)"
15
+ :size="size"/>
16
+ </div>
17
+ </div>
18
+ </template>
19
+
20
+ <script>
21
+ import ToolBarItem from './ToolBarItem.vue';
22
+ import Item from './ToolBarItem.js';
23
+
24
+ export default {
25
+ name: 'TnxelToolbar',
26
+ components: {ToolBarItem},
27
+ props: {
28
+ leftItems: Array,
29
+ rightItems: Array,
30
+ size: {
31
+ type: String,
32
+ validator: value => {
33
+ return ['small', 'default', 'large'].includes(value);
34
+ },
35
+ default: 'default',
36
+ },
37
+ },
38
+ data() {
39
+ return {
40
+ Item,
41
+ model: {},
42
+ };
43
+ },
44
+ methods: {}
45
+ }
46
+ </script>
47
+
48
+ <style>
49
+ .tnxel-toolbar {
50
+ height: 32px;
51
+ padding: 0 8px;
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: space-between;
55
+ }
56
+ </style>
@@ -59,23 +59,22 @@ export default {
59
59
  }
60
60
  },
61
61
  created() {
62
- let vm = this;
63
62
  let params;
64
63
  if (typeof this.params === 'function') {
65
64
  params = this.params();
66
65
  } else {
67
66
  params = this.params;
68
67
  }
69
- window.tnx.app.rpc.get(this.url, params, function(list) {
70
- vm.selectable = [];
68
+ window.tnx.app.rpc.get(this.url, params).then((list) => {
69
+ this.selectable = [];
71
70
  list.forEach(item => {
72
- if (vm.formatter) {
73
- vm.formatter(item);
71
+ if (this.formatter) {
72
+ this.formatter(item);
74
73
  }
75
- vm.selectable.push({
76
- key: item[vm.keyName],
77
- label: item[vm.labelName],
78
- index: item[vm.indexName],
74
+ this.selectable.push({
75
+ key: item[this.keyName],
76
+ label: item[this.labelName],
77
+ index: item[this.indexName],
79
78
  });
80
79
  });
81
80
  });
@@ -293,7 +293,7 @@ export default {
293
293
  this.files = [].concat(this.fileList);
294
294
  let vm = this;
295
295
  // 需在vue渲染之后才可正常操作dom元素
296
- this.$nextTick(function () {
296
+ this.$nextTick(() => {
297
297
  // 初始化显示尺寸
298
298
  let width = vm.uploadSize.width;
299
299
  if (width) {
@@ -360,7 +360,7 @@ export default {
360
360
  }
361
361
  // 校验文件重复
362
362
  const vm = this;
363
- if (this.files.contains(function (f) {
363
+ if (this.files.contains((f) => {
364
364
  const raw = f.raw ? f.raw : f;
365
365
  return file.uid !== raw.uid && vm.getFileId(file) === vm.getFileId(raw);
366
366
  })) {
@@ -430,7 +430,7 @@ export default {
430
430
 
431
431
  const vm = this;
432
432
  const rpc = this.tnx.app.rpc;
433
- return new Promise(function (resolve, reject) {
433
+ return new Promise((resolve, reject) => {
434
434
  if (vm.validate(file)) {
435
435
  let $upload = $('#' + vm.id + ' .el-upload');
436
436
  if (vm.showFileList && vm.fileNumExceed) {
@@ -438,29 +438,12 @@ export default {
438
438
  }
439
439
 
440
440
  // 上传前需确保用户在fss应用中已登录
441
- rpc.ensureLogined(function () {
442
- if (vm.beforeUpload) {
443
- let promise = vm.beforeUpload(file);
444
- if (promise instanceof Promise) {
445
- promise.then(function () {
446
- resolve(file);
447
- }).catch(function () {
448
- reject(file);
449
- });
450
- } else if (promise === false) {
451
- reject(file);
452
- } else {
453
- resolve(file);
454
- }
455
- } else {
456
- resolve(file);
457
- }
458
- }, {
441
+ rpc.ensureLogined({
459
442
  app: vm.app,
460
443
  toLogin(loginFormUrl, originalUrl, originalMethod) {
461
444
  $upload.css('visibility', 'unset');
462
445
  // 从当前应用登录表单地址
463
- rpc.get('/authentication/login-url', function (loginUrl) {
446
+ rpc.get('/authentication/login-url').then(loginUrl => {
464
447
  if (loginUrl) {
465
448
  // 默认登录后跳转回当前页面
466
449
  loginUrl += loginUrl.contains('?') ? '&' : '?';
@@ -473,6 +456,23 @@ export default {
473
456
  });
474
457
  return true;
475
458
  }
459
+ }).then(() => {
460
+ if (vm.beforeUpload) {
461
+ let promise = vm.beforeUpload(file);
462
+ if (promise instanceof Promise) {
463
+ promise.then(() => {
464
+ resolve(file);
465
+ }).catch(() => {
466
+ reject(file);
467
+ });
468
+ } else if (promise === false) {
469
+ reject(file);
470
+ } else {
471
+ resolve(file);
472
+ }
473
+ } else {
474
+ resolve(file);
475
+ }
476
476
  });
477
477
  } else {
478
478
  reject(file);
@@ -557,14 +557,14 @@ export default {
557
557
  }
558
558
  },
559
559
  removeFile(file) {
560
- this.files.remove(function (f) {
560
+ this.files.remove((f) => {
561
561
  return file.uid === f.uid;
562
562
  });
563
563
  this.$refs.upload.handleRemove(file);
564
564
  if (!this.fileNumExceed) {
565
565
  this.handleErrors([]); // 移除一个文件后,此时如果有错误提示则一定为数量超限,需清空错误提示
566
566
  let container = $('#' + this.id);
567
- this.$nextTick(function () {
567
+ this.$nextTick(() => {
568
568
  // 去掉文件列表的样式,以免其占高度
569
569
  $('.el-upload-list', container).removeAttr('style');
570
570
  // 恢复添加框默认样式
@@ -0,0 +1,16 @@
1
+ import { App, Component } from 'vue';
2
+ import { Router } from 'vue-router';
3
+ import TnxTd from '../tnxtd';
4
+ import TDesign from 'tdesign-vue-next';
5
+
6
+ export default class TnxTdd extends TnxTd {
7
+
8
+ constructor(apiBaseUrl: string) {
9
+ super(apiBaseUrl);
10
+ }
11
+
12
+ createVueApp(rootComponent: Component, router: Router, rootProps?: Record<string, any>): App {
13
+ return super.createVueApp(rootComponent, router, rootProps).use(TDesign);
14
+ }
15
+
16
+ }
@@ -0,0 +1,367 @@
1
+ /**
2
+ * 校验规则转换器,将服务端元数据中的校验规则转换为validator组件的规则。
3
+ * validator组件详见:https://github.com/validatorjs/validator.js
4
+ */
5
+ import validator from 'validator';
6
+ import {Validator} from '../../../../tnxcore/src/tnxcore';
7
+ import {ApiModelPropertyMeta, ApiModelMeta} from '../../../../tnxcore/src/api/meta';
8
+
9
+ export type ValidateResultType = 'error' | 'warning' | 'success';
10
+
11
+ export type ValidateResult = {
12
+ result: boolean;
13
+ message?: string;
14
+ type?: ValidateResultType;
15
+ }
16
+
17
+ export type ValidatorRule = {
18
+ name?: string;
19
+ required?: boolean;
20
+ message?: string;
21
+ type?: ValidateResultType;
22
+ trigger?: 'blur' | 'change';
23
+ validator?: (fieldValue: string) => ValidateResult;
24
+ }
25
+
26
+ export default class TnxTdValidator extends Validator {
27
+
28
+ getRule(validationName: string, validationValue: any, fieldMeta: ApiModelPropertyMeta): ValidatorRule | undefined {
29
+ let rule: ValidatorRule;
30
+ let fieldCaption = '';
31
+ // 据目前观察,字段格式校验的错误消息均显示在字段旁,无需显示字段名称,未来如果出现不在字段旁显示的场景,再考虑扩展
32
+ // if (fieldMeta && fieldMeta.caption) {
33
+ // fieldCaption = fieldMeta.caption;
34
+ // }
35
+ switch (validationName) {
36
+ case 'required':
37
+ case 'notNull':
38
+ case 'notEmpty':
39
+ case 'notBlank':
40
+ if (validationValue === true) {
41
+ rule = {
42
+ required: true,
43
+ validator(fieldValue: string): ValidateResult {
44
+ if (validationValue) {
45
+ let blank = fieldValue === undefined || fieldValue === null;
46
+ if (!blank && typeof fieldValue === 'string') {
47
+ blank = fieldValue.trim().length === 0;
48
+ }
49
+ if (blank) {
50
+ let message = this.getErrorMessage(validationName, fieldCaption);
51
+ return {result: false, message, type: 'error'};
52
+ }
53
+ }
54
+ return {result: true};
55
+ }
56
+ }
57
+ }
58
+ break;
59
+ case 'minLength':
60
+ rule = {
61
+ validator(fieldValue: string): ValidateResult {
62
+ if (typeof validationValue === 'number' && typeof fieldValue === 'string') {
63
+ let enterLength = fieldValue.indexOf('\n') < 0 ? 0 : (fieldValue.match(/\n/g) as any).length;
64
+ let fieldLength = fieldValue.length + enterLength;
65
+ if (fieldLength < validationValue) {
66
+ let message = this.getErrorMessage(validationName, fieldCaption,
67
+ validationValue, validationValue - fieldLength);
68
+ return {result: false, message, type: 'error'};
69
+ }
70
+ }
71
+ return {result: true};
72
+ }
73
+ };
74
+ break;
75
+ case 'maxLength':
76
+ rule = {
77
+ validator(fieldValue: string): ValidateResult {
78
+ if (typeof validationValue === 'number' && typeof fieldValue === 'string') {
79
+ let enterLength = fieldValue.indexOf('\n') < 0 ? 0 : (fieldValue.match(/\n/g) as any).length;
80
+ let fieldLength = fieldValue.length + enterLength;
81
+ if (fieldLength > validationValue) {
82
+ let message = this.getErrorMessage(validationName, fieldCaption,
83
+ validationValue, fieldLength - validationValue);
84
+ return {result: false, message, type: 'error'};
85
+ }
86
+ }
87
+ return {result: true};
88
+ }
89
+ };
90
+ break;
91
+ case 'number':
92
+ if (validationValue === true) {
93
+ rule = {
94
+ validator(fieldValue: string): ValidateResult {
95
+ if (fieldValue && typeof fieldValue === 'string') {
96
+ if (!validator.isNumeric(fieldValue, {no_symbols: true})) {
97
+ let message = this.getErrorMessage(validationName, fieldCaption);
98
+ return {result: false, message, type: 'error'};
99
+ }
100
+ }
101
+ return {result: true};
102
+ }
103
+ };
104
+ }
105
+ break;
106
+ case 'notContainsHtmlChars':
107
+ if (validationValue === true) {
108
+ rule = {
109
+ validator(fieldValue: string): ValidateResult {
110
+ if (typeof fieldValue === 'string') {
111
+ let limitedValues = ['<', '>', "'", '"', '\\'];
112
+ for (let i = 0; i < limitedValues.length; i++) {
113
+ if (fieldValue.indexOf(limitedValues[i]) >= 0) {
114
+ let s = limitedValues.join(' ');
115
+ let message = this.getErrorMessage('notContains', fieldCaption, s);
116
+ return {result: false, message, type: 'error'};
117
+ }
118
+ }
119
+ }
120
+ return {result: true};
121
+ }
122
+ };
123
+ }
124
+ break;
125
+ case 'notContainsIllegalFilenameChars':
126
+ if (validationValue === true) {
127
+ rule = {
128
+ validator(fieldValue: string): ValidateResult {
129
+ if (typeof fieldValue === 'string') {
130
+ let limitedValues = ['/', '\\', ':', '*', '?', '"', '<', '>', '|'];
131
+ for (let i = 0; i < limitedValues.length; i++) {
132
+ if (fieldValue.indexOf(limitedValues[i]) >= 0) {
133
+ let s = limitedValues.join(' ');
134
+ let message = this.getErrorMessage('notContains', fieldCaption, s);
135
+ return {result: false, message, type: 'error'};
136
+ }
137
+ }
138
+ }
139
+ return {result: true};
140
+ }
141
+ };
142
+ }
143
+ break;
144
+ case 'notContains':
145
+ if (validationValue) {
146
+ rule = {
147
+ validator(fieldValue: string): ValidateResult {
148
+ if (typeof fieldValue === 'string') {
149
+ let limitedValues = (validationValue as string).split(' ');
150
+ for (let i = 0; i < limitedValues.length; i++) {
151
+ if (validator.contains(fieldValue, limitedValues[i])) {
152
+ let message = this.getErrorMessage('notContains', fieldCaption,
153
+ validationValue);
154
+ return {result: false, message, type: 'error'};
155
+ }
156
+ }
157
+ }
158
+ return {result: true};
159
+ }
160
+ };
161
+ }
162
+ break;
163
+ case 'rejectHtmlTags':
164
+ if (validationValue === true) {
165
+ rule = {
166
+ validator(fieldValue: string): ValidateResult {
167
+ if (fieldValue) {
168
+ if (/<[a-z]+[ ]*[/]?[ ]*>/gi.test(fieldValue)) {
169
+ let message = this.getErrorMessage(validationName, fieldCaption);
170
+ return {result: false, message, type: 'error'};
171
+ }
172
+ }
173
+ return {result: true};
174
+ }
175
+ };
176
+ }
177
+ break;
178
+ case 'allowedHtmlTags':
179
+ if (validationValue) {
180
+ rule = {
181
+ validator(fieldValue: string): ValidateResult {
182
+ if (typeof fieldValue === 'string') {
183
+ let tags = (validationValue as string).toLowerCase().split(',');
184
+ if (tags.length) {
185
+ let value = fieldValue.toLowerCase();
186
+ let leftIndex = value.indexOf('<');
187
+ let rightIndex = leftIndex >= 0 ? value.indexOf('>', leftIndex) : -1;
188
+ while (leftIndex >= 0 && rightIndex >= 0) {
189
+ let sub = value.substring(leftIndex + 1, rightIndex);
190
+ let spaceIndex = sub.indexOf(' ');
191
+ let tag = spaceIndex >= 0 ? sub.substring(0, spaceIndex) : sub;
192
+ if (tag.startsWith('/')) {
193
+ tag = tag.substring(1);
194
+ }
195
+ if (!tags.contains(tag.toLowerCase())) {
196
+ let message = this.getErrorMessage(validationName, fieldCaption,
197
+ tags.join(', '));
198
+ return {result: false, message, type: 'error'};
199
+ }
200
+ leftIndex = value.indexOf('<', rightIndex);
201
+ rightIndex = leftIndex >= 0 ? value.indexOf('>', leftIndex) : -1;
202
+ }
203
+ }
204
+ }
205
+ return {result: true};
206
+ }
207
+ };
208
+ }
209
+ break;
210
+ case 'forbiddenHtmlTags':
211
+ if (validationValue) {
212
+ rule = {
213
+ validator(fieldValue: string): ValidateResult {
214
+ if (fieldValue) {
215
+ let tags = (validationValue as string).toLowerCase().split(',');
216
+ if (tags.length) {
217
+ let value = fieldValue.toLowerCase();
218
+ for (let tag of tags) {
219
+ if (value.includes('<' + tag + '>') || value.includes('<' + tag + ' ')) {
220
+ let message = this.getErrorMessage(validationName, fieldCaption,
221
+ tags.join(', '));
222
+ return {result: false, message, type: 'error'};
223
+ }
224
+ }
225
+ }
226
+ }
227
+ return {result: true};
228
+ }
229
+ };
230
+ }
231
+ break;
232
+ case 'email':
233
+ if (validationValue === true) {
234
+ rule = {
235
+ validator(fieldValue: string): ValidateResult {
236
+ if (typeof fieldValue === 'string' && fieldValue) {
237
+ if (!validator.isEmail(fieldValue)) {
238
+ let message = this.getErrorMessage(validationName, fieldCaption);
239
+ return {result: false, message, type: 'error'};
240
+ }
241
+ }
242
+ return {result: true};
243
+ }
244
+ }
245
+ }
246
+ break;
247
+ case 'cellphone':
248
+ case 'idCardNo':
249
+ case 'url':
250
+ case 'opposableUrl':
251
+ rule = {
252
+ validator(fieldValue: string): ValidateResult {
253
+ if (validationValue) {
254
+ let message = this.validateRegExp(validationName, fieldValue, fieldCaption);
255
+ if (message) {
256
+ return {result: false, message, type: 'error'};
257
+ }
258
+ }
259
+ return {result: true};
260
+ }
261
+ };
262
+ break;
263
+ case 'regex':
264
+ rule = {
265
+ validator(fieldValue: string): ValidateResult {
266
+ if (fieldValue) {
267
+ let pattern = (validationValue as any).pattern;
268
+ if (!pattern.startsWith('^')) {
269
+ pattern = '^' + pattern;
270
+ }
271
+ if (!pattern.endsWith('$')) {
272
+ pattern += '$';
273
+ }
274
+ let regexp = new RegExp(pattern, 'gi');
275
+ if (!regexp.test(fieldValue)) {
276
+ let message = (validationValue as any).message;
277
+ if (message) {
278
+ message = fieldCaption + message;
279
+ } else {
280
+ message = this.getErrorMessage('regex', fieldCaption, '');
281
+ }
282
+ return {result: false, message, type: 'error'};
283
+ }
284
+ }
285
+ return {result: true};
286
+ }
287
+ }
288
+ break;
289
+ case 'custom':
290
+ if (typeof validationValue === 'function') {
291
+ rule = {
292
+ validator(fieldValue: string): ValidateResult {
293
+ let message = validationValue(fieldValue);
294
+ if (message) {
295
+ return {result: false, message, type: 'error'};
296
+ }
297
+ return {result: true};
298
+ }
299
+ }
300
+ }
301
+ break;
302
+ }
303
+ if (rule) {
304
+ rule.name = validationName;
305
+ let metaType = 'text';
306
+ if (fieldMeta && fieldMeta.type) {
307
+ metaType = fieldMeta.type.toLowerCase();
308
+ }
309
+ let optional = metaType === 'option' || metaType === 'datetime' || metaType === 'date' || metaType === 'time';
310
+ rule.trigger = optional ? 'change' : 'blur';
311
+ }
312
+ return rule;
313
+ }
314
+
315
+ getRules(meta: ApiModelMeta): Record<string, ValidatorRule[]> {
316
+ let rules: Record<string, ValidatorRule[]> = {};
317
+ Object.keys(meta).forEach(fieldName => {
318
+ let fieldMeta = meta[fieldName];
319
+ if (fieldMeta.validation) {
320
+ let fieldRules: ValidatorRule[] = [];
321
+ Object.keys(fieldMeta.validation).forEach(validationName => {
322
+ let validationValue = fieldMeta.validation[validationName];
323
+ let rule = this.getRule(validationName, validationValue, fieldMeta);
324
+ if (rule) {
325
+ fieldRules.push(rule);
326
+ }
327
+ });
328
+ // 将可能包含的引用字段路径中的.替换为__,以符合async-validator规则名称的规范
329
+ let ruleName = fieldName.replace('.', '__');
330
+ rules[ruleName] = fieldRules;
331
+ }
332
+ });
333
+ return rules;
334
+ }
335
+
336
+ /**
337
+ * 用指定规则集校验指定模型中的所有字符串值字段
338
+ * @return 校验结果错误消息集映射,key-字段名,value-错误消息集,校验通过的不包含在内
339
+ */
340
+ validate(rules: Record<string, ValidatorRule[]>, model: Record<string, any>): Record<string, string[]> {
341
+ let result: Record<string, string[]> = {};
342
+ if (rules && model) {
343
+ Object.keys(rules).forEach(fieldName => {
344
+ const fieldRules = rules[fieldName];
345
+ if (Array.isArray(fieldRules) && fieldRules.length) {
346
+ const messages: string[] = [];
347
+ const fieldValue = model[fieldName];
348
+ if (typeof fieldValue === 'string') {
349
+ for (let rule of fieldRules) {
350
+ if (typeof rule.validator === 'function') {
351
+ const r = rule.validator(fieldValue);
352
+ if (r && r.result !== true && r.message) {
353
+ messages.push(r.message);
354
+ }
355
+ }
356
+ }
357
+ if (messages.length > 0) {
358
+ result[fieldName] = messages;
359
+ }
360
+ }
361
+ }
362
+ });
363
+ }
364
+ return result;
365
+ }
366
+
367
+ }
@@ -0,0 +1,16 @@
1
+ import { App, Component } from 'vue';
2
+ import { Router } from 'vue-router';
3
+ import TnxTd from '../tnxtd';
4
+ import TDesign from 'tdesign-mobile-vue';
5
+
6
+ export default class TnxTdm extends TnxTd {
7
+
8
+ constructor(apiBaseUrl: string) {
9
+ super(apiBaseUrl);
10
+ }
11
+
12
+ createVueApp(rootComponent: Component, router: Router, rootProps?: Record<string, any>): App {
13
+ return super.createVueApp(rootComponent, router, rootProps).use(TDesign);
14
+ }
15
+
16
+ }
@@ -0,0 +1,20 @@
1
+ import TnxVue from '../tnxvue';
2
+ import {Icon} from 'tdesign-icons-vue-next';
3
+ import Validator from './foundation/validator';
4
+
5
+ export {Validator};
6
+
7
+ export default class TnxTd extends TnxVue {
8
+
9
+ static {
10
+ TnxTd.Foundations.Validator = Validator;
11
+ }
12
+
13
+ constructor(apiBaseUrl: string) {
14
+ super(apiBaseUrl);
15
+ Object.assign(this.components, {
16
+ Icon,
17
+ });
18
+ }
19
+
20
+ }