@truenewx/tnxvue3 2.6.0

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 (60) hide show
  1. package/README.md +3 -0
  2. package/package.json +76 -0
  3. package/sample/App.vue +19 -0
  4. package/sample/main.js +11 -0
  5. package/sample/pages/index.vue +79 -0
  6. package/sample/pages/info.vue +28 -0
  7. package/sample/tnx.js +31 -0
  8. package/src/aj-captcha/Verify/VerifyPoints.vue +258 -0
  9. package/src/aj-captcha/Verify/VerifySlide.vue +379 -0
  10. package/src/aj-captcha/Verify.vue +375 -0
  11. package/src/aj-captcha/api/index.js +19 -0
  12. package/src/aj-captcha/utils/ase.js +11 -0
  13. package/src/aj-captcha/utils/util.js +35 -0
  14. package/src/ant-design/tnxad-theme.css +5 -0
  15. package/src/ant-design/tnxad.css +8 -0
  16. package/src/ant-design/tnxad.js +23 -0
  17. package/src/element-plus/alert/Alert.vue +112 -0
  18. package/src/element-plus/avatar/Avatar.vue +124 -0
  19. package/src/element-plus/button/Button.vue +184 -0
  20. package/src/element-plus/check-icon/CheckIcon.vue +61 -0
  21. package/src/element-plus/close-error-button/CloseErrorButton.vue +45 -0
  22. package/src/element-plus/curd/Curd.vue +224 -0
  23. package/src/element-plus/date-picker/DatePicker.vue +206 -0
  24. package/src/element-plus/date-range/DateRange.vue +78 -0
  25. package/src/element-plus/datetime-picker/DateTimePicker.vue +129 -0
  26. package/src/element-plus/detail-form/DetailForm.vue +88 -0
  27. package/src/element-plus/dialog/Dialog.vue +259 -0
  28. package/src/element-plus/dialog/DialogContent.vue +13 -0
  29. package/src/element-plus/drawer/Drawer.vue +175 -0
  30. package/src/element-plus/dropdown-item/DropdownItem.vue +30 -0
  31. package/src/element-plus/enum-select/EnumSelect.vue +125 -0
  32. package/src/element-plus/fetch-cascader/FetchCascader.vue +138 -0
  33. package/src/element-plus/fetch-select/FetchSelect.vue +166 -0
  34. package/src/element-plus/fetch-tags/FetchTags.vue +122 -0
  35. package/src/element-plus/fss-upload/FssUpload.vue +306 -0
  36. package/src/element-plus/fss-view/FssView.vue +163 -0
  37. package/src/element-plus/icon/Icon.vue +221 -0
  38. package/src/element-plus/input-number/InputNumber.vue +150 -0
  39. package/src/element-plus/paged/Paged.vue +76 -0
  40. package/src/element-plus/permission-tree/PermissionTree.vue +184 -0
  41. package/src/element-plus/query-form/QueryForm.vue +138 -0
  42. package/src/element-plus/query-table/QueryTable.vue +402 -0
  43. package/src/element-plus/region-cascader/RegionCascader.vue +108 -0
  44. package/src/element-plus/select/Select.vue +446 -0
  45. package/src/element-plus/slider/Slider.vue +88 -0
  46. package/src/element-plus/steps-nav/StepsNav.vue +57 -0
  47. package/src/element-plus/submit-form/SubmitForm.vue +236 -0
  48. package/src/element-plus/table-column/TableColumn.vue +32 -0
  49. package/src/element-plus/tabs/Tabs.vue +93 -0
  50. package/src/element-plus/tnxel.css +890 -0
  51. package/src/element-plus/tnxel.js +528 -0
  52. package/src/element-plus/transfer/Transfer.vue +117 -0
  53. package/src/element-plus/upload/Upload.vue +856 -0
  54. package/src/percent/Percent.vue +12 -0
  55. package/src/text/Text.vue +33 -0
  56. package/src/tnxvue-cli.js +64 -0
  57. package/src/tnxvue-router.js +161 -0
  58. package/src/tnxvue-validator.js +365 -0
  59. package/src/tnxvue.css +12 -0
  60. package/src/tnxvue.js +343 -0
@@ -0,0 +1,259 @@
1
+ <template>
2
+ <el-dialog class="tnxel-dialog"
3
+ v-model="visible"
4
+ destroy-on-close
5
+ append-to-body
6
+ :modal="options.modal"
7
+ :close-on-click-modal="options['close-on-click-modal']"
8
+ :close-on-press-escape="options['close-on-press-escape']"
9
+ :show-close="options['show-close']"
10
+ :center="options.center"
11
+ :before-close="beforeClose"
12
+ @closed="onClosed">
13
+ <template #header>
14
+ <div class="tnxel-dialog-title" :class="mergeClass({'border-bottom': title})" v-html="title"
15
+ v-if="title || options['show-close']"></div>
16
+ </template>
17
+ <template v-if="$slots.default">
18
+ <slot></slot>
19
+ </template>
20
+ <div :class="mergeClass()" v-html="contentValue" v-else-if="contentValue"></div>
21
+ <tnxel-dialog-content :class="mergeClass()" ref="content" v-bind="contentProps" v-else></tnxel-dialog-content>
22
+ <template #footer v-if="buttons && buttons.length">
23
+ <div class="tnxel-dialog-footer" :class="mergeClass()">
24
+ <el-button v-for="(button, index) in buttons" :type="button.type" :key="index"
25
+ @click="btnClick(index)">{{ button.caption || button.text }}
26
+ </el-button>
27
+ </div>
28
+ </template>
29
+ </el-dialog>
30
+ </template>
31
+
32
+ <script>
33
+ import $ from 'jquery';
34
+ import DialogContent from './DialogContent.vue';
35
+
36
+ const util = window.tnx.util;
37
+
38
+ export default {
39
+ name: 'TnxelDialog',
40
+ components: {
41
+ 'tnxel-dialog-content': DialogContent,
42
+ },
43
+ props: {
44
+ modelValue: Boolean,
45
+ container: String,
46
+ title: String,
47
+ content: String,
48
+ contentProps: Object,
49
+ buttons: Array,
50
+ theme: String,
51
+ width: {
52
+ type: [Number, String],
53
+ default: 512,
54
+ },
55
+ },
56
+ emits: ['update:modelValue', 'shown', 'closed'],
57
+ data() {
58
+ return {
59
+ visible: this.modelValue,
60
+ contentValue: this.content,
61
+ options: {
62
+ modal: true, // 是否需要遮罩层
63
+ 'close-on-click-modal': false, // 是否可以通过点击遮罩层关闭对话框
64
+ 'close-on-press-escape': true, // 是否可以通过按下 ESC 关闭对话框
65
+ 'show-close': true, // 是否显示关闭按钮
66
+ center: false, // 是否对头部和底部采用居中布局
67
+ width: this.width,
68
+ // 以上均为element的Dialog组件配置项
69
+ onShown: undefined, // 对话框展示后的事件回调
70
+ onClosed: undefined, // 对话框关闭后的事件回调
71
+ },
72
+ middleTop: '40vh',
73
+ heightChangeObserver: null,
74
+ };
75
+ },
76
+ computed: {
77
+ dialogTop() {
78
+ if (typeof this.options.top === 'function') {
79
+ return this.options.top();
80
+ } else if (this.options.top) {
81
+ return this.options.top;
82
+ } else {
83
+ return this.middleTop;
84
+ }
85
+ },
86
+ dialogWidth() {
87
+ if (typeof this.options.width === 'function') {
88
+ return this.options.width();
89
+ } else if (typeof this.options.width === 'number') {
90
+ return this.options.width + 'px';
91
+ } else {
92
+ return this.options.width;
93
+ }
94
+ },
95
+ },
96
+ watch: {
97
+ modelValue(newValue, oldValue) {
98
+ this.visible = this.modelValue;
99
+ if (newValue && !oldValue) { // 从隐藏到显示
100
+ let vm = this;
101
+ this.$nextTick(function () {
102
+ vm.locate(true);
103
+ });
104
+ }
105
+ },
106
+ visible() {
107
+ this.$emit('update:modelValue', this.visible);
108
+ },
109
+ },
110
+ mounted() {
111
+ let vm = this;
112
+ this.$nextTick(function () {
113
+ if (this.visible) {
114
+ vm.locate(true);
115
+ }
116
+
117
+ if (vm.$refs.content && !vm.$refs.content.close) {
118
+ vm.$refs.content.close = function () {
119
+ vm.close();
120
+ }
121
+ }
122
+
123
+ if (typeof vm.options.onShown === 'function') {
124
+ vm.options.onShown.call(this);
125
+ } else {
126
+ vm.$emit('shown');
127
+ }
128
+ });
129
+ },
130
+ methods: {
131
+ mergeClass(classObject) {
132
+ classObject = classObject || {};
133
+ if (this.theme) {
134
+ classObject['theme-' + this.theme] = true;
135
+ }
136
+ return classObject;
137
+ },
138
+ locate(observe) {
139
+ let $dialog = undefined;
140
+ if (this.container) {
141
+ let $container = $(this.container);
142
+ if ($container.length) {
143
+ $dialog = $container.next('.el-overlay').find('.el-dialog');
144
+ }
145
+ }
146
+ if (!$dialog?.length) {
147
+ $dialog = $('.el-dialog:last');
148
+ }
149
+ if ($dialog.length) {
150
+ const height = $dialog.height();
151
+ const docHeight = window.tnx.util.dom.getDocHeight();
152
+ // 对话框高度占文档高度的比例
153
+ const heightRatio = height / docHeight;
154
+ // 为了获得更好的视觉舒适度,根据高度比确定对话框中线位置:从33vh->50vh
155
+ const baseline = 33 + (50 - 33) * heightRatio;
156
+ const baseTop = docHeight * baseline / 100;
157
+ let top = (baseTop - height / 2);
158
+ top = Math.max(top, 8); // 至少顶部留8px空隙
159
+ this.middleTop = top + 'px';
160
+ $dialog.css({
161
+ 'margin-top': this.dialogTop,
162
+ 'width': this.dialogWidth,
163
+ 'max-height': 'calc(100vh - 16px)', // 最大高度时上下各留8px空隙
164
+ });
165
+
166
+ if (observe) {
167
+ this.heightChangeObserver = util.dom.observeHeightChange($dialog[0], this.locate);
168
+ }
169
+ }
170
+ },
171
+ btnClick(index) {
172
+ const button = this.buttons[index];
173
+ if (button && typeof button.click === 'function') {
174
+ if (button.click.call(this.$refs.content, this.close) === false) {
175
+ return;
176
+ }
177
+ }
178
+ this.close();
179
+ },
180
+ close(callback) {
181
+ const vm = this;
182
+ this.beforeClose(function () {
183
+ if (typeof callback === 'function') {
184
+ vm.options.onClosed = util.function.around(vm.options.onClosed, function (onClosed) {
185
+ if (onClosed) {
186
+ onClosed();
187
+ }
188
+ callback();
189
+ });
190
+ }
191
+ vm.visible = false;
192
+ });
193
+ },
194
+ beforeClose(done) {
195
+ if (typeof this.options.beforeClose === 'function') {
196
+ if (!this.options.beforeClose()) {
197
+ return;
198
+ }
199
+ }
200
+ done();
201
+ },
202
+ onClosed() {
203
+ if (typeof this.options.onClosed === 'function') {
204
+ this.options.onClosed.call(this.$refs.content);
205
+ } else {
206
+ this.$emit('closed');
207
+ }
208
+ if (this.heightChangeObserver) {
209
+ this.heightChangeObserver.disconnect();
210
+ }
211
+ }
212
+ }
213
+ }
214
+ </script>
215
+
216
+ <style>
217
+ .tnxel-dialog {
218
+ display: none;
219
+ }
220
+
221
+ .tnxel-dialog.el-dialog {
222
+ display: flex;
223
+ flex-direction: column;
224
+ margin-bottom: 0;
225
+ }
226
+
227
+ .tnxel-dialog .el-dialog__header {
228
+ padding: 0;
229
+ }
230
+
231
+ .tnxel-dialog .el-dialog__header .el-dialog__headerbtn {
232
+ width: 48px;
233
+ height: 48px;
234
+ }
235
+
236
+ .tnxel-dialog .tnxel-dialog-title {
237
+ padding: 0.75rem 1rem;
238
+ font-size: 1rem;
239
+ }
240
+
241
+ .tnxel-dialog .tnxel-dialog-title > :last-child,
242
+ .tnxel-dialog .el-dialog__body > div > :last-child {
243
+ margin-bottom: 0;
244
+ }
245
+
246
+ .tnxel-dialog .el-dialog__footer {
247
+ padding: 0;
248
+ }
249
+
250
+ .tnxel-dialog .tnxel-dialog-footer {
251
+ padding: 1rem;
252
+ display: flex;
253
+ flex-direction: row-reverse;
254
+ }
255
+
256
+ .tnxel-dialog .el-dialog__footer .el-button {
257
+ margin-left: 10px;
258
+ }
259
+ </style>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <div></div>
3
+ </template>
4
+
5
+ <script>
6
+ export default {
7
+ components: {},
8
+ data() {
9
+ return {};
10
+ },
11
+ methods: {}
12
+ }
13
+ </script>
@@ -0,0 +1,175 @@
1
+ <template>
2
+ <el-drawer
3
+ v-model="visible"
4
+ destroy-on-close
5
+ append-to-body
6
+ :size="options.width"
7
+ :modal="options.modal"
8
+ :close-on-click-modal="options['close-on-click-modal']"
9
+ :close-on-press-escape="options['close-on-press-escape']"
10
+ :show-close="options['show-close']"
11
+ :center="options.center"
12
+ :before-close="beforeClose"
13
+ :with-header="!!(title || options['show-close'])"
14
+ @closed="onClosed">
15
+ <template #title>
16
+ <div class="tnxel-drawer-title" :class="themeClass" v-html="title"></div>
17
+ </template>
18
+ <div class="tnxel-drawer-main" :class="themeClass">
19
+ <div class="tnxel-drawer-content">
20
+ <div v-html="contentValue" v-if="contentValue"/>
21
+ <tnxel-drawer-content ref="content" v-bind="contentProps" v-else>
22
+ </tnxel-drawer-content>
23
+ </div>
24
+ <div class="tnxel-drawer-footer" v-if="buttons && buttons.length">
25
+ <el-button v-for="(button, index) in buttons" :type="button.type" :key="index"
26
+ @click="btnClick(index)">{{ button.caption || button.text }}
27
+ </el-button>
28
+ </div>
29
+ </div>
30
+ </el-drawer>
31
+ </template>
32
+
33
+ <script>
34
+ const util = window.tnx.util;
35
+
36
+ export default {
37
+ name: 'TnxelDrawer',
38
+ components: {
39
+ 'tnxel-drawer-content': null,
40
+ },
41
+ props: {
42
+ title: String,
43
+ content: String,
44
+ contentProps: Object,
45
+ buttons: Array,
46
+ theme: String,
47
+ },
48
+ data() {
49
+ return {
50
+ visible: true,
51
+ contentValue: this.content,
52
+ options: {
53
+ modal: true, // 是否需要遮罩层
54
+ 'close-on-click-modal': false, // 是否可以通过点击遮罩层关闭对话框
55
+ 'close-on-press-escape': true, // 是否可以通过按下 ESC 关闭对话框
56
+ 'show-close': true, // 是否显示关闭按钮
57
+ center: false, // 是否对头部和底部采用居中布局
58
+ // 以上均为element的Drawer组件配置项
59
+ width: undefined,
60
+ onShown: undefined, // 对话框展示后的事件回调
61
+ onClosed: undefined, // 对话框关闭后的事件回调
62
+ },
63
+ };
64
+ },
65
+ mounted() {
66
+ let vm = this;
67
+ this.$nextTick(function () {
68
+ if (vm.$refs.content && !vm.$refs.content.close) {
69
+ vm.$refs.content.close = function () {
70
+ vm.close();
71
+ }
72
+ }
73
+ if (typeof vm.options.onShown === 'function') {
74
+ vm.options.onShown.call(this);
75
+ }
76
+ });
77
+ },
78
+ computed: {
79
+ themeClass() {
80
+ let classObject = {};
81
+ if (this.theme) {
82
+ classObject['theme-' + this.theme] = true;
83
+ }
84
+ return classObject;
85
+ },
86
+ },
87
+ methods: {
88
+ btnClick(index) {
89
+ const button = this.buttons[index];
90
+ if (button && typeof button.click === 'function') {
91
+ if (button.click.call(this.$refs.content, this.close) === false) {
92
+ return;
93
+ }
94
+ }
95
+ this.close();
96
+ },
97
+ close(callback) {
98
+ const vm = this;
99
+ this.beforeClose(function () {
100
+ if (typeof callback === 'function') {
101
+ vm.options.onClosed = util.function.around(vm.options.onClosed, function (onClosed) {
102
+ if (onClosed) {
103
+ onClosed();
104
+ }
105
+ callback();
106
+ });
107
+ }
108
+ vm.visible = false;
109
+ });
110
+ },
111
+ beforeClose(done) {
112
+ if (typeof this.options.beforeClose === 'function') {
113
+ if (!this.options.beforeClose()) {
114
+ return;
115
+ }
116
+ }
117
+ done();
118
+ },
119
+ onClosed() {
120
+ if (typeof this.options.onClosed === 'function') {
121
+ this.options.onClosed.call(this.$refs.content);
122
+ }
123
+ }
124
+ }
125
+ }
126
+ </script>
127
+
128
+ <style>
129
+ .el-drawer__header {
130
+ padding: 0;
131
+ margin-bottom: 0;
132
+ color: unset;
133
+ }
134
+
135
+ .el-drawer__header .el-drawer__close-btn {
136
+ margin-top: -0.5rem;
137
+ margin-right: 0.5rem;
138
+ color: var(--el-text-color-secondary);
139
+ }
140
+
141
+ .tnxel-drawer-title {
142
+ padding: 0.75rem 1rem;
143
+ font-size: 1rem;
144
+ }
145
+
146
+ .el-drawer__body {
147
+ padding: 0;
148
+ flex-grow: 1;
149
+ max-height: calc(100vh - 60px);
150
+ }
151
+
152
+ .tnxel-drawer-main {
153
+ height: 100%;
154
+ display: flex;
155
+ flex-direction: column;
156
+ }
157
+
158
+ .tnxel-drawer-main .tnxel-drawer-content {
159
+ flex-grow: 1;
160
+ padding: 1rem 1rem 0 1rem;
161
+ overflow: auto;
162
+ }
163
+
164
+ .tnxel-drawer-footer {
165
+ padding: 1rem;
166
+ display: flex;
167
+ flex-direction: row-reverse;
168
+ justify-content: flex-start;
169
+ }
170
+
171
+ .tnxel-drawer-footer .el-button {
172
+ margin-left: 0;
173
+ margin-right: 0.5rem;
174
+ }
175
+ </style>
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <el-dropdown-item :disabled="disabled">
3
+ <el-tooltip :content="tooltip" :placement="tooltipPlacement" :offset="32" :disabled="!tooltip">
4
+ <div class="w-100 flex-v-center" :title="title" v-if="icon">
5
+ <tnxel-icon :value="icon" :size="iconSize"/>
6
+ <span class="w-100"><slot></slot></span>
7
+ </div>
8
+ <div class="w-100" :title="title" v-else>
9
+ <slot></slot>
10
+ </div>
11
+ </el-tooltip>
12
+ </el-dropdown-item>
13
+ </template>
14
+
15
+ <script>
16
+ export default {
17
+ name: 'TnxelDropdownItem',
18
+ props: {
19
+ icon: String,
20
+ iconSize: Number,
21
+ title: String,
22
+ tooltip: String,
23
+ tooltipPlacement: {
24
+ type: String,
25
+ default: 'right',
26
+ },
27
+ disabled: Boolean,
28
+ },
29
+ }
30
+ </script>
@@ -0,0 +1,125 @@
1
+ <template>
2
+ <tnxel-fetch-cascader v-model="model" url="/api/meta/enums" value-name="key" text-name="caption"
3
+ index-name="searchIndex" :disabled="disabled" :empty="empty" :filterable="filterable"
4
+ :theme="theme"
5
+ :params="{
6
+ type: type,
7
+ subtype:subtype,
8
+ grouped: true,
9
+ }" v-if="grouped"/>
10
+ <tnxel-select ref="select" v-model="model" :id="id" :selector="selector" :items="items"
11
+ value-name="key" text-name="caption" index-name="searchIndex"
12
+ :default-value="defaultValue" :empty="empty" :empty-value="emptyValue"
13
+ :placeholder="placeholder" :disabled="disabled" :filterable="filterable"
14
+ :theme="theme" :size="size" :tag-click="tagClick" :change="change" v-else/>
15
+ </template>
16
+
17
+ <script>
18
+ import Select, {isMultiSelector} from '../select/Select.vue';
19
+ import FetchCascader from '../fetch-cascader/FetchCascader.vue';
20
+
21
+ export default {
22
+ name: 'TnxelEnumSelect',
23
+ components: {
24
+ 'tnxel-select': Select,
25
+ 'tnxel-fetch-cascader': FetchCascader,
26
+ },
27
+ props: {
28
+ id: [Number, String],
29
+ modelValue: [String, Number, Boolean, Array],
30
+ selector: String,
31
+ type: {
32
+ type: String,
33
+ required: true,
34
+ },
35
+ subtype: String,
36
+ defaultValue: String,
37
+ empty: {
38
+ type: [Boolean, String],
39
+ default: false,
40
+ },
41
+ emptyValue: {
42
+ type: [String, Boolean, Number],
43
+ default: () => null,
44
+ },
45
+ placeholder: String,
46
+ disabled: Boolean,
47
+ tagClick: Function,
48
+ change: Function,
49
+ grouped: {
50
+ type: Boolean,
51
+ default: false,
52
+ },
53
+ filterable: Boolean,
54
+ theme: String,
55
+ size: String,
56
+ },
57
+ emits: ['update:modelValue'],
58
+ data() {
59
+ return {
60
+ model: this.modelValue,
61
+ items: null,
62
+ };
63
+ },
64
+ watch: {
65
+ model(value) {
66
+ this.$emit('update:modelValue', value);
67
+ },
68
+ modelValue() {
69
+ this.initModel();
70
+ },
71
+ type() {
72
+ this.init();
73
+ },
74
+ subtype() {
75
+ this.init();
76
+ }
77
+ },
78
+ mounted() {
79
+ this.init();
80
+ },
81
+ methods: {
82
+ init() {
83
+ if (typeof this.type === 'string') {
84
+ if (this.type.toLowerCase() === 'boolean') {
85
+ this.items = [{
86
+ key: true,
87
+ caption: true.toText(),
88
+ }, {
89
+ key: false,
90
+ caption: false.toText(),
91
+ }];
92
+ this.initModel();
93
+ } else {
94
+ let vm = this;
95
+ window.tnx.app.rpc.loadEnumItems(this.type, this.subtype, function (items) {
96
+ vm.items = items;
97
+ vm.initModel();
98
+ });
99
+ }
100
+ }
101
+ },
102
+ initModel() {
103
+ let oldModel = this.model;
104
+ this.model = this.modelValue;
105
+ if (isMultiSelector(this.selector)) {
106
+ return;
107
+ }
108
+ if ((this.model === undefined || this.model === null) && !this.empty && this.items && this.items.length) {
109
+ let item = this.items[0];
110
+ this.model = item.key;
111
+ if (this.model !== oldModel && this.change) {
112
+ this.change(item);
113
+ }
114
+ }
115
+ },
116
+ getText(value) {
117
+ // 暂不支持分组枚举类型
118
+ if (this.$refs.select) {
119
+ return this.$refs.select.getText(value);
120
+ }
121
+ return undefined;
122
+ },
123
+ }
124
+ }
125
+ </script>