@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,150 @@
1
+ <template>
2
+ <div class="tnxel-input-number" :class="containerClassObject">
3
+ <el-input-number ref="input" class="flex-grow-1" :class="{'rounded-end-0': suffix, 'text-start': !controls}"
4
+ v-model="model"
5
+ :min="min" :max="max"
6
+ :controls="controls" controls-position="right"
7
+ :placeholder="placeholderText" :disabled="disabled"
8
+ :step="step || Math.pow(10, -this.scale)" step-strictly
9
+ :precision="scale"
10
+ :value-on-clear="null"
11
+ :size="size"
12
+ @change="onChange"
13
+ @blur="$emit('blur', $event)"/>
14
+ <div class="el-input-group__append" v-if="suffix">{{ suffix }}</div>
15
+ </div>
16
+ </template>
17
+
18
+ <script>
19
+ export default {
20
+ name: 'TnxelInputNumber',
21
+ props: {
22
+ modelValue: [Number, String],
23
+ span: Number,
24
+ min: Number,
25
+ max: Number,
26
+ placeholder: {
27
+ type: String,
28
+ default: () => '请设置',
29
+ },
30
+ disabled: Boolean,
31
+ append: String,
32
+ controls: {
33
+ type: Boolean,
34
+ default: true,
35
+ },
36
+ step: Number,
37
+ scale: {
38
+ type: Number,
39
+ default: 0,
40
+ },
41
+ required: Boolean,
42
+ size: String,
43
+ },
44
+ emits: ['update:modelValue', 'blur'],
45
+ data() {
46
+ return {
47
+ model: typeof this.modelValue === 'string' ? Number(this.modelValue) : this.modelValue,
48
+ showRequiredError: false,
49
+ }
50
+ },
51
+ computed: {
52
+ placeholderText() {
53
+ if (this.placeholder) {
54
+ return this.placeholder;
55
+ }
56
+ let text = this.disabled ? '未' : '请';
57
+ text += this.controls ? '设置' : '输入';
58
+ return text;
59
+ },
60
+ containerClassObject() {
61
+ let classObject = {
62
+ 'is-error': this.showRequiredError,
63
+ };
64
+ if (this.span) {
65
+ classObject['el-col'] = true;
66
+ classObject['el-col-' + this.span] = true;
67
+ }
68
+ return classObject;
69
+ },
70
+ suffix() {
71
+ if (this.append) {
72
+ return this.append;
73
+ }
74
+ if (this.$slots?.append) {
75
+ let append = this.$slots.append();
76
+ if (append?.length) {
77
+ return append[0].text;
78
+ }
79
+ }
80
+ return null;
81
+ }
82
+ },
83
+ watch: {
84
+ model(value) {
85
+ this.$emit('update:modelValue', value);
86
+ },
87
+ modelValue(value) {
88
+ this.model = typeof value === 'string' ? Number(value) : value;
89
+ }
90
+ },
91
+ methods: {
92
+ validateRequired(focusError) {
93
+ if (this.required) {
94
+ this.showRequiredError = this.model === undefined || this.model === null || this.model === '';
95
+ if (this.showRequiredError && focusError) {
96
+ this.focus();
97
+ }
98
+ return !this.showRequiredError;
99
+ }
100
+ return true;
101
+ },
102
+ focus() {
103
+ this.$refs.input.focus();
104
+ },
105
+ onChange() {
106
+ this.validateRequired(false);
107
+ },
108
+ }
109
+ }
110
+ </script>
111
+
112
+ <style>
113
+ .tnxel-input-number {
114
+ display: flex;
115
+ }
116
+
117
+ .tnxel-input-number .el-input-number {
118
+ width: auto;
119
+ line-height: 1;
120
+ }
121
+
122
+ .tnxel-input-number .el-input__inner {
123
+ min-width: 50px;
124
+ }
125
+
126
+ .tnxel-input-number .el-input-number.text-start .el-input__inner {
127
+ text-align: left;
128
+ }
129
+
130
+ .tnxel-input-number .el-input-number.is-controls-right .el-input__wrapper {
131
+ padding-left: 8px;
132
+ }
133
+
134
+ .tnxel-input-number .el-input-number .el-input__wrapper .el-input__suffix {
135
+ display: none;
136
+ }
137
+
138
+ .tnxel-input-number .el-input-number.rounded-end-0 .el-input__wrapper {
139
+ border-top-right-radius: 0;
140
+ border-bottom-right-radius: 0;
141
+ }
142
+
143
+ .tnxel-input-number .el-input-group__append {
144
+ border: 1px solid var(--el-border-color);
145
+ border-left: none;
146
+ line-height: 1;
147
+ border-top-right-radius: var(--el-input-border-radius, var(--el-border-radius-base));
148
+ border-bottom-right-radius: var(--el-input-border-radius, var(--el-border-radius-base));
149
+ }
150
+ </style>
@@ -0,0 +1,76 @@
1
+ <template>
2
+ <div class="tnxel-pagination-container"
3
+ :class="{'justify-content-center': align === 'center', 'justify-content-end': align === 'right'}" v-if="model">
4
+ <slot :paged="value" :change="change" :background="background">
5
+ <el-pagination layout="total, slot, prev, pager, next" :background="background" @current-change="change"
6
+ v-model:page-size="model.pageSize" v-model:current-page="model.pageNo"
7
+ :total="model.total || 0">
8
+ <span class="el-pagination__page-size" v-if="pageSizeItems.length <= 1">{{ model.pageSize }}条/页</span>
9
+ <tnxel-select class="el-pagination__page-size" v-model="model.pageSize" :items="pageSizeItems" v-else/>
10
+ </el-pagination>
11
+ </slot>
12
+ </div>
13
+ </template>
14
+
15
+ <script>
16
+ import Select from '../select/Select.vue';
17
+
18
+ export default {
19
+ name: 'TnxelPaged',
20
+ components: {
21
+ 'tnxel-select': Select,
22
+ },
23
+ props: {
24
+ value: Object,
25
+ change: {
26
+ type: Function
27
+ },
28
+ background: {
29
+ type: Boolean,
30
+ default: true,
31
+ },
32
+ align: String,
33
+ pageSizes: Array,
34
+ },
35
+ data() {
36
+ return {
37
+ model: this.value,
38
+ }
39
+ },
40
+ watch: {
41
+ value() {
42
+ this.model = this.value;
43
+ }
44
+ },
45
+ computed: {
46
+ pageSizeItems() {
47
+ let pageSizeItems = [];
48
+ if (this.pageSizes) {
49
+ for (let pageSize of this.pageSizes) {
50
+ pageSizeItems.push({
51
+ value: pageSize,
52
+ text: pageSize + '条/页',
53
+ });
54
+ }
55
+ }
56
+ return pageSizeItems;
57
+ }
58
+ },
59
+ }
60
+ </script>
61
+
62
+ <style>
63
+ .tnxel-pagination-container {
64
+ display: flex;
65
+ padding: 0.5rem 0;
66
+ }
67
+
68
+ .tnxel-pagination-container .el-pagination {
69
+ padding: 0;
70
+ }
71
+
72
+ .tnxel-pagination-container .el-pagination__page-size {
73
+ color: var(--el-text-color-regular);
74
+ margin-right: 1rem;
75
+ }
76
+ </style>
@@ -0,0 +1,184 @@
1
+ <template>
2
+ <el-tree class="permission-tree"
3
+ ref="tree"
4
+ :data="nodes"
5
+ :default-expand-all="true"
6
+ :expand-on-click-node="false"
7
+ node-key="id"
8
+ :style="{'max-height': maxHeight}"
9
+ >
10
+ <template #default="{node, data}">
11
+ <div class="permission-node">
12
+ <el-checkbox v-model="data.checked" v-if="data.permission" @change="()=>{
13
+ onCheckChange(data);
14
+ }">{{ data.label }}
15
+ </el-checkbox>
16
+ <span v-else-if="node">{{ data.label }}</span>
17
+ <span class="text-muted" :class="{'d-none': !data.remark}">({{ data.remark }})</span>
18
+ </div>
19
+ </template>
20
+ </el-tree>
21
+ </template>
22
+
23
+ <script>
24
+ function isChecked(app, permissions, permission) {
25
+ if (permission && permissions) {
26
+ if (app) {
27
+ permission = app + '.' + permission;
28
+ }
29
+ return permissions.contains(permission);
30
+ }
31
+ return false;
32
+ }
33
+
34
+ function addMenuItemToTreeNodes(app, parentId, menuItems, treeNodes, permissions) {
35
+ for (let i = 0; i < menuItems.length; i++) {
36
+ const item = menuItems[i];
37
+ if (item.caption) { // 有显示名称才可进行权限分配,否则只是一个隐藏的菜单项
38
+ let node = {
39
+ id: (parentId || 'node') + '-' + i,
40
+ parentId: parentId,
41
+ label: item.caption,
42
+ path: item.path,
43
+ permission: item.permission,
44
+ checked: isChecked(app, permissions, item.permission),
45
+ remark: item.desc || item.permissionCaption,
46
+ };
47
+ if (item.subs && item.subs.length) {
48
+ node.children = node.children || [];
49
+ addMenuItemToTreeNodes(app, node.id, item.subs, node.children, permissions);
50
+ }
51
+ treeNodes.push(node);
52
+ }
53
+ }
54
+ }
55
+
56
+ function getTreeNodes(menu, permissions) {
57
+ const nodes = [];
58
+ if (menu) {
59
+ let items = menu.getAssignableItems();
60
+ addMenuItemToTreeNodes(menu.app, undefined, items, nodes, permissions);
61
+ }
62
+ return nodes;
63
+ }
64
+
65
+ function addCheckedNodePermissionTo(app, nodes, permissions) {
66
+ for (let node of nodes) {
67
+ if (node.checked && node.permission) {
68
+ let permission = node.permission;
69
+ if (app) {
70
+ permission = app + '.' + permission;
71
+ }
72
+ permissions.push(permission);
73
+ }
74
+ if (node.children) {
75
+ addCheckedNodePermissionTo(app, node.children, permissions);
76
+ }
77
+ }
78
+ }
79
+
80
+ function setCheckdByPermission(nodes, permission, checked) {
81
+ if (nodes && permission) {
82
+ for (let node of nodes) {
83
+ if (node.permission === permission) {
84
+ node.checked = checked;
85
+ }
86
+ setCheckdByPermission(node.children, permission, checked);
87
+ }
88
+ }
89
+ }
90
+
91
+ export default {
92
+ name: 'TnxelPermissionTree',
93
+ props: {
94
+ menu: {
95
+ type: Object,
96
+ required: true,
97
+ },
98
+ permissions: Array,
99
+ maxHeight: String,
100
+ },
101
+ data() {
102
+ return {
103
+ nodes: getTreeNodes(this.menu, this.permissions)
104
+ };
105
+ },
106
+ watch: {
107
+ menu(menu) {
108
+ this.nodes = getTreeNodes(menu, this.permissions);
109
+ },
110
+ permissions(permissions) {
111
+ this.nodes = getTreeNodes(this.menu, permissions);
112
+ }
113
+ },
114
+ methods: {
115
+ getNode(nodeId, nodes) {
116
+ if (nodeId) {
117
+ nodes = nodes || this.nodes;
118
+ for (let node of nodes) {
119
+ if (node.id === nodeId) {
120
+ return node;
121
+ }
122
+ if (node.children) {
123
+ let child = this.getNode(nodeId, node.children);
124
+ if (child) {
125
+ return child;
126
+ }
127
+ }
128
+ }
129
+ }
130
+ return undefined;
131
+ },
132
+ onCheckChange(node) {
133
+ if (node.checked) { // 节点被选中,则上级节点必须选中
134
+ let parentNode = this.getNode(node.parentId);
135
+ if (parentNode) {
136
+ parentNode.checked = true;
137
+ setCheckdByPermission(this.nodes, parentNode.permission, true);
138
+ }
139
+ } else { // 节点未选中,则下级节点必须全部未选中
140
+ if (node.children) {
141
+ node.children.forEach(child => {
142
+ child.checked = false;
143
+ setCheckdByPermission(this.nodes, child.permission, false);
144
+ });
145
+ }
146
+ }
147
+ setCheckdByPermission(this.nodes, node.permission, node.checked);
148
+ },
149
+ getPermissions(withApp) {
150
+ // 除非指定不附带app,否则默认根据菜单中配置的app进行附带
151
+ let app = this.menu.app;
152
+ if (withApp === false) {
153
+ app = null;
154
+ }
155
+
156
+ const permissions = [];
157
+ addCheckedNodePermissionTo(app, this.nodes, permissions);
158
+ return permissions;
159
+ }
160
+ }
161
+ }
162
+ </script>
163
+
164
+ <style>
165
+ .permission-tree {
166
+ overflow-y: auto;
167
+ padding: 4px;
168
+ }
169
+
170
+ .permission-tree .permission-node {
171
+ width: 100%;
172
+ display: flex;
173
+ align-items: center;
174
+ justify-content: space-between;
175
+ }
176
+
177
+ .permission-tree .permission-node > :last-child {
178
+ margin-left: 6px;
179
+ margin-right: 6px;
180
+ white-space: normal;
181
+ line-height: 1;
182
+ text-align: right;
183
+ }
184
+ </style>
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <el-form :id="id" :inline="inline" :model="params" :class="theme ? ('theme-' + theme) : null">
3
+ <slot></slot>
4
+ <el-form-item class="me-0" v-if="queryText || clearText">
5
+ <tnxel-button :type="theme || 'primary'" icon="Search" @click="toQuery" :plain="plain"
6
+ v-if="queryText">
7
+ {{ queryText }}
8
+ </tnxel-button>
9
+ <el-button @click="toClear" :plain="plain" title="清空查询条件" v-if="clearText">{{ clearText }}</el-button>
10
+ </el-form-item>
11
+ </el-form>
12
+ </template>
13
+
14
+ <script>
15
+ import {ObjectUtil} from '@truenewx/tnxcore/src/tnxcore-util';
16
+ import Button from '../button/Button.vue';
17
+
18
+ export default {
19
+ name: 'TnxelQueryForm',
20
+ components: {
21
+ 'tnxel-button': Button,
22
+ },
23
+ props: {
24
+ id: String,
25
+ modelValue: {
26
+ type: Object,
27
+ default() {
28
+ return {};
29
+ },
30
+ },
31
+ theme: String,
32
+ query: Function,
33
+ queryText: {
34
+ type: String,
35
+ default: '查询',
36
+ },
37
+ clearText: {
38
+ type: String,
39
+ default: '清空',
40
+ },
41
+ clear: Function,
42
+ plain: {
43
+ type: Boolean,
44
+ default: true,
45
+ },
46
+ init: { // 是否初始化执行查询
47
+ type: Boolean,
48
+ default: false,
49
+ },
50
+ inline: {
51
+ type: Boolean,
52
+ default: true,
53
+ }
54
+ },
55
+ emits: ['update:modelValue'],
56
+ data() {
57
+ return {
58
+ params: this.modelValue,
59
+ }
60
+ },
61
+ computed: {
62
+ cacheKey() {
63
+ let key = this.$options.name;
64
+ if (this.id) {
65
+ key += '-' + this.id;
66
+ }
67
+ return key;
68
+ }
69
+ },
70
+ watch: {
71
+ modelValue() {
72
+ this.params = this.modelValue;
73
+ this.cacheParams();
74
+ }
75
+ },
76
+ mounted() {
77
+ let vm = this;
78
+ setTimeout(function () {
79
+ let queryable = vm.init;
80
+ if (vm.$route.meta.isHistory()) {
81
+ queryable = true; // 历史性访问均需要执行查询
82
+ let params = vm.$route.meta.cache[vm.cacheKey];
83
+ if (params) {
84
+ let keys = Object.keys(vm.params);
85
+ for (let key of keys) {
86
+ let value = params[key];
87
+ if (value !== undefined) {
88
+ vm.params[key] = value;
89
+ }
90
+ }
91
+ }
92
+ }
93
+ if (queryable) {
94
+ vm.toQuery();
95
+ }
96
+ });
97
+ },
98
+ methods: {
99
+ cacheParams() {
100
+ if (typeof this.params.pageNo === 'string') {
101
+ this.params.pageNo = parseInt(this.params.pageNo);
102
+ }
103
+ this.$route.meta.cache[this.cacheKey] = this.params;
104
+ },
105
+ toQuery(event) { // 为了避免传递事件参数,不直接使用query()
106
+ if (this.query) {
107
+ // event不为空,说明方法调用来自于查询按钮点击,此时如果查询参数中有页码,则需重置页码为1
108
+ if (event && this.params.pageNo !== undefined) {
109
+ this.params.pageNo = 1;
110
+ }
111
+ this.cacheParams();
112
+ this.query();
113
+ }
114
+ },
115
+ toClear() {
116
+ if (Object.keys(this.params).length) {
117
+ if (this.clear) {
118
+ if (this.clear(this.params) === false) {
119
+ return;
120
+ }
121
+ } else {
122
+ ObjectUtil.clear(this.params, ['pageSize', 'pageNo', 'ignoring', 'orderBy']);
123
+ if (ObjectUtil.isNotNull(this.params.pageNo)) {
124
+ this.params.pageNo = 1;
125
+ }
126
+ }
127
+ this.$emit('update:modelValue', this.params);
128
+ let parameters = window.tnx.util.net.getParameters();
129
+ if (Object.keys(parameters).length) {
130
+ this.$router.replace(this.$route.path);
131
+ } else {
132
+ this.toQuery();
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ </script>