xt-element-ui 2.1.61 → 2.1.72

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.
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "xt-element-ui",
3
- "version": "2.1.61",
3
+ "version": "2.1.72",
4
4
  "description": "基于 Vue 2.7 + ElementUI 的企业级组件库,提供丰富的自定义组件和扩展组件",
5
5
  "main": "lib/index.common.js",
6
- "module": "lib/index.esm.js",
7
6
  "unpkg": "lib/index.umd.min.js",
8
7
  "style": "lib/index.css",
9
8
  "scripts": {
@@ -19,7 +18,6 @@
19
18
  },
20
19
  "devDependencies": {
21
20
  "@vue/cli-service": "~4.5.19",
22
- "patch-package": "^8.0.1",
23
21
  "sass": "^1.56.0",
24
22
  "sass-loader": "^10.5.2",
25
23
  "vuepress": "^1.9.10",
@@ -28,7 +26,10 @@
28
26
  },
29
27
  "files": [
30
28
  "lib",
31
- "src",
29
+ "src/components",
30
+ "src/styles",
31
+ "src/utils",
32
+ "src/index.js",
32
33
  "README.md",
33
34
  "CHANGELOG.md",
34
35
  "docs/components/**/*md",
@@ -37,8 +38,8 @@
37
38
  "dependencies": {
38
39
  "echarts": "^4.0.0",
39
40
  "element-ui": "^2.11.1",
40
- "vue": "^2.6.10",
41
- "vue-server-renderer": "^2.7.14"
41
+ "patch-package": "^8.0.1",
42
+ "vue": "^2.6.10"
42
43
  },
43
44
  "keywords": [
44
45
  "vue",
@@ -0,0 +1,10 @@
1
+
2
+ import XtTabPane from './index.vue'
3
+
4
+ XtTabPane.install = function(Vue) {
5
+ Vue.component(XtTabPane.name, XtTabPane)
6
+ }
7
+
8
+ export default XtTabPane
9
+ export { XtTabPane }
10
+
@@ -15,6 +15,14 @@ export default {
15
15
  label: {
16
16
  type: String,
17
17
  default: ''
18
+ },
19
+ disabled: {
20
+ type: Boolean,
21
+ default: false
22
+ },
23
+ closable: {
24
+ type: Boolean,
25
+ default: true
18
26
  }
19
27
  },
20
28
  computed: {
@@ -24,7 +32,12 @@ export default {
24
32
  },
25
33
  mounted() {
26
34
  if (this.$parent && typeof this.$parent.addPane === 'function') {
27
- this.$parent.addPane({ name: this.name, label: this.label })
35
+ this.$parent.addPane({
36
+ name: this.name,
37
+ label: this.label,
38
+ disabled: this.disabled,
39
+ closable: this.closable
40
+ })
28
41
  }
29
42
  },
30
43
  beforeDestroy() {
@@ -1,10 +1,8 @@
1
1
  import XtTabs from './index.vue'
2
- import XtTabPane from './TabPane.vue'
3
2
 
4
3
  XtTabs.install = function(Vue) {
5
4
  Vue.component(XtTabs.name, XtTabs)
6
- Vue.component(XtTabPane.name, XtTabPane)
7
5
  }
8
6
 
9
7
  export default XtTabs
10
- export { XtTabPane }
8
+ export { XtTabs }
@@ -1,34 +1,42 @@
1
1
  <template>
2
- <div class="xt-tabs" :class="[
3
- `xt-tabs--${position}`,
4
- { 'xt-tabs--card': type === 'card' }
2
+ <div ref="tabsWrap" class="xt-tabs" :class="[
3
+ `xt-tabs--${actualPosition}`,
4
+ { 'xt-tabs--card': type === 'card' },
5
+ { 'xt-tabs--border-card': type === 'border-card' },
6
+ { 'xt-tabs--editable': editable }
5
7
  ]">
6
- <div class="xt-tabs__header">
7
- <div class="xt-tabs__nav">
8
+ <div class="xt-tabs__header" ref="tabsHeader">
9
+ <div class="xt-tabs__nav" ref="tabsNav">
8
10
  <div
9
11
  v-for="(pane, index) in panes"
10
12
  :key="pane.name"
11
13
  class="xt-tabs__nav-item"
12
- :class="{ 'xt-tabs__nav-item--active': activeName === pane.name }"
13
- @click="handleTabClick(pane.name)"
14
+ :class="{
15
+ 'xt-tabs__nav-item--active': activeName === pane.name,
16
+ 'xt-tabs__nav-item--disabled': pane.disabled
17
+ }"
18
+ @click="handleTabClick(pane)"
14
19
  >
15
20
  <span class="xt-tabs__nav-link">{{ pane.label }}</span>
21
+ <span
22
+ v-if="(closable || pane.closable) && !editable"
23
+ class="xt-tabs__close"
24
+ @click.stop="handleTabRemove(pane.name)"
25
+ >×</span>
26
+ </div>
27
+ <div
28
+ v-if="addable || editable"
29
+ class="xt-tabs__nav-item xt-tabs__nav-item--add"
30
+ @click="handleTabAdd"
31
+ >
32
+ <span>+</span>
16
33
  </div>
17
34
  </div>
18
35
  <div class="xt-tabs__nav-indicator" :style="indicatorStyle"></div>
19
36
  </div>
20
37
 
21
38
  <div class="xt-tabs__content">
22
- <transition name="xt-tabs-fade" mode="out-in">
23
- <div
24
- v-for="(pane, index) in panes"
25
- :key="pane.name"
26
- class="xt-tabs__pane"
27
- v-show="activeName === pane.name"
28
- >
29
- <slot :name="pane.name"></slot>
30
- </div>
31
- </transition>
39
+ <slot></slot>
32
40
  </div>
33
41
  </div>
34
42
  </template>
@@ -43,13 +51,29 @@ export default {
43
51
  },
44
52
  type: {
45
53
  type: String,
46
- default: 'default',
47
- validator: (val) => ['default', 'card'].includes(val)
54
+ default: 'card',
55
+ validator: (val) => ['default', 'card', 'border-card'].includes(val)
48
56
  },
49
57
  position: {
50
58
  type: String,
51
59
  default: 'top',
52
60
  validator: (val) => ['top', 'bottom', 'left', 'right'].includes(val)
61
+ },
62
+ tabPosition: {
63
+ type: String,
64
+ default: ''
65
+ },
66
+ closable: {
67
+ type: Boolean,
68
+ default: false
69
+ },
70
+ addable: {
71
+ type: Boolean,
72
+ default: false
73
+ },
74
+ editable: {
75
+ type: Boolean,
76
+ default: false
53
77
  }
54
78
  },
55
79
  data() {
@@ -58,56 +82,291 @@ export default {
58
82
  activeName: this.value
59
83
  }
60
84
  },
61
- watch: {
62
- value(val) {
63
- this.activeName = val
64
- },
65
- activeName(val) {
66
- this.$emit('input', val)
67
- this.$emit('change', val)
68
- }
69
- },
70
85
  computed: {
86
+ actualPosition() {
87
+ return this.tabPosition || this.position
88
+ },
71
89
  indicatorStyle() {
72
90
  const activeIndex = this.panes.findIndex(p => p.name === this.activeName)
73
- if (activeIndex === -1) return { display: 'none' }
91
+ if (activeIndex === -1 || !this.$refs.tabsNav) return { display: 'none' }
74
92
 
75
- const navItems = this.$el && this.$el.querySelectorAll('.xt-tabs__nav-item')
93
+ const navItems = this.$refs.tabsNav.querySelectorAll('.xt-tabs__nav-item:not(.xt-tabs__nav-item--add)')
76
94
  if (!navItems || navItems.length === 0) return { display: 'none' }
77
95
 
78
96
  const activeItem = navItems[activeIndex]
79
- return {
80
- left: `${activeItem.offsetLeft}px`,
81
- width: `${activeItem.offsetWidth}px`,
82
- display: 'block'
97
+ if (!activeItem) return { display: 'none' }
98
+
99
+ const pos = this.actualPosition
100
+ const base = { transition: 'all 0.3s ease' }
101
+ if (pos === 'top' || pos === 'bottom') {
102
+ return {
103
+ ...base,
104
+ left: `${activeItem.offsetLeft}px`,
105
+ width: `${activeItem.offsetWidth}px`,
106
+ height: '2px',
107
+ bottom: pos === 'top' ? 0 : 'auto',
108
+ top: pos === 'bottom' ? 0 : 'auto',
109
+ display: 'block'
110
+ }
111
+ } else {
112
+ return {
113
+ ...base,
114
+ top: `${activeItem.offsetTop}px`,
115
+ height: `${activeItem.offsetHeight}px`,
116
+ width: '2px',
117
+ left: pos === 'left' ? 'auto' : 0,
118
+ right: pos === 'right' ? 0 : 'auto',
119
+ display: 'block'
120
+ }
83
121
  }
84
122
  }
85
123
  },
124
+ watch: {
125
+ value(val) {
126
+ this.activeName = val
127
+ },
128
+ activeName(val) {
129
+ this.$emit('input', val)
130
+ this.$emit('change', val)
131
+ this.$nextTick(() => this.$forceUpdate())
132
+ },
133
+ panes: {
134
+ handler() {
135
+ this.$nextTick(() => this.$forceUpdate())
136
+ },
137
+ deep: true
138
+ }
139
+ },
86
140
  methods: {
87
- handleTabClick(name) {
88
- this.activeName = name
141
+ handleTabClick(pane) {
142
+ if (pane.disabled) return
143
+ this.activeName = pane.name
144
+ this.$emit('tab-click', pane)
145
+ },
146
+ handleTabRemove(name) {
147
+ this.$emit('tab-remove', name)
148
+ this.$emit('edit', name, 'remove')
149
+ const index = this.panes.findIndex(p => p.name === name)
150
+ if (index > -1) {
151
+ this.panes.splice(index, 1)
152
+ if (this.activeName === name && this.panes.length > 0) {
153
+ this.activeName = this.panes[0].name
154
+ }
155
+ }
156
+ },
157
+ handleTabAdd() {
158
+ this.$emit('tab-add')
159
+ this.$emit('edit', null, 'add')
89
160
  },
90
161
  addPane(pane) {
91
- if (!this.panes.find(p => p.name === pane.name)) {
162
+ const exist = this.panes.some(p => p.name === pane.name)
163
+ if (!exist) {
92
164
  this.panes.push(pane)
93
165
  if (!this.activeName && this.panes.length === 1) {
94
166
  this.activeName = pane.name
95
167
  }
168
+ this.$nextTick(() => this.$forceUpdate())
96
169
  }
97
170
  },
98
171
  removePane(name) {
99
- const index = this.panes.findIndex(p => p.name === name)
100
- if (index > -1) {
101
- this.panes.splice(index, 1)
172
+ const idx = this.panes.findIndex(p => p.name === name)
173
+ if (idx > -1) {
174
+ this.panes.splice(idx, 1)
175
+ this.$nextTick(() => this.$forceUpdate())
102
176
  }
177
+ },
178
+ collectPanes() {
179
+ this.panes = []
180
+ this.$children.forEach(child => {
181
+ if (child.$options.name === 'XtTabPane') {
182
+ this.addPane({
183
+ name: child.name,
184
+ label: child.label,
185
+ disabled: child.disabled,
186
+ closable: child.closable
187
+ })
188
+ }
189
+ })
103
190
  }
104
191
  },
105
192
  mounted() {
106
- this.$children.forEach(child => {
107
- if (child.$options.name === 'XtTabPane') {
108
- this.addPane({ name: child.name, label: child.label })
109
- }
193
+ this.$nextTick(() => {
194
+ this.collectPanes()
195
+ this.$watch(() => this.$children, () => {
196
+ this.collectPanes()
197
+ }, { flush: 'post' })
110
198
  })
111
199
  }
112
200
  }
113
201
  </script>
202
+
203
+ <style lang="scss" scoped>
204
+ .xt-tabs {
205
+ display: flex;
206
+ flex-direction: column;
207
+
208
+ &--top, &--bottom {
209
+ flex-direction: column;
210
+ .xt-tabs__header {
211
+ width: 100%;
212
+ }
213
+ }
214
+
215
+ &--left, &--right {
216
+ flex-direction: row;
217
+ .xt-tabs__header {
218
+ flex-direction: column;
219
+ width: auto;
220
+ }
221
+ .xt-tabs__nav {
222
+ flex-direction: column;
223
+ }
224
+ .xt-tabs__nav-indicator {
225
+ width: 2px;
226
+ height: auto;
227
+ }
228
+ .xt-tabs__content {
229
+ border-left: 1px solid #dcdfe6;
230
+ border-top: none;
231
+ }
232
+ &--right .xt-tabs__content {
233
+ border-left: none;
234
+ border-right: 1px solid #dcdfe6;
235
+ }
236
+ }
237
+
238
+ &--top .xt-tabs__nav-indicator {
239
+ bottom: 0;
240
+ }
241
+ &--bottom .xt-tabs__nav-indicator {
242
+ top: 0;
243
+ }
244
+ &--left .xt-tabs__nav-indicator {
245
+ right: 0;
246
+ }
247
+ &--right .xt-tabs__nav-indicator {
248
+ left: 0;
249
+ }
250
+
251
+ &--card {
252
+ .xt-tabs__header {
253
+ margin-bottom: -1px;
254
+ }
255
+
256
+ .xt-tabs__nav-item {
257
+ border: 1px solid transparent;
258
+ border-bottom: none;
259
+ background: #f5f7fa;
260
+
261
+ &--active {
262
+ background: #fff;
263
+ border-color: #dcdfe6;
264
+ border-bottom-color: #fff;
265
+ }
266
+ }
267
+ &--left, &--right {
268
+ .xt-tabs__nav-item {
269
+ border-bottom: 1px solid transparent;
270
+ border-right: none;
271
+ }
272
+ .xt-tabs__nav-item--active {
273
+ border-right-color: #fff;
274
+ border-bottom-color: #dcdfe6;
275
+ }
276
+ }
277
+ }
278
+
279
+ &--border-card {
280
+ border: 1px solid #dcdfe6;
281
+ border-radius: 4px;
282
+ overflow: hidden;
283
+
284
+ .xt-tabs__nav-item--active {
285
+ background: #ecf5ff;
286
+ color: #409eff;
287
+ }
288
+ }
289
+
290
+ &--editable {
291
+ .xt-tabs__close {
292
+ display: inline-block;
293
+ }
294
+ }
295
+ }
296
+
297
+ .xt-tabs__header {
298
+ position: relative;
299
+ display: flex;
300
+ }
301
+
302
+ .xt-tabs__nav {
303
+ display: flex;
304
+ flex-wrap: nowrap;
305
+ }
306
+
307
+ .xt-tabs__nav-item {
308
+ position: relative;
309
+ padding: 12px 20px;
310
+ cursor: pointer;
311
+ font-size: 14px;
312
+ color: #606266;
313
+ transition: all 0.3s;
314
+ white-space: nowrap;
315
+
316
+ &--active {
317
+ color: #409eff;
318
+ }
319
+
320
+ &--disabled {
321
+ cursor: not-allowed;
322
+ color: #c0c4cc;
323
+ }
324
+
325
+ &--add {
326
+ color: #909399;
327
+ font-size: 20px;
328
+ }
329
+
330
+ &:hover:not(.xt-tabs__nav-item--disabled) {
331
+ color: #409eff;
332
+ background-color: #ecf5ff;
333
+ }
334
+ }
335
+
336
+ .xt-tabs__nav-link {
337
+ display: inline-block;
338
+ }
339
+
340
+ .xt-tabs__close {
341
+ display: none;
342
+ margin-left: 8px;
343
+ font-size: 16px;
344
+ line-height: 1;
345
+ color: #909399;
346
+ vertical-align: middle;
347
+
348
+ &:hover {
349
+ color: #f56c6c;
350
+ }
351
+ }
352
+
353
+ .xt-tabs__nav-indicator {
354
+ position: absolute;
355
+ height: 2px;
356
+ background-color: #409eff;
357
+ }
358
+
359
+ .xt-tabs__content {
360
+ flex: 1;
361
+ padding: 16px;
362
+ border-top: 1px solid #dcdfe6;
363
+ }
364
+
365
+ .xt-tabs--border-card .xt-tabs__content {
366
+ border-top: none;
367
+ }
368
+
369
+ .xt-tabs__pane {
370
+ min-height: 40px;
371
+ }
372
+ </style>
package/src/index.js CHANGED
@@ -31,6 +31,7 @@ import XtMapProvider from './components/xt-map-provider'
31
31
  import XtGridBox from './components/xt-grid-box'
32
32
  import XtGridItem from './components/xt-grid-item'
33
33
  import XtProgress from './components/xt-progress'
34
+ import XtTabPane from './components/xt-tab-pane'
34
35
  import XtTabs from './components/xt-tabs'
35
36
  import XtBadge from './components/xt-badge'
36
37
  import XtDatePicker from './components/xt-date-picker'
@@ -66,6 +67,7 @@ const components = [
66
67
  XtGridBox,
67
68
  XtGridItem,
68
69
  XtProgress,
70
+ XtTabPane,
69
71
  XtTabs,
70
72
  XtBadge,
71
73
  XtDatePicker,