1a105934bb2f8b638454ca192c5789af1987041b6878b7a8139c083bfbe9124dd114adbadde286795efc486c73d1fd93c6a4eaf8abbadc2c297a03f53d478b 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <script>
  2. import TabNav from './tab-nav';
  3. export default {
  4. name: 'ElTabs',
  5. components: {
  6. TabNav
  7. },
  8. props: {
  9. type: String,
  10. activeName: String,
  11. closable: Boolean,
  12. addable: Boolean,
  13. value: {},
  14. editable: Boolean,
  15. tabPosition: {
  16. type: String,
  17. default: 'top'
  18. },
  19. beforeLeave: Function,
  20. stretch: Boolean
  21. },
  22. provide() {
  23. return {
  24. rootTabs: this
  25. };
  26. },
  27. data() {
  28. return {
  29. currentName: this.value || this.activeName,
  30. panes: []
  31. };
  32. },
  33. watch: {
  34. activeName(value) {
  35. this.setCurrentName(value);
  36. },
  37. value(value) {
  38. this.setCurrentName(value);
  39. },
  40. currentName(value) {
  41. if (this.$refs.nav) {
  42. this.$nextTick(() => {
  43. this.$refs.nav.$nextTick(_ => {
  44. this.$refs.nav.scrollToActiveTab();
  45. });
  46. });
  47. }
  48. }
  49. },
  50. methods: {
  51. calcPaneInstances(isForceUpdate = false) {
  52. if (this.$slots.default) {
  53. const paneSlots = this.$slots.default.filter(vnode => vnode.tag &&
  54. vnode.componentOptions && vnode.componentOptions.Ctor.options.name === 'ElTabPane');
  55. // update indeed
  56. const panes = paneSlots.map(({ componentInstance }) => componentInstance);
  57. const panesChanged = !(panes.length === this.panes.length && panes.every((pane, index) => pane === this.panes[index]));
  58. if (isForceUpdate || panesChanged) {
  59. this.panes = panes;
  60. }
  61. } else if (this.panes.length !== 0) {
  62. this.panes = [];
  63. }
  64. },
  65. handleTabClick(tab, tabName, event) {
  66. if (tab.disabled) return;
  67. this.setCurrentName(tabName);
  68. this.$emit('tab-click', tab, event);
  69. },
  70. handleTabRemove(pane, ev) {
  71. if (pane.disabled) return;
  72. ev.stopPropagation();
  73. this.$emit('edit', pane.name, 'remove');
  74. this.$emit('tab-remove', pane.name);
  75. },
  76. handleTabAdd() {
  77. this.$emit('edit', null, 'add');
  78. this.$emit('tab-add');
  79. },
  80. setCurrentName(value) {
  81. const changeCurrentName = () => {
  82. this.currentName = value;
  83. this.$emit('input', value);
  84. };
  85. if (this.currentName !== value && this.beforeLeave) {
  86. const before = this.beforeLeave(value, this.currentName);
  87. if (before && before.then) {
  88. before
  89. .then(() => {
  90. changeCurrentName();
  91. this.$refs.nav && this.$refs.nav.removeFocus();
  92. }, () => {
  93. // https://github.com/ElemeFE/element/pull/14816
  94. // ignore promise rejection in `before-leave` hook
  95. });
  96. } else if (before !== false) {
  97. changeCurrentName();
  98. }
  99. } else {
  100. changeCurrentName();
  101. }
  102. }
  103. },
  104. render(h) {
  105. let {
  106. type,
  107. handleTabClick,
  108. handleTabRemove,
  109. handleTabAdd,
  110. currentName,
  111. panes,
  112. editable,
  113. addable,
  114. tabPosition,
  115. stretch
  116. } = this;
  117. const newButton = editable || addable
  118. ? (
  119. <span
  120. class="el-tabs__new-tab"
  121. on-click={ handleTabAdd }
  122. tabindex="0"
  123. on-keydown={ (ev) => { if (ev.keyCode === 13) { handleTabAdd(); }} }
  124. >
  125. <i class="el-icon-plus"></i>
  126. </span>
  127. )
  128. : null;
  129. const navData = {
  130. props: {
  131. currentName,
  132. onTabClick: handleTabClick,
  133. onTabRemove: handleTabRemove,
  134. editable,
  135. type,
  136. panes,
  137. stretch
  138. },
  139. ref: 'nav'
  140. };
  141. const header = (
  142. <div class={['el-tabs__header', `is-${tabPosition}`]}>
  143. {newButton}
  144. <tab-nav { ...navData }></tab-nav>
  145. </div>
  146. );
  147. const panels = (
  148. <div class="el-tabs__content">
  149. {this.$slots.default}
  150. </div>
  151. );
  152. return (
  153. <div class={{
  154. 'el-tabs': true,
  155. 'el-tabs--card': type === 'card',
  156. [`el-tabs--${tabPosition}`]: true,
  157. 'el-tabs--border-card': type === 'border-card'
  158. }}>
  159. { tabPosition !== 'bottom' ? [header, panels] : [panels, header] }
  160. </div>
  161. );
  162. },
  163. created() {
  164. if (!this.currentName) {
  165. this.setCurrentName('0');
  166. }
  167. this.$on('tab-nav-update', this.calcPaneInstances.bind(null, true));
  168. },
  169. mounted() {
  170. this.calcPaneInstances();
  171. },
  172. updated() {
  173. this.calcPaneInstances();
  174. }
  175. };
  176. </script>