@truenewx/tnxvue3 3.4.2 → 3.4.4

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 (32) hide show
  1. package/package.json +14 -8
  2. package/src/element-plus/aj-captcha/api/index.js +2 -2
  3. package/src/element-plus/avatar/Avatar.vue +4 -27
  4. package/src/element-plus/date-picker/DatePicker.vue +8 -9
  5. package/src/element-plus/dialog/Dialog.vue +15 -15
  6. package/src/element-plus/drawer/Drawer.vue +2 -2
  7. package/src/element-plus/edit-table/EditTable.vue +10 -10
  8. package/src/element-plus/enum-select/EnumSelect.vue +30 -30
  9. package/src/element-plus/enum-view/EnumView.vue +1 -3
  10. package/src/element-plus/fetch-cascader/FetchCascader.vue +4 -4
  11. package/src/element-plus/fetch-select/FetchSelect.vue +3 -3
  12. package/src/element-plus/fetch-tags/FetchTags.vue +1 -1
  13. package/src/element-plus/fss-upload/FssUpload.vue +75 -114
  14. package/src/element-plus/fss-view/FssView.vue +28 -30
  15. package/src/element-plus/query-form/QueryForm.vue +3 -3
  16. package/src/element-plus/query-table/QueryTable.vue +12 -12
  17. package/src/element-plus/region-cascader/RegionCascader.vue +3 -3
  18. package/src/element-plus/select/Select.vue +56 -56
  19. package/src/element-plus/submit-form/SubmitForm.vue +5 -5
  20. package/src/element-plus/tnxel-validator.ts +347 -0
  21. package/src/element-plus/tnxel.ts +575 -0
  22. package/src/element-plus/transfer/Transfer.vue +2 -2
  23. package/src/element-plus/upload/Upload.vue +68 -70
  24. package/src/tdesign/desktop/tnxtdd.ts +5 -5
  25. package/src/tdesign/mobile/tnxtdm.css +0 -0
  26. package/src/tdesign/mobile/tnxtdm.ts +7 -6
  27. package/src/tdesign/{foundation/validator.ts → tnxtd-validator.ts} +4 -4
  28. package/src/tdesign/tnxtd.ts +6 -7
  29. package/src/tnxvue-router.ts +8 -14
  30. package/src/tnxvue.ts +53 -26
  31. package/tsconfig.json +32 -19
  32. package/src/element-plus/tnxel.js +0 -598
@@ -0,0 +1,575 @@
1
+ /**
2
+ * 基于ElementPlus的扩展支持
3
+ */
4
+ import * as Vue from 'vue';
5
+ import ElementPlus, {
6
+ ElLoading,
7
+ ElMessage,
8
+ ElMessageBox,
9
+ ElMessageBoxOptions,
10
+ MessageParams,
11
+ LoadingOptions,
12
+ LoadingInstance,
13
+ } from 'element-plus';
14
+ import ElementPlus_zh_CN from 'element-plus/es/locale/lang/zh-cn';
15
+ import TnxVue, {DialogOptions, DrawerOptions, util} from '../tnxvue.ts';
16
+ import Validator from './tnxel-validator.ts';
17
+
18
+ import CaptchaVerify from './aj-captcha/Verify.vue';
19
+ import Avatar from './avatar/Avatar.vue';
20
+ import Alert from './alert/Alert.vue';
21
+ import Button from './button/Button.vue';
22
+ import CheckIcon from './check-icon/CheckIcon.vue';
23
+ import CloseErrorButton from './close-error-button/CloseErrorButton.vue';
24
+ import Curd from './curd/Curd.vue';
25
+ import DatePicker from './date-picker/DatePicker.vue';
26
+ import DateRange from './date-range/DateRange.vue';
27
+ import DateTimePicker from './datetime-picker/DateTimePicker.vue';
28
+ import DetailForm from './detail-form/DetailForm.vue';
29
+ import Dialog from './dialog/Dialog.vue';
30
+ import Drawer from './drawer/Drawer.vue';
31
+ import DropdownItem from './dropdown-item/DropdownItem.vue';
32
+ import EditTable from './edit-table/EditTable.vue';
33
+ import EnumSelect from './enum-select/EnumSelect.vue';
34
+ import EnumView from './enum-view/EnumView.vue';
35
+ import FetchCascader from './fetch-cascader/FetchCascader.vue';
36
+ import FetchSelect from './fetch-select/FetchSelect.vue';
37
+ import FetchTags from './fetch-tags/FetchTags.vue';
38
+ import FssUpload from './fss-upload/FssUpload.vue';
39
+ import FssView from './fss-view/FssView.vue';
40
+ import Icon from './icon/Icon.vue';
41
+ import InputDropdown from './input-dropdown/InputDropdown.vue';
42
+ import InputNumber from './input-number/InputNumber.vue';
43
+ import Paged from './paged/Paged.vue';
44
+ import PermissionTree from './permission-tree/PermissionTree.vue';
45
+ import QueryForm from './query-form/QueryForm.vue';
46
+ import QueryTable from './query-table/QueryTable.vue';
47
+ import RegionCascader from './region-cascader/RegionCascader.vue';
48
+ import Select from './select/Select.vue';
49
+ import Slider from './slider/Slider.vue';
50
+ import StepsNav from './steps-nav/StepsNav.vue';
51
+ import SubmitForm from './submit-form/SubmitForm.vue';
52
+ import TabColumn from './table-column/TableColumn.vue';
53
+ import Tabs from './tabs/Tabs.vue';
54
+ import Transfer from './transfer/Transfer.vue';
55
+ import Upload from './upload/Upload.vue';
56
+
57
+ import './tnxel.css';
58
+
59
+ export {util};
60
+
61
+ export {Validator};
62
+
63
+ const dialogContainerClass = 'tnxel-dialog-container';
64
+ const drawerContainerClass = 'tnxel-drawer-container';
65
+
66
+ type VueComponent = Vue.Component & {
67
+ components?: Record<string, Vue.Component>;
68
+ };
69
+
70
+ type ModalComponentInstance = Vue.ComponentPublicInstance & {
71
+ id: string;
72
+ close: () => Promise<void>;
73
+ }
74
+
75
+ export default class TnxEl extends TnxVue {
76
+
77
+ validator: Validator;
78
+
79
+ constructor(apiBaseUrl: string, id: string = 'tnxel') {
80
+ super(apiBaseUrl, id);
81
+ this.foundations.Validator = Validator;
82
+ this.validator = new Validator();
83
+ this.libs.ElementPlus = ElementPlus;
84
+ Object.assign(this.components, {
85
+ CaptchaVerify,
86
+ Avatar,
87
+ Alert,
88
+ Button,
89
+ CheckIcon,
90
+ CloseErrorButton,
91
+ Curd,
92
+ DatePicker,
93
+ DateRange,
94
+ DateTimePicker,
95
+ DetailForm,
96
+ Dialog,
97
+ Drawer,
98
+ DropdownItem,
99
+ EditTable,
100
+ EnumSelect,
101
+ EnumView,
102
+ FetchCascader,
103
+ FetchSelect,
104
+ FetchTags,
105
+ FssUpload,
106
+ FssView,
107
+ Icon,
108
+ InputDropdown,
109
+ InputNumber,
110
+ Paged,
111
+ PermissionTree,
112
+ QueryForm,
113
+ QueryTable,
114
+ RegionCascader,
115
+ Select,
116
+ Slider,
117
+ StepsNav,
118
+ SubmitForm,
119
+ TabColumn,
120
+ Tabs,
121
+ Transfer,
122
+ Upload,
123
+ });
124
+
125
+ if (this.router) {
126
+ this.router.beforeLeave = this.util.function.around(this.router.beforeLeave, (beforeLeave, to) => {
127
+ // 页面跳转前关闭当前页面中可能存在的所有消息框和对话框
128
+ this.closeMessage();
129
+ this.closeDialog(true);
130
+ beforeLeave.call(this.router, to);
131
+ });
132
+ }
133
+ }
134
+
135
+ install(app: Vue.App): void {
136
+ super.install(app);
137
+
138
+ // 始终安装ElementPlus,以避免对于不同Vue实例未安装的问题
139
+ app.use(ElementPlus, {
140
+ locale: ElementPlus_zh_CN,
141
+ });
142
+ }
143
+
144
+ private dialogInstances: ModalComponentInstance[] = []; // 对话框实例堆栈
145
+
146
+ dialog(content: string | Vue.Component | Vue.DefineComponent,
147
+ title?: string,
148
+ options: DialogOptions = {},
149
+ contentProps: Record<string, any> = {}): ModalComponentInstance {
150
+ this.closeMessage();
151
+
152
+ let componentDefinition: VueComponent = Object.assign({}, Dialog);
153
+ if (this.isComponent(content)) {
154
+ const components = Object.assign({}, Dialog.components || {});
155
+ components['tnxel-dialog-content'] = content;
156
+ componentDefinition.components = components;
157
+ content = null;
158
+ }
159
+
160
+ const dialogId = 'dialog-' + (new Date().getTime());
161
+ const container = document.createElement('div');
162
+ container.className = dialogContainerClass;
163
+ container.id = dialogId;
164
+ document.body.appendChild(container);
165
+
166
+ const buttons = this.getOptionsButtons(options);
167
+ const containerSelector = '.' + dialogContainerClass + '#' + dialogId;
168
+ let dialogVm = this.createVueApp(componentDefinition, null, {
169
+ modelValue: true,
170
+ container: containerSelector,
171
+ title: title,
172
+ content: content,
173
+ contentProps: contentProps,
174
+ buttons: buttons,
175
+ theme: options.theme,
176
+ });
177
+ const dialog = dialogVm.mount(containerSelector) as ModalComponentInstance;
178
+ dialog.$options = Object.assign(dialog.$options || {}, options);
179
+ dialog.$options.onClosed = this.util.function.around(dialog.$options.onClosed, (onClosed: () => void): void => {
180
+ dialogVm.unmount();
181
+ this.dialogInstances.remove(dialog);
182
+
183
+ const containerElement = document.querySelector(containerSelector) as HTMLElement | null;
184
+ if (containerElement) {
185
+ const nextElement = containerElement.nextElementSibling;
186
+ if (nextElement && nextElement.classList.contains('el-overlay')) {
187
+ nextElement.remove();
188
+ }
189
+ containerElement.remove();
190
+ }
191
+
192
+ if (onClosed) {
193
+ onClosed.call(dialog);
194
+ }
195
+ });
196
+ this.dialogInstances.push(dialog);
197
+ return dialog;
198
+ }
199
+
200
+ closeDialog(all?: boolean): Promise<void> {
201
+ return new Promise<void>((resolve) => {
202
+ if (this.dialogInstances.length) {
203
+ let dialog = this.dialogInstances.pop();
204
+ let promises = [];
205
+ while (dialog) {
206
+ promises.push(dialog.close());
207
+ if (all) {
208
+ dialog = this.dialogInstances.pop();
209
+ } else {
210
+ break;
211
+ }
212
+ }
213
+ Promise.all(promises).then(() => {
214
+ resolve();
215
+ });
216
+ } else {
217
+ resolve();
218
+ }
219
+ });
220
+ }
221
+
222
+ private drawerInstances: ModalComponentInstance[] = []; // 抽屉实例堆栈
223
+
224
+ drawer(content: string | Vue.Component,
225
+ title?: string,
226
+ options: DrawerOptions = {},
227
+ contentProps: Record<string, any> = {}): ModalComponentInstance {
228
+ this.closeMessage();
229
+
230
+ let componentDefinition: VueComponent = Object.assign({}, Drawer);
231
+ if (this.isComponent(content)) {
232
+ const components = Object.assign({}, Drawer.components || {});
233
+ components['tnxel-drawer-content'] = content;
234
+ componentDefinition.components = components;
235
+ content = null;
236
+ }
237
+
238
+ const drawerId = 'drawer-' + (new Date().getTime());
239
+ const container = document.createElement('div');
240
+ container.className = drawerContainerClass;
241
+ container.id = drawerId;
242
+ document.body.appendChild(container);
243
+
244
+ const buttons = this.getOptionsButtons(options);
245
+ const containerSelector = '.' + drawerContainerClass + '#' + drawerId;
246
+ let drawerVm = this.createVueApp(componentDefinition, null, {
247
+ content: content,
248
+ title: title,
249
+ contentProps: contentProps,
250
+ buttons: buttons,
251
+ theme: options.theme,
252
+ });
253
+ const drawer = drawerVm.mount(containerSelector) as ModalComponentInstance;
254
+ drawer.$options = Object.assign(drawer.$options || {}, options);
255
+ drawer.$options.onClosed = this.util.function.around(drawer.$options.onClosed, (onClosed: () => void): void => {
256
+ drawerVm.unmount();
257
+ this.drawerInstances.remove(drawer);
258
+
259
+ const containerElement = document.querySelector(containerSelector) as HTMLElement | null;
260
+ if (containerElement) {
261
+ const nextElement = containerElement.nextElementSibling;
262
+ if (nextElement && nextElement.classList.contains('el-overlay')) {
263
+ nextElement.remove();
264
+ }
265
+ containerElement.remove();
266
+ }
267
+
268
+ if (onClosed) {
269
+ onClosed.call(drawer);
270
+ }
271
+ });
272
+ this.drawerInstances.push(drawer);
273
+ return drawer;
274
+ }
275
+
276
+ closeDrawer(all?: boolean): Promise<void> {
277
+ return new Promise<void>((resolve) => {
278
+ if (this.drawerInstances.length) {
279
+ let drawer = this.drawerInstances.pop();
280
+ let promises = [];
281
+ while (drawer) {
282
+ promises.push(drawer.close());
283
+ if (all) {
284
+ drawer = this.drawerInstances.pop();
285
+ } else {
286
+ break;
287
+ }
288
+ }
289
+ Promise.all(promises).then(() => {
290
+ resolve();
291
+ });
292
+ } else {
293
+ resolve();
294
+ }
295
+ });
296
+ }
297
+
298
+ private closeMessage() {
299
+ ElMessage.closeAll();
300
+ this.closeLoading();
301
+ }
302
+
303
+ private handleZIndex(selector: string): void {
304
+ setTimeout(() => {
305
+ const topZIndex = window.tnx.util.dom.minTopZIndex(2);
306
+ if (selector.endsWith(':last')) {
307
+ selector = selector.substring(0, selector.length - ':last'.length);
308
+ }
309
+ const elements = document.querySelectorAll(selector);
310
+ const el = elements.length > 0 ? elements.item(elements.length - 1) as HTMLElement : null;
311
+ if (el) {
312
+ const zIndexStr = window.getComputedStyle(el).zIndex;
313
+ const zIndex = Number(zIndexStr);
314
+ if (isNaN(zIndex) || topZIndex > zIndex) {
315
+ el.style.zIndex = String(topZIndex);
316
+ const modal = el.nextElementSibling as HTMLElement | null;
317
+ if (modal && modal.classList.contains('v-modal')) {
318
+ modal.style.zIndex = String(topZIndex - 1);
319
+ }
320
+ }
321
+ }
322
+ });
323
+ }
324
+
325
+ toast(message: string, timeout?: number): Promise<void> {
326
+ this.closeMessage();
327
+ return new Promise<void>((resolve) => {
328
+ let options: MessageParams = {
329
+ type: 'success', // 默认为成功主题,可更改为其它主题
330
+ offset: this.util.dom.getDocHeight() * 0.4,
331
+ dangerouslyUseHTMLString: true,
332
+ showClose: false,
333
+ message: message,
334
+ duration: timeout || 2000,
335
+ customClass: 'tnxel-toast',
336
+ onClose: () => {
337
+ resolve();
338
+ }
339
+ };
340
+ ElMessage(options);
341
+ this.handleZIndex('.el-message:last');
342
+ this.eventBus.emit('tnx.toast', options);
343
+ });
344
+ }
345
+
346
+ async alert(message: string, title: string = '提示'): Promise<void> {
347
+ let options: ElMessageBoxOptions = {
348
+ title: title,
349
+ dangerouslyUseHTMLString: true,
350
+ type: 'warning',
351
+ confirmButtonText: '确定',
352
+ };
353
+ this.closeMessage();
354
+ await ElMessageBox.alert(message, options);
355
+ this.handleZIndex('.el-message-box__wrapper:last');
356
+ this.eventBus.emit('tnx.alert', Object.assign({}, options, {message}));
357
+ }
358
+
359
+ async success(message: string): Promise<void> {
360
+ let options: ElMessageBoxOptions = {
361
+ title: '成功',
362
+ dangerouslyUseHTMLString: true,
363
+ type: 'success',
364
+ confirmButtonText: '确定',
365
+ };
366
+ this.closeMessage();
367
+ await ElMessageBox.alert(message, options);
368
+ this.handleZIndex('.el-message-box__wrapper:last');
369
+ this.eventBus.emit('tnx.success', Object.assign({}, options, {message}));
370
+ }
371
+
372
+ async error(message: string): Promise<void> {
373
+ let options: ElMessageBoxOptions = {
374
+ title: '错误',
375
+ dangerouslyUseHTMLString: true,
376
+ type: 'error',
377
+ confirmButtonText: '确定',
378
+ };
379
+ this.closeMessage();
380
+ await ElMessageBox.alert(message, options);
381
+ this.handleZIndex('.el-message-box__wrapper:last');
382
+ this.eventBus.emit('tnx.error', Object.assign({}, options, {message}));
383
+ }
384
+
385
+ confirm(message: string, title = '确认') {
386
+ let options: ElMessageBoxOptions = {
387
+ title: title,
388
+ type: 'info',
389
+ icon: Vue.markRaw(Icon.components.QuestionFilled),
390
+ confirmButtonText: '确定',
391
+ cancelButtonText: '取消',
392
+ dangerouslyUseHTMLString: true,
393
+ distinguishCancelAndClose: true,
394
+ };
395
+ // if (options.reverse) {
396
+ // options.customClass = 'reverse';
397
+ // let buttonText = options.confirmButtonText;
398
+ // options.confirmButtonText = options.cancelButtonText;
399
+ // options.cancelButtonText = buttonText;
400
+ // }
401
+
402
+ this.closeMessage();
403
+ return new Promise<boolean>(resolve => {
404
+ ElMessageBox.confirm(message, options).then(() => {
405
+ resolve(true);
406
+ }).catch(() => {
407
+ resolve(false);
408
+ });
409
+ this.handleZIndex('.el-message-box__wrapper:last');
410
+ this.eventBus.emit('tnx.confirm', Object.assign({}, options, {message}));
411
+ })
412
+ }
413
+
414
+ private loadingInstance: LoadingInstance | null;
415
+
416
+ showLoading(message?: string): Promise<void> {
417
+ return new Promise<void>((resolve, reject) => {
418
+ this.closeMessage();
419
+ try {
420
+ let options: LoadingOptions = {
421
+ text: message,
422
+ };
423
+ this.loadingInstance = ElLoading.service(options);
424
+ this.handleZIndex('.el-loading-mask');
425
+ this.eventBus.emit('tnx.showLoading', options);
426
+ let vm = this.loadingInstance.vm;
427
+ if (vm) {
428
+ this.nextTickTimeout(vm, () => {
429
+ resolve();
430
+ }, 500);
431
+ } else {
432
+ resolve();
433
+ }
434
+ } catch (e) {
435
+ this.loadingInstance = null;
436
+ console.error(e);
437
+ reject(e);
438
+ }
439
+ });
440
+ }
441
+
442
+ closeLoading(): void {
443
+ if (this.loadingInstance) { // 确保绝对的单例
444
+ this.loadingInstance.close();
445
+ this.loadingInstance = null;
446
+ }
447
+ }
448
+
449
+ hideLoading(): void {
450
+ this.closeLoading();
451
+ }
452
+
453
+ /**
454
+ * 对表格组件的格式化支持
455
+ */
456
+ table = {
457
+
458
+ date: {
459
+ formatDateTime(row: Record<string, any>, columnIndex: number, cellValue: any): string | undefined {
460
+ if (cellValue) {
461
+ return new Date(cellValue).formatDateTime();
462
+ }
463
+ return undefined;
464
+ },
465
+ formatDate(row: Record<string, any>, columnIndex: number, cellValue: any): string | undefined {
466
+ if (cellValue) {
467
+ return new Date(cellValue).formatDate();
468
+ }
469
+ return undefined;
470
+ },
471
+ formatTime(row: Record<string, any>, columnIndex: number, cellValue: any): string | undefined {
472
+ if (typeof cellValue === 'number') {
473
+ cellValue = new Date(cellValue);
474
+ }
475
+ if (cellValue instanceof Date) {
476
+ cellValue = cellValue.formatTime();
477
+ }
478
+ if (typeof cellValue === 'string') {
479
+ return cellValue;
480
+ }
481
+ return undefined;
482
+ },
483
+ formatTimeMinute(row: Record<string, any>, columnIndex: number, cellValue: any): string | undefined {
484
+ if (typeof cellValue === 'number') {
485
+ cellValue = new Date(cellValue);
486
+ }
487
+ if (cellValue instanceof Date) {
488
+ cellValue = cellValue.formatTimeMinute();
489
+ }
490
+ if (typeof cellValue === 'string') {
491
+ let array = cellValue.split(':');
492
+ if (array.length > 1) {
493
+ return array[0] + ':' + array[1];
494
+ }
495
+ }
496
+ return undefined;
497
+ },
498
+ formatDateMinute(row: Record<string, any>, columnIndex: number, cellValue: any): string | undefined {
499
+ if (cellValue) {
500
+ return new Date(cellValue).formatDateMinute();
501
+ }
502
+ return undefined;
503
+ },
504
+ formatDateMonth(row: Record<string, any>, columnIndex: number, cellValue: any): string | undefined {
505
+ if (cellValue) {
506
+ return new Date(cellValue).formatDateMonth();
507
+ }
508
+ return undefined;
509
+ },
510
+ formatPermanentableDate(row: Record<string, any>, columnIndex: number, cellValue: any): string | undefined {
511
+ if (Array.isArray(cellValue)) {
512
+ cellValue = cellValue[columnIndex];
513
+ }
514
+ return util.date.formatPermanentableDate(cellValue);
515
+ },
516
+ /**
517
+ * 将Java标准的日期格式转换为Day.js的日期格式
518
+ * @param format Java标准的日期格式
519
+ * @returns {String} Day.js的日期格式
520
+ */
521
+ toDayJsDateFormat(format: string): string {
522
+ return format.replaceAll('y', 'Y').replaceAll('d', 'D');
523
+ },
524
+ },
525
+
526
+ number: {
527
+ formatPercent(row: Record<string, any>, columnIndex: number, cellValue: any): string | undefined {
528
+ if (typeof cellValue !== 'number') {
529
+ cellValue = parseFloat(cellValue);
530
+ }
531
+ if (!isNaN(cellValue)) {
532
+ return cellValue.toPercent();
533
+ }
534
+ return undefined;
535
+ }
536
+ },
537
+
538
+ boolean: {
539
+ items: {
540
+ getText(type: string, value: any): string | undefined {
541
+ let items = this[type];
542
+ if (Array.isArray(items)) {
543
+ for (let item of items) {
544
+ if (item.value === value) {
545
+ return item.text;
546
+ }
547
+ }
548
+ }
549
+ return undefined;
550
+ },
551
+ has: [{
552
+ value: true,
553
+ text: '有',
554
+ }, {
555
+ value: false,
556
+ text: '无',
557
+ }]
558
+ },
559
+ format(row: Record<string, any>, columnIndex: number, cellValue: any): any {
560
+ if (typeof cellValue === 'boolean') {
561
+ cellValue = cellValue.toText();
562
+ }
563
+ return cellValue;
564
+ },
565
+ formatHas(row: Record<string, any>, columnIndex: number, cellValue: any): any {
566
+ if (typeof cellValue === 'boolean') {
567
+ cellValue = this.items.getText('has', cellValue);
568
+ }
569
+ return cellValue;
570
+ }
571
+ },
572
+
573
+ }
574
+
575
+ }
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <el-transfer class="tnxel-transfer" v-model="selected" :data="selectable" :titles="titles"
3
- :filterable="filterable" :filter-method="filter" :filter-placeholder="placeholder"/>
3
+ :filterable="filterable" :filter-method="filter" :filter-placeholder="placeholder"/>
4
4
  </template>
5
5
 
6
6
  <script>
@@ -65,7 +65,7 @@ export default {
65
65
  } else {
66
66
  params = this.params;
67
67
  }
68
- window.tnx.app.rpc.get(this.url, params).then((list) => {
68
+ window.tnx.api.get(this.url, params).then((list) => {
69
69
  this.selectable = [];
70
70
  list.forEach(item => {
71
71
  if (this.formatter) {