@truenewx/tnxvue3 3.4.4 → 3.4.5

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.
@@ -0,0 +1,179 @@
1
+ <template>
2
+ <t-dialog
3
+ class="tnxtdm-dialog"
4
+ v-model:visible="visible"
5
+ :title="title"
6
+ :width="width"
7
+ :close-on-overlay-click="options['close-on-overlay-click']"
8
+ :close-btn="!buttons?.length"
9
+ :destroy-on-close="true"
10
+ @closed="onClosed"
11
+ >
12
+ <template #default>
13
+ <div v-if="content" v-html="content"></div>
14
+ <tnxtdm-dialog-content ref="content" v-bind="contentProps" v-else/>
15
+ </template>
16
+ <template #actions v-if="buttons?.length">
17
+ <div class="tnxtdm-dialog-footer">
18
+ <t-button v-for="(button, index) in buttons" :key="index" :theme="button.type || 'primary'"
19
+ :loading="buttonLoadings[index]" @click="btnClick(index)" block>
20
+ {{ button.caption || button.text }}
21
+ </t-button>
22
+ </div>
23
+ </template>
24
+ </t-dialog>
25
+ </template>
26
+
27
+ <script>
28
+ import DialogContent from './DialogContent.vue';
29
+
30
+ export default {
31
+ name: 'TnxtdmDialog',
32
+ components: {
33
+ 'tnxtdm-dialog-content': DialogContent,
34
+ },
35
+ props: {
36
+ id: {
37
+ type: String,
38
+ default: () => window.tnx.util.string.uuid32(),
39
+ },
40
+ modelValue: Boolean,
41
+ container: String,
42
+ title: String,
43
+ content: String,
44
+ contentProps: Object,
45
+ buttons: Array,
46
+ theme: String,
47
+ width: {
48
+ type: [Number, String],
49
+ default: '100%',
50
+ },
51
+ },
52
+ emits: ['update:modelValue', 'shown', 'closed'],
53
+ data() {
54
+ return {
55
+ visible: this.modelValue,
56
+ options: {
57
+ modal: true,
58
+ 'close-on-overlay-click': true,
59
+ onShown: undefined,
60
+ onClosed: undefined,
61
+ beforeClose: undefined,
62
+ },
63
+ buttonLoadings: [],
64
+ };
65
+ },
66
+ watch: {
67
+ modelValue(val) {
68
+ this.visible = val;
69
+ },
70
+ visible(val) {
71
+ this.$emit('update:modelValue', val);
72
+ }
73
+ },
74
+ mounted() {
75
+ this.$nextTick(() => {
76
+ if (this.$refs.content && !this.$refs.content.close) {
77
+ this.$refs.content.close = () => {
78
+ this.close();
79
+ }
80
+ }
81
+
82
+ if (typeof this.options.onShown === 'function') {
83
+ this.options.onShown.call(this);
84
+ } else {
85
+ this.$emit('shown');
86
+ }
87
+ });
88
+ },
89
+ methods: {
90
+ btnClick(index) {
91
+ const button = this.buttons[index];
92
+ if (button) {
93
+ let click = button.click;
94
+ if (typeof click === 'string') {
95
+ if (typeof this.$refs.content[click] === 'function') {
96
+ click = this.$refs.content[click];
97
+ } else {
98
+ console.error(`Method '${click}' not found in component:`, this.$refs.content);
99
+ click = null;
100
+ }
101
+ }
102
+
103
+ if (typeof click === 'function') {
104
+ let result = click.call(this.$refs.content, this.close);
105
+ if (result === 'loading') {
106
+ this.buttonLoadings[index] = true;
107
+ return;
108
+ }
109
+ if (result === false) {
110
+ return;
111
+ }
112
+ }
113
+ }
114
+ this.close();
115
+ },
116
+ close() {
117
+ return new Promise((resolve) => {
118
+ this.beforeClose(() => {
119
+ const originalOnClosed = this.options.onClosed;
120
+ this.options.onClosed = window.tnx.util.function.around(originalOnClosed, function (onClosed) {
121
+ if (onClosed) {
122
+ onClosed();
123
+ }
124
+ resolve();
125
+ });
126
+ this.visible = false;
127
+ });
128
+ });
129
+ },
130
+ beforeClose(done) {
131
+ try {
132
+ if (typeof this.options.beforeClose === 'function') {
133
+ if (this.options.beforeClose.call(this.$refs.content) === false) {
134
+ return;
135
+ }
136
+ }
137
+ done();
138
+ } catch (e) {
139
+ console.error(e);
140
+ }
141
+ },
142
+ onClosed() {
143
+ if (typeof this.options.onClosed === 'function') {
144
+ this.options.onClosed.call(this.$refs.content);
145
+ } else {
146
+ this.$emit('closed');
147
+ }
148
+ }
149
+ }
150
+ }
151
+ </script>
152
+
153
+ <style>
154
+ .tnxtdm-dialog {
155
+
156
+ &.t-dialog__wrapper.t-popup--center {
157
+ width: calc(100% - 48px);
158
+ max-width: calc(var(--max-page-width) - 48px);
159
+ }
160
+
161
+ .t-dialog__content {
162
+ padding-top: 1rem;
163
+ }
164
+
165
+ .t-dialog__close-btn {
166
+ right: 12px;
167
+ }
168
+
169
+ .t-dialog__footer {
170
+ padding: 12px 24px;
171
+
172
+ .tnxtdm-dialog-footer {
173
+ padding: 12px 0;
174
+ width: 100%;
175
+ }
176
+ }
177
+
178
+ }
179
+ </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,176 @@
1
+ <template>
2
+ <tnxtdm-popup
3
+ class="tnxtdm-drawer"
4
+ v-model="visible"
5
+ :placement="placement"
6
+ :title="title"
7
+ @closed="onClosed"
8
+ >
9
+ <template #default>
10
+ <div class="tnxtdm-drawer-body">
11
+ <div v-if="content" v-html="content"></div>
12
+ <tnxtdm-drawer-content ref="content" v-bind="contentProps" v-else/>
13
+ </div>
14
+ <div class="tnxtdm-drawer-footer" v-if="buttons?.length">
15
+ <t-button v-for="(button, index) in buttons" :key="index" :theme="button.type || 'default'"
16
+ :loading="buttonLoadings[index]" @click="btnClick(index)" block>
17
+ {{ button.caption || button.text }}
18
+ </t-button>
19
+ </div>
20
+ </template>
21
+ </tnxtdm-popup>
22
+ </template>
23
+
24
+ <script>
25
+ import DrawerContent from './DrawerContent.vue';
26
+ import Popup from '../popup/Popup.vue';
27
+
28
+ export default {
29
+ name: 'TnxtdmDrawer',
30
+ components: {
31
+ 'tnxtdm-drawer-content': DrawerContent,
32
+ 'tnxtdm-popup': Popup,
33
+ },
34
+ props: {
35
+ id: {
36
+ type: String,
37
+ default: () => window.tnx.util.string.uuid32(),
38
+ },
39
+ modelValue: Boolean,
40
+ container: String,
41
+ title: String,
42
+ content: String,
43
+ contentProps: Object,
44
+ buttons: Array,
45
+ theme: String,
46
+ placement: {
47
+ type: String, // 'top' | 'left' | 'right' | 'bottom' | 'center'
48
+ default: 'bottom', // 默认底部弹出
49
+ },
50
+ },
51
+ emits: ['update:modelValue', 'shown', 'closed'],
52
+ data() {
53
+ return {
54
+ visible: false,
55
+ initialized: false,
56
+ options: {
57
+ modal: true,
58
+ onShown: undefined,
59
+ onClosed: undefined,
60
+ beforeClose: undefined,
61
+ },
62
+ buttonLoadings: [],
63
+ };
64
+ },
65
+ watch: {
66
+ modelValue(val) {
67
+ this.visible = val;
68
+ },
69
+ visible(val) {
70
+ if (this.initialized) {
71
+ this.$emit('update:modelValue', val);
72
+ }
73
+ }
74
+ },
75
+ mounted() {
76
+ this.$nextTick(() => {
77
+ this.initialized = true;
78
+ this.visible = this.modelValue;
79
+ if (this.$refs.content && !this.$refs.content.close) {
80
+ this.$refs.content.close = () => {
81
+ this.close();
82
+ }
83
+ }
84
+
85
+ if (typeof this.options.onShown === 'function') {
86
+ this.options.onShown.call(this);
87
+ } else {
88
+ this.$emit('shown');
89
+ }
90
+ });
91
+ },
92
+ methods: {
93
+ btnClick(index) {
94
+ const button = this.buttons[index];
95
+ if (button) {
96
+ let click = button.click;
97
+ if (typeof click === 'string') {
98
+ if (typeof this.$refs.content[click] === 'function') {
99
+ click = this.$refs.content[click];
100
+ } else {
101
+ console.error(`Method '${click}' not found in component:`, this.$refs.content);
102
+ click = null;
103
+ }
104
+ }
105
+
106
+ if (typeof click === 'function') {
107
+ let result = click.call(this.$refs.content, this.close);
108
+ if (result === 'loading') {
109
+ this.buttonLoadings[index] = true;
110
+ return;
111
+ }
112
+ if (result === false) {
113
+ return;
114
+ }
115
+ }
116
+ }
117
+ this.close();
118
+ },
119
+ close() {
120
+ return new Promise((resolve) => {
121
+ this.beforeClose(() => {
122
+ const originalOnClosed = this.options.onClosed;
123
+ this.options.onClosed = window.tnx.util.function.around(originalOnClosed, function (onClosed) {
124
+ if (onClosed) {
125
+ onClosed();
126
+ }
127
+ resolve();
128
+ });
129
+ this.visible = false;
130
+ });
131
+ });
132
+ },
133
+ beforeClose(done) {
134
+ try {
135
+ if (typeof this.options.beforeClose === 'function') {
136
+ if (this.options.beforeClose.call(this.$refs.content) === false) {
137
+ return;
138
+ }
139
+ }
140
+ done();
141
+ } catch (e) {
142
+ console.error(e);
143
+ }
144
+ },
145
+ onClosed() {
146
+ if (typeof this.options.onClosed === 'function') {
147
+ this.options.onClosed.call(this.$refs.content);
148
+ } else {
149
+ this.$emit('closed');
150
+ }
151
+ }
152
+ }
153
+ }
154
+ </script>
155
+
156
+ <style>
157
+ .tnxtdm-drawer {
158
+ .tnxtdm-drawer-body {
159
+ flex: 1;
160
+ overflow-y: auto;
161
+ padding: 16px;
162
+ }
163
+
164
+ .tnxtdm-drawer-footer {
165
+ padding: 12px 1rem calc(12px + env(safe-area-inset-bottom, 0));
166
+ border-top: 1px solid var(--td-border-level-1-color);
167
+ display: flex;
168
+ flex-direction: row-reverse;
169
+
170
+ .t-button {
171
+ width: fit-content;
172
+ margin-left: 12px;
173
+ }
174
+ }
175
+ }
176
+ </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,160 @@
1
+ <template>
2
+ <tnxtdm-select ref="select"
3
+ v-model="model"
4
+ :id="id"
5
+ :selector="selector"
6
+ :items="items"
7
+ value-name="key" text-name="caption" index-name="searchIndex"
8
+ :default-value="defaultValue"
9
+ :empty="empty"
10
+ :empty-value="emptyValue"
11
+ :placeholder="placeholder"
12
+ :width="width"
13
+ :disabled="isDisabled"
14
+ :filterable="filterable"
15
+ :theme="theme"
16
+ :size="size"
17
+ :tag-click="tagClick"
18
+ :change="change">
19
+ <template #option="{item}" v-if="$slots.option">
20
+ <slot name="option" :item="item"></slot>
21
+ </template>
22
+ </tnxtdm-select>
23
+ </template>
24
+
25
+ <script>
26
+ import Select, {isMultiSelector} from '../select/Select.vue';
27
+
28
+ export default {
29
+ name: 'TnxtdmEnumSelect',
30
+ components: {
31
+ 'tnxtdm-select': Select,
32
+ },
33
+ props: {
34
+ id: [Number, String],
35
+ modelValue: [String, Number, Boolean, Array],
36
+ selector: String,
37
+ type: {
38
+ type: String,
39
+ required: true,
40
+ },
41
+ subtype: String,
42
+ defaultValue: String,
43
+ empty: {
44
+ type: [Boolean, String],
45
+ default: false,
46
+ },
47
+ emptyValue: {
48
+ type: [String, Number, Boolean, Array],
49
+ default: '',
50
+ },
51
+ placeholder: String,
52
+ width: String,
53
+ disabled: Boolean,
54
+ tagClick: Function,
55
+ change: Function,
56
+ grouped: {
57
+ type: Boolean,
58
+ default: false,
59
+ },
60
+ filterable: Boolean,
61
+ theme: String,
62
+ size: String,
63
+ },
64
+ emits: ['update:modelValue'],
65
+ data() {
66
+ return {
67
+ model: this.modelValue,
68
+ items: null,
69
+ formDisabled: false,
70
+ };
71
+ },
72
+ computed: {
73
+ isDisabled() {
74
+ return this.disabled || this.formDisabled;
75
+ },
76
+ },
77
+ watch: {
78
+ model(value) {
79
+ this.$emit('update:modelValue', value);
80
+ },
81
+ modelValue() {
82
+ this.initModel();
83
+ },
84
+ type() {
85
+ this.init();
86
+ },
87
+ subtype() {
88
+ this.init();
89
+ }
90
+ },
91
+ mounted() {
92
+ this.init();
93
+ this.initFormDisabled();
94
+ },
95
+ methods: {
96
+ initFormDisabled() {
97
+ let parent = this.$parent;
98
+ while (parent) {
99
+ if (parent.$options && (parent.$options.name === 't-form' || parent.$options.name === 'TForm')) {
100
+ this.$watch(() => parent.disabled || (parent.$props && parent.$props.disabled), (val) => {
101
+ this.formDisabled = val;
102
+ }, {immediate: true});
103
+ break;
104
+ }
105
+ parent = parent.$parent;
106
+ }
107
+ },
108
+ init() {
109
+ if (typeof this.type === 'string') {
110
+ if (this.type.toLowerCase() === 'boolean') {
111
+ this.items = [{
112
+ key: true,
113
+ caption: true.toText(),
114
+ }, {
115
+ key: false,
116
+ caption: false.toText(),
117
+ }];
118
+ this.initModel();
119
+ } else {
120
+ if (window.tnx && window.tnx.meta) {
121
+ window.tnx.meta.resolveEnumItems(this.type, this.subtype).then((items) => {
122
+ this.items = items;
123
+ this.initModel();
124
+ });
125
+ }
126
+ }
127
+ }
128
+ },
129
+ initModel() {
130
+ let oldModel = this.model;
131
+ this.model = this.modelValue;
132
+ if (isMultiSelector(this.selector)) {
133
+ return;
134
+ }
135
+ if ((this.model === undefined || this.model === null) && !this.empty && this.items && this.items.length) {
136
+ let item = this.items[0];
137
+ this.model = item.key;
138
+ if (this.model !== oldModel && this.change) {
139
+ this.change(item);
140
+ }
141
+ }
142
+ },
143
+ getText(value) {
144
+ if (this.$refs.select) {
145
+ return this.$refs.select.getText(value);
146
+ }
147
+ return undefined;
148
+ },
149
+ disableItem(itemKey, disabled, reverseOther) {
150
+ if (this.$refs.select) {
151
+ this.$refs.select.disableItem(itemKey, disabled, reverseOther);
152
+ }
153
+ },
154
+ }
155
+ }
156
+ </script>
157
+
158
+ <style>
159
+
160
+ </style>
@@ -0,0 +1,106 @@
1
+ <template>
2
+ <slot name="trigger" :show="open" :visible="visible"></slot>
3
+ <t-popup
4
+ class="tnxtdm-popup"
5
+ v-model="visible"
6
+ :placement="placement"
7
+ :destroy-on-close="true"
8
+ v-bind="$attrs"
9
+ >
10
+ <div class="tnxtdm-popup-header" v-if="title || $slots.left || $slots.right">
11
+ <div class="tnxtdm-popup-header-left">
12
+ <slot name="left">
13
+ <t-icon name="blank" size="24px"/>
14
+ </slot>
15
+ </div>
16
+ <div class="tnxtdm-popup-header-title">
17
+ <slot name="title">{{ title }}</slot>
18
+ </div>
19
+ <div class="tnxtdm-popup-header-right">
20
+ <slot name="right">
21
+ <t-icon class="text-placeholder" name="close" size="24px" @click="close"/>
22
+ </slot>
23
+ </div>
24
+ </div>
25
+ <div class="tnxtdm-popup-content">
26
+ <slot></slot>
27
+ </div>
28
+ </t-popup>
29
+ </template>
30
+
31
+ <script>
32
+ export default {
33
+ name: 'TnxtdmPopup',
34
+ props: {
35
+ modelValue: Boolean,
36
+ title: String,
37
+ placement: {
38
+ type: String,
39
+ default: 'bottom',
40
+ },
41
+ },
42
+ data() {
43
+ return {
44
+ visible: false,
45
+ }
46
+ },
47
+ watch: {
48
+ modelValue(val) {
49
+ this.visible = val;
50
+ },
51
+ visible(val) {
52
+ this.$emit('update:modelValue', val);
53
+ },
54
+ },
55
+ emits: ['update:modelValue', 'close'],
56
+ mounted() {
57
+ this.visible = this.modelValue;
58
+ },
59
+ methods: {
60
+ open() {
61
+ this.visible = true;
62
+ },
63
+ close() {
64
+ this.visible = false;
65
+ this.$emit('close');
66
+ },
67
+ },
68
+ };
69
+ </script>
70
+
71
+ <style>
72
+ .tnxtdm-popup {
73
+ border-radius: 12px 12px 0 0;
74
+ max-height: 80vh;
75
+ display: flex;
76
+ flex-direction: column;
77
+ }
78
+
79
+ .tnxtdm-popup .tnxtdm-popup-header {
80
+ display: flex;
81
+ justify-content: space-between;
82
+ align-items: center;
83
+ padding: 12px 1rem;
84
+ border-bottom: 1px solid var(--td-border-level-1-color);
85
+ }
86
+
87
+ .tnxtdm-popup .tnxtdm-popup-header .tnxtdm-popup-header-title {
88
+ font-size: 1rem;
89
+ font-weight: 600;
90
+ color: var(--td-text-color-primary);
91
+ flex: 1;
92
+ text-align: center;
93
+ }
94
+
95
+ .tnxtdm-popup .tnxtdm-popup-header .tnxtdm-popup-header-left,
96
+ .tnxtdm-popup .tnxtdm-popup-header .tnxtdm-popup-header-right {
97
+ display: flex;
98
+ align-items: center;
99
+ min-width: 24px;
100
+ }
101
+
102
+ .tnxtdm-popup .tnxtdm-popup-content {
103
+ flex: 1;
104
+ overflow-y: auto;
105
+ }
106
+ </style>