@truenewx/tnxvue3 3.4.4 → 3.4.6

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 (35) hide show
  1. package/package.json +18 -17
  2. package/src/bootstrap-vue/dialog/Dialog.vue +22 -10
  3. package/src/bootstrap-vue/select/Select.vue +3 -4
  4. package/src/components/ScrollView.vue +69 -0
  5. package/src/css.d.ts +4 -0
  6. package/src/element-plus/aj-captcha/utils/ase.js +4 -4
  7. package/src/element-plus/dialog/Dialog.vue +19 -7
  8. package/src/element-plus/drawer/Drawer.vue +20 -3
  9. package/src/element-plus/fss-upload/FssUpload.vue +1 -1
  10. package/src/element-plus/icon/Icon.vue +3 -0
  11. package/src/element-plus/select/Select.vue +3 -4
  12. package/src/element-plus/tnxel.css +0 -8
  13. package/src/element-plus/tnxel.ts +58 -72
  14. package/src/tdesign/mobile/calendar/Calendar.vue +121 -0
  15. package/src/tdesign/mobile/date-time-picker/DateTimePicker.vue +147 -0
  16. package/src/tdesign/mobile/dialog/Dialog.vue +179 -0
  17. package/src/tdesign/mobile/dialog/DialogContent.vue +13 -0
  18. package/src/tdesign/mobile/drawer/Drawer.vue +197 -0
  19. package/src/tdesign/mobile/drawer/DrawerContent.vue +13 -0
  20. package/src/tdesign/mobile/enum-select/EnumSelect.vue +164 -0
  21. package/src/tdesign/mobile/popup/Popup.vue +106 -0
  22. package/src/tdesign/mobile/region-picker/RegionPicker.vue +223 -0
  23. package/src/tdesign/mobile/select/Select.vue +479 -0
  24. package/src/tdesign/mobile/slide-radio-group/SlideRadioGroup.vue +397 -0
  25. package/src/tdesign/mobile/tnxtdm.css +125 -0
  26. package/src/tdesign/mobile/tnxtdm.ts +305 -1
  27. package/src/tdesign/tnxtd-validator.ts +14 -13
  28. package/src/tdesign/tnxtd.css +98 -0
  29. package/src/tdesign/tnxtd.ts +4 -0
  30. package/src/tnxvue-router.ts +59 -5
  31. package/src/tnxvue.ts +32 -11
  32. package/src/vue.d.ts +6 -0
  33. package/tsconfig.json +4 -7
  34. /package/src/{percent → components}/Percent.vue +0 -0
  35. /package/src/{text → components}/Text.vue +0 -0
@@ -1,17 +1,321 @@
1
1
  import {App, Component} from 'vue';
2
2
  import {Router} from 'vue-router';
3
3
  import TnxTd from '../tnxtd.ts';
4
- import TDesign from 'tdesign-mobile-vue';
4
+ import TDesign, {Dialog as TDialogPlugin, Toast} from 'tdesign-mobile-vue/esm';
5
+ import 'tdesign-mobile-vue/esm/style/index.js'
5
6
  import './tnxtdm.css';
7
+ import * as Vue from "vue";
8
+ import {EnumType, EnumItem} from '../../../../tnxcore/src/api/meta.ts';
9
+ import {type DialogOptions, type DrawerOptions, type OpenOptions} from '../../tnxvue.ts';
10
+ import Calendar from './calendar/Calendar.vue';
11
+ import DateTimePicker from './date-time-picker/DateTimePicker.vue';
12
+ import Dialog from './dialog/Dialog.vue';
13
+ import Drawer from './drawer/Drawer.vue';
14
+ import EnumSelect from './enum-select/EnumSelect.vue';
15
+ import Popup from './popup/Popup.vue';
16
+ import RegionPicker from './region-picker/RegionPicker.vue';
17
+ import Select from './select/Select.vue';
18
+ import SlideRadioGroup from './slide-radio-group/SlideRadioGroup.vue';
19
+
20
+ export type {EnumType, EnumItem};
21
+ export type {OpenOptions};
22
+
23
+ const components = {
24
+ Calendar,
25
+ DateTimePicker,
26
+ Dialog,
27
+ Drawer,
28
+ EnumSelect,
29
+ Popup,
30
+ RegionPicker,
31
+ Select,
32
+ SlideRadioGroup,
33
+ };
34
+
35
+ const dialogContainerClass = 'tnxtdm-dialog-container';
36
+ const drawerContainerClass = 'tnxtdm-drawer-container';
37
+
38
+ type VueComponent = Vue.Component & {
39
+ components?: Record<string, Vue.Component>;
40
+ };
41
+
42
+ type ModalComponentInstance = Vue.ComponentPublicInstance & {
43
+ id: string;
44
+ close: () => Promise<void>;
45
+ options: Record<string, any>;
46
+ }
47
+
48
+ type ToastTheme = 'success' | 'warning' | 'error' | 'loading';
6
49
 
7
50
  export default class TnxTdm extends TnxTd {
8
51
 
9
52
  constructor(apiBaseUrl: string, id: string = 'tnxtdm') {
10
53
  super(apiBaseUrl, id);
54
+ Object.assign(this.components, components);
11
55
  }
12
56
 
13
57
  createVueApp(rootComponent: Component, router?: Router, rootProps?: Record<string, any>): App {
14
58
  return super.createVueApp(rootComponent, router, rootProps).use(TDesign);
15
59
  }
16
60
 
61
+ private closeMessage() {
62
+ Toast.clear();
63
+ }
64
+
65
+ toast(message: string, theme?: string, options?: { duration?: number }): Promise<void> {
66
+ return new Promise<void>((resolve) => {
67
+ this.closeMessage();
68
+ Toast({
69
+ direction: 'column',
70
+ duration: options?.duration || 2000,
71
+ theme: theme as ToastTheme,
72
+ placement: 'middle',
73
+ message: message,
74
+ onClose: () => resolve(),
75
+ });
76
+ });
77
+ }
78
+
79
+ private dialogInstances: ModalComponentInstance[] = []; // 对话框实例堆栈
80
+ private drawerInstances: ModalComponentInstance[] = []; // 抽屉实例堆栈
81
+
82
+ dialog(content: string | Vue.Component,
83
+ title?: string,
84
+ options: DialogOptions = {},
85
+ contentProps: Record<string, any> = {}): ModalComponentInstance {
86
+ this.closeMessage();
87
+
88
+ let componentDefinition: VueComponent = Object.assign({}, Dialog);
89
+ if (this.isComponent(content)) {
90
+ const components = Object.assign({}, Dialog.components || {});
91
+ components['tnxtdm-dialog-content'] = content;
92
+ componentDefinition.components = components;
93
+ content = null;
94
+ }
95
+
96
+ const dialogId = 'dialog-' + (new Date().getTime());
97
+ const container = document.createElement('div');
98
+ container.className = dialogContainerClass;
99
+ container.id = dialogId;
100
+ document.body.appendChild(container);
101
+
102
+ const buttons = this.getOptionsButtons(options);
103
+ const containerSelector = '.' + dialogContainerClass + '#' + dialogId;
104
+ let dialogVm = this.createVueApp(componentDefinition, null, {
105
+ modelValue: true,
106
+ container: containerSelector,
107
+ title: title,
108
+ content: content,
109
+ contentProps: contentProps,
110
+ buttons: buttons,
111
+ theme: options.theme,
112
+ });
113
+ const dialog = dialogVm.mount(containerSelector) as ModalComponentInstance;
114
+ dialog.options = Object.assign(dialog.options || {}, options);
115
+ dialog.options.onClosed = this.util.function.around(dialog.options.onClosed, (onClosed: () => void): void => {
116
+ dialogVm.unmount();
117
+ this.dialogInstances.remove(dialog);
118
+
119
+ const containerElement = document.querySelector(containerSelector) as HTMLElement | null;
120
+ if (containerElement) {
121
+ containerElement.remove();
122
+ }
123
+
124
+ if (onClosed) {
125
+ onClosed.call(dialog);
126
+ }
127
+ });
128
+ this.dialogInstances.push(dialog);
129
+ return dialog;
130
+ }
131
+
132
+ closeDialog(all?: boolean): void {
133
+ if (this.dialogInstances.length) {
134
+ let dialog = this.dialogInstances.pop();
135
+ while (dialog) {
136
+ dialog.close();
137
+ if (all) {
138
+ dialog = this.dialogInstances.pop();
139
+ } else {
140
+ break;
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ drawer(content: string | Vue.Component,
147
+ title?: string,
148
+ options: DrawerOptions = {},
149
+ contentProps: Record<string, any> = {}): ModalComponentInstance {
150
+ this.closeMessage();
151
+
152
+ let componentDefinition: VueComponent = Object.assign({}, Drawer);
153
+ if (this.isComponent(content)) {
154
+ const components = Object.assign({}, Drawer.components || {});
155
+ components['tnxtdm-drawer-content'] = content;
156
+ componentDefinition.components = components;
157
+ content = null;
158
+ }
159
+
160
+ const drawerId = 'drawer-' + (new Date().getTime());
161
+ const container = document.createElement('div');
162
+ container.className = drawerContainerClass;
163
+ container.id = drawerId;
164
+ document.body.appendChild(container);
165
+
166
+ const buttons = this.getOptionsButtons(options);
167
+ const containerSelector = '.' + drawerContainerClass + '#' + drawerId;
168
+ let drawerVm = 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
+ placement: options.placement,
177
+ });
178
+ const drawer = drawerVm.mount(containerSelector) as ModalComponentInstance;
179
+ drawer.options = Object.assign(drawer.options || {}, options);
180
+ drawer.options.onClosed = this.util.function.around(drawer.options.onClosed, (onClosed: () => void): void => {
181
+ drawerVm.unmount();
182
+ this.drawerInstances.remove(drawer);
183
+
184
+ const containerElement = document.querySelector(containerSelector) as HTMLElement | null;
185
+ if (containerElement) {
186
+ containerElement.remove();
187
+ }
188
+
189
+ if (onClosed) {
190
+ onClosed.call(drawer);
191
+ }
192
+ });
193
+ this.drawerInstances.push(drawer);
194
+ return drawer;
195
+ }
196
+
197
+ closeDrawer(all?: boolean): void {
198
+ if (this.drawerInstances.length) {
199
+ let drawer = this.drawerInstances.pop();
200
+ while (drawer) {
201
+ drawer.close();
202
+ if (all) {
203
+ drawer = this.drawerInstances.pop();
204
+ } else {
205
+ break;
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ alert(message: string, options?: { title?: string, theme?: string, confirmButtonText?: string }): Promise<void> {
212
+ return new Promise<void>((resolve) => {
213
+ this.closeMessage();
214
+ let title = options?.title || '提示';
215
+ let theme = options?.theme || 'warning';
216
+ let iconName = 'info-circle';
217
+ let iconColor = 'var(--td-brand-color)';
218
+ let confirmBtn: any = {
219
+ content: options?.confirmButtonText || '确定',
220
+ theme: 'primary'
221
+ };
222
+
223
+ if (theme === 'success') {
224
+ iconName = 'check-circle';
225
+ iconColor = 'var(--td-success-color)';
226
+ confirmBtn.theme = 'success';
227
+ } else if (theme === 'error') {
228
+ iconName = 'close-circle';
229
+ iconColor = 'var(--td-error-color)';
230
+ confirmBtn.theme = 'danger';
231
+ } else if (theme === 'warning') {
232
+ iconName = 'error-circle';
233
+ iconColor = 'var(--td-warning-color)';
234
+ confirmBtn.theme = 'warning';
235
+ } else if (theme === 'info') {
236
+ iconName = 'info-circle';
237
+ iconColor = 'var(--td-info-color)';
238
+ confirmBtn.theme = 'info';
239
+ }
240
+
241
+ let maxZIndex = 0;
242
+ const allElements = document.getElementsByTagName('*');
243
+ for (let i = 0; i < allElements.length; i++) {
244
+ const zIndex = parseInt(window.getComputedStyle(allElements[i]).zIndex);
245
+ if (!isNaN(zIndex) && zIndex > maxZIndex) {
246
+ maxZIndex = zIndex;
247
+ }
248
+ }
249
+
250
+ TDialogPlugin.alert({
251
+ title: (h) => {
252
+ return h('div', {
253
+ style: {
254
+ display: 'flex',
255
+ alignItems: 'center',
256
+ justifyContent: 'center'
257
+ }
258
+ }, [
259
+ h(this.components.Icon, {
260
+ name: iconName,
261
+ style: {marginRight: '6px', color: iconColor, fontSize: '20px'}
262
+ }),
263
+ h('span', title)
264
+ ]);
265
+ },
266
+ content: message,
267
+ confirmBtn: confirmBtn,
268
+ zIndex: maxZIndex + 2,
269
+ showOverlay: true,
270
+ overlayProps: {
271
+ zIndex: maxZIndex + 1
272
+ },
273
+ destroyOnClose: true,
274
+ onConfirm: () => {
275
+ resolve();
276
+ },
277
+ onClose: () => {
278
+ resolve();
279
+ }
280
+ });
281
+ });
282
+ }
283
+
284
+ success(message: string, options?: { title?: string, confirmButtonText?: string }): Promise<void> {
285
+ return this.alert(message, Object.assign({title: '成功', theme: 'success'}, options));
286
+ }
287
+
288
+ error(message: string, options?: { title?: string, confirmButtonText?: string }): Promise<void> {
289
+ return this.alert(message, Object.assign({title: '错误', theme: 'error'}, options));
290
+ }
291
+
292
+ confirm(message: string, options?: { title?: string }): Promise<boolean> {
293
+ return new Promise<boolean>((resolve) => {
294
+ this.closeMessage();
295
+ TDialogPlugin.confirm({
296
+ title: options?.title || '确认',
297
+ content: message,
298
+ confirmBtn: '确定',
299
+ cancelBtn: '取消',
300
+ onConfirm: () => {
301
+ resolve(true);
302
+ },
303
+ onCancel: () => {
304
+ resolve(false);
305
+ },
306
+ onClose: () => {
307
+ resolve(false);
308
+ }
309
+ });
310
+ });
311
+ }
312
+
313
+ showLoading(message?: string): Promise<void> {
314
+ return this.toast(message, 'loading', {duration: 0});
315
+ }
316
+
317
+ closeLoading(): void {
318
+ Toast.clear();
319
+ }
320
+
17
321
  }
@@ -32,6 +32,7 @@ export default class TnxTdValidator extends Validator {
32
32
  // if (fieldMeta && fieldMeta.caption) {
33
33
  // fieldCaption = fieldMeta.caption;
34
34
  // }
35
+ let _this = this;
35
36
  switch (validationName) {
36
37
  case 'required':
37
38
  case 'notNull':
@@ -47,7 +48,7 @@ export default class TnxTdValidator extends Validator {
47
48
  blank = fieldValue.trim().length === 0;
48
49
  }
49
50
  if (blank) {
50
- let message = this.getErrorMessage(validationName, fieldCaption);
51
+ let message = _this.getErrorMessage(validationName, fieldCaption);
51
52
  return {result: false, message, type: 'error'};
52
53
  }
53
54
  }
@@ -63,7 +64,7 @@ export default class TnxTdValidator extends Validator {
63
64
  let enterLength = fieldValue.indexOf('\n') < 0 ? 0 : (fieldValue.match(/\n/g) as any).length;
64
65
  let fieldLength = fieldValue.length + enterLength;
65
66
  if (fieldLength < validationValue) {
66
- let message = this.getErrorMessage(validationName, fieldCaption,
67
+ let message = _this.getErrorMessage(validationName, fieldCaption,
67
68
  validationValue, validationValue - fieldLength);
68
69
  return {result: false, message, type: 'error'};
69
70
  }
@@ -79,7 +80,7 @@ export default class TnxTdValidator extends Validator {
79
80
  let enterLength = fieldValue.indexOf('\n') < 0 ? 0 : (fieldValue.match(/\n/g) as any).length;
80
81
  let fieldLength = fieldValue.length + enterLength;
81
82
  if (fieldLength > validationValue) {
82
- let message = this.getErrorMessage(validationName, fieldCaption,
83
+ let message = _this.getErrorMessage(validationName, fieldCaption,
83
84
  validationValue, fieldLength - validationValue);
84
85
  return {result: false, message, type: 'error'};
85
86
  }
@@ -94,7 +95,7 @@ export default class TnxTdValidator extends Validator {
94
95
  validator(fieldValue: string): ValidateResult {
95
96
  if (fieldValue && typeof fieldValue === 'string') {
96
97
  if (!validator.isNumeric(fieldValue, {no_symbols: true})) {
97
- let message = this.getErrorMessage(validationName, fieldCaption);
98
+ let message = _this.getErrorMessage(validationName, fieldCaption);
98
99
  return {result: false, message, type: 'error'};
99
100
  }
100
101
  }
@@ -112,7 +113,7 @@ export default class TnxTdValidator extends Validator {
112
113
  for (let i = 0; i < limitedValues.length; i++) {
113
114
  if (fieldValue.indexOf(limitedValues[i]) >= 0) {
114
115
  let s = limitedValues.join(' ');
115
- let message = this.getErrorMessage('notContains', fieldCaption, s);
116
+ let message = _this.getErrorMessage('notContains', fieldCaption, s);
116
117
  return {result: false, message, type: 'error'};
117
118
  }
118
119
  }
@@ -131,7 +132,7 @@ export default class TnxTdValidator extends Validator {
131
132
  for (let i = 0; i < limitedValues.length; i++) {
132
133
  if (fieldValue.indexOf(limitedValues[i]) >= 0) {
133
134
  let s = limitedValues.join(' ');
134
- let message = this.getErrorMessage('notContains', fieldCaption, s);
135
+ let message = _this.getErrorMessage('notContains', fieldCaption, s);
135
136
  return {result: false, message, type: 'error'};
136
137
  }
137
138
  }
@@ -149,7 +150,7 @@ export default class TnxTdValidator extends Validator {
149
150
  let limitedValues = (validationValue as string).split(' ');
150
151
  for (let i = 0; i < limitedValues.length; i++) {
151
152
  if (validator.contains(fieldValue, limitedValues[i])) {
152
- let message = this.getErrorMessage('notContains', fieldCaption,
153
+ let message = _this.getErrorMessage('notContains', fieldCaption,
153
154
  validationValue);
154
155
  return {result: false, message, type: 'error'};
155
156
  }
@@ -166,7 +167,7 @@ export default class TnxTdValidator extends Validator {
166
167
  validator(fieldValue: string): ValidateResult {
167
168
  if (fieldValue) {
168
169
  if (/<[a-z]+[ ]*[/]?[ ]*>/gi.test(fieldValue)) {
169
- let message = this.getErrorMessage(validationName, fieldCaption);
170
+ let message = _this.getErrorMessage(validationName, fieldCaption);
170
171
  return {result: false, message, type: 'error'};
171
172
  }
172
173
  }
@@ -193,7 +194,7 @@ export default class TnxTdValidator extends Validator {
193
194
  tag = tag.substring(1);
194
195
  }
195
196
  if (!tags.contains(tag.toLowerCase())) {
196
- let message = this.getErrorMessage(validationName, fieldCaption,
197
+ let message = _this.getErrorMessage(validationName, fieldCaption,
197
198
  tags.join(', '));
198
199
  return {result: false, message, type: 'error'};
199
200
  }
@@ -217,7 +218,7 @@ export default class TnxTdValidator extends Validator {
217
218
  let value = fieldValue.toLowerCase();
218
219
  for (let tag of tags) {
219
220
  if (value.includes('<' + tag + '>') || value.includes('<' + tag + ' ')) {
220
- let message = this.getErrorMessage(validationName, fieldCaption,
221
+ let message = _this.getErrorMessage(validationName, fieldCaption,
221
222
  tags.join(', '));
222
223
  return {result: false, message, type: 'error'};
223
224
  }
@@ -235,7 +236,7 @@ export default class TnxTdValidator extends Validator {
235
236
  validator(fieldValue: string): ValidateResult {
236
237
  if (typeof fieldValue === 'string' && fieldValue) {
237
238
  if (!validator.isEmail(fieldValue)) {
238
- let message = this.getErrorMessage(validationName, fieldCaption);
239
+ let message = _this.getErrorMessage(validationName, fieldCaption);
239
240
  return {result: false, message, type: 'error'};
240
241
  }
241
242
  }
@@ -251,7 +252,7 @@ export default class TnxTdValidator extends Validator {
251
252
  rule = {
252
253
  validator(fieldValue: string): ValidateResult {
253
254
  if (validationValue) {
254
- let message = this.validateRegExp(validationName, fieldValue, fieldCaption);
255
+ let message = _this.validateRegExp(validationName, fieldValue, fieldCaption);
255
256
  if (message) {
256
257
  return {result: false, message, type: 'error'};
257
258
  }
@@ -277,7 +278,7 @@ export default class TnxTdValidator extends Validator {
277
278
  if (message) {
278
279
  message = fieldCaption + message;
279
280
  } else {
280
- message = this.getErrorMessage('regex', fieldCaption, '');
281
+ message = _this.getErrorMessage('regex', fieldCaption, '');
281
282
  }
282
283
  return {result: false, message, type: 'error'};
283
284
  }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * tnxtd.css
3
+ */
4
+ :root {
5
+ --border: 1px solid var(--td-component-border);
6
+ }
7
+
8
+ body {
9
+ color: var(--td-text-color-primary);
10
+ }
11
+
12
+ .border {
13
+ border: var(--border);
14
+ }
15
+
16
+ .border-top {
17
+ border-top: var(--border);
18
+ }
19
+
20
+ .border-bottom {
21
+ border-bottom: var(--border);
22
+ }
23
+
24
+ .border-left {
25
+ border-left: var(--border);
26
+ }
27
+
28
+ .border-right {
29
+ border-right: var(--border);
30
+ }
31
+
32
+ .border-top-0,
33
+ .border-top-0::before,
34
+ .border-top-0 .t-cell-group--bordered::before {
35
+ border-top: none !important;
36
+ }
37
+
38
+ .border-bottom-0,
39
+ .border-bottom-0::after,
40
+ .border-bottom-0 .t-cell-group--bordered::after {
41
+ border-bottom: none !important;
42
+ }
43
+
44
+ .shadow {
45
+ box-shadow: var(--td-shadow-1);
46
+ }
47
+
48
+ .bg-page {
49
+ background-color: var(--td-bg-color-page);
50
+ }
51
+
52
+ .bg-container {
53
+ background-color: var(--td-bg-color-container);
54
+ }
55
+
56
+ .bg-component {
57
+ background-color: var(--td-bg-color-component);
58
+ }
59
+
60
+ .text-brand,
61
+ .color-brand {
62
+ color: var(--td-text-color-brand);
63
+ }
64
+
65
+ .text-primary,
66
+ .color-primary {
67
+ color: var(--td-text-color-primary);
68
+ }
69
+
70
+ .text-secondary,
71
+ .color-secondary {
72
+ color: var(--td-text-color-secondary);
73
+ }
74
+
75
+ .text-placeholder,
76
+ .color-placeholder {
77
+ color: var(--td-text-color-placeholder);
78
+ }
79
+
80
+ .text-disabled,
81
+ .color-disabled {
82
+ color: var(--td-text-color-disabled);
83
+ }
84
+
85
+ .t-icon[theme="success"] {
86
+ color: var(--td-success-color);
87
+ }
88
+
89
+ .t-button--size-large .t-icon + .t-button__content:not(:empty) {
90
+ margin-left: 0.5rem;
91
+ }
92
+
93
+ .t-form__item--left .t-form__label,
94
+ .t-form__item--right .t-form__label {
95
+ height: 100%;
96
+ display: flex;
97
+ align-items: center;
98
+ }
@@ -1,15 +1,19 @@
1
1
  import TnxVue from '../tnxvue.ts';
2
2
  import {Icon} from 'tdesign-icons-vue-next';
3
3
  import Validator from './tnxtd-validator.ts';
4
+ import './tnxtd.css';
4
5
 
5
6
  export {Validator};
6
7
 
7
8
  export default class TnxTd extends TnxVue {
8
9
 
10
+ validator: Validator;
11
+
9
12
  constructor(apiBaseUrl: string, id: string = 'tnxtd') {
10
13
  super(apiBaseUrl, id);
11
14
 
12
15
  this.foundations.Validator = Validator;
16
+ this.validator = new Validator();
13
17
 
14
18
  Object.assign(this.components, {
15
19
  Icon,
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import {
5
5
  createRouter,
6
+ createWebHistory,
6
7
  createWebHashHistory,
7
8
  Router,
8
9
  RouteRecordRaw,
@@ -17,12 +18,14 @@ import * as NetUtil from '../../tnxcore/src/util/net.ts';
17
18
  export type RouteItem = {
18
19
  caption: string;
19
20
  path: string;
21
+ anonymous?: boolean; // 是否可以匿名访问
20
22
  icon?: string;
21
23
  page?: string;
22
24
  component?: () => Promise<any>;
23
25
  redirect?: never;
24
26
  alias?: string;
25
27
  subs?: RouteItem[];
28
+ parent?: RouteItem;
26
29
  }
27
30
 
28
31
  function addRoute(routes: RouteRecordRaw[], superiorPath: string, item: RouteItem, fnImportPage?: (page: string) => Promise<any>): void {
@@ -87,21 +90,51 @@ function getCurrentRoute(router: Router): RouteLocationNormalizedLoadedGeneric {
87
90
  return router.currentRoute.value;
88
91
  }
89
92
 
93
+ function findItemByPath(parent: RouteItem, items: RouteItem[], path: string): RouteItem | null {
94
+ for (const item of items) {
95
+ if (item.path === path) {
96
+ item.parent = parent;
97
+ return item;
98
+ }
99
+ if (item.subs) {
100
+ const found = findItemByPath(item, item.subs, path);
101
+ if (found) {
102
+ return found;
103
+ }
104
+ }
105
+ }
106
+ return null;
107
+ }
108
+
109
+ export class RouteMenu {
110
+
111
+ items: RouteItem[];
112
+
113
+ constructor(items: RouteItem[]) {
114
+ this.items = items;
115
+ }
116
+
117
+ findItem(path: string): RouteItem | null {
118
+ return findItemByPath(null, this.items, path);
119
+ }
120
+
121
+ }
122
+
90
123
  export type VueRouter = Router & {
91
124
  history: RouterHistory;
92
125
  prev?: RouteLocationNormalizedLoaded;
93
- $beforeLeaveHandlers: Record<string, (to: RouteLocationNormalized) => boolean>;
126
+ $beforeLeaveHandlers: Record<string, (to: RouteLocationNormalized) => boolean | Promise<boolean>>;
94
127
  beforeLeave: (handler: (to: RouteLocationNormalized) => boolean) => void;
95
128
  pushState: (path: string) => boolean;
96
129
  replaceState: (path: string) => boolean;
97
130
  backTo: (path?: string) => void;
98
131
  }
99
132
 
100
- export default function (items: RouteItem[], fnImportPage?: (page: string) => Promise<any>): VueRouter {
133
+ export default function (items: RouteItem[], useHashHistory = true, fnImportPage?: (page: string) => Promise<any>): VueRouter {
101
134
  const routes: RouteRecordRaw[] = [];
102
135
  applyItemsToRoutes('', items, routes, fnImportPage);
103
136
 
104
- const routerHistory = createWebHashHistory();
137
+ const routerHistory = useHashHistory ? createWebHashHistory() : createWebHistory();
105
138
  const router: VueRouter = createRouter({
106
139
  history: routerHistory,
107
140
  routes,
@@ -121,7 +154,7 @@ export default function (items: RouteItem[], fnImportPage?: (page: string) => Pr
121
154
 
122
155
  // 注册离开页面前事件处理支持
123
156
  router.$beforeLeaveHandlers = {};
124
- router.beforeLeave = function (handler: (to: RouteLocationNormalized) => boolean): void {
157
+ router.beforeLeave = function (handler: (to: RouteLocationNormalized) => boolean | Promise<boolean>): void {
125
158
  let $route = getCurrentRoute(router);
126
159
  let path = $route.path;
127
160
  router.$beforeLeaveHandlers[path] = handler;
@@ -130,8 +163,29 @@ export default function (items: RouteItem[], fnImportPage?: (page: string) => Pr
130
163
  router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded, next: NavigationGuardNext): void => {
131
164
  let allow = true;
132
165
  let beforeLeaveHandler = router.$beforeLeaveHandlers[from.path];
166
+ if (!beforeLeaveHandler) {
167
+ let toItem = findItemByPath(null, items, to.path);
168
+ if (toItem && !toItem.anonymous) {
169
+ beforeLeaveHandler = async (to: RouteLocationNormalized): Promise<boolean> => {
170
+ let logined = await window.tnx.auth.isLogined();
171
+ if (!logined) {
172
+ let url = window.tnx.util.net.appendParams(to.path, to.query);
173
+ window.tnx.api.toLogin(url);
174
+ }
175
+ return logined;
176
+ };
177
+ }
178
+ }
133
179
  if (beforeLeaveHandler) {
134
- if (beforeLeaveHandler(to) === false) {
180
+ let result = beforeLeaveHandler(to);
181
+ if (result instanceof Promise) {
182
+ result.then((allowed: boolean): void => {
183
+ if (allowed) {
184
+ next();
185
+ }
186
+ });
187
+ return;
188
+ } else if (result === false) {
135
189
  allow = false;
136
190
  }
137
191
  }