update Tabs
update Tabs
This commit is contained in:
parent
871ed4d810
commit
17f52abf5b
7 changed files with 510 additions and 20 deletions
|
@ -1,13 +1,51 @@
|
||||||
<template>
|
<template>
|
||||||
|
<div :class="prefixCls"><slot></slot></div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
const prefixCls = 'ivu-tabs-tabpane';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {},
|
name: 'TabPane',
|
||||||
data () {
|
props: {
|
||||||
return {}
|
key: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {},
|
data () {
|
||||||
methods: {}
|
return {
|
||||||
|
prefixCls: prefixCls,
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateNav () {
|
||||||
|
this.$parent.updateNav();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
label () {
|
||||||
|
this.updateNav();
|
||||||
|
},
|
||||||
|
icon () {
|
||||||
|
this.updateNav();
|
||||||
|
},
|
||||||
|
disabled () {
|
||||||
|
this.updateNav();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -1,13 +1,200 @@
|
||||||
<template>
|
<template>
|
||||||
|
<div :class="classes">
|
||||||
|
<div :class="[prefixCls + '-bar']">
|
||||||
|
<div :class="[prefixCls + '-nav-container']">
|
||||||
|
<div :class="[prefixCls + '-nav-wrap']">
|
||||||
|
<div :class="[prefixCls + '-nav-scroll']">
|
||||||
|
<div :class="[prefixCls + '-nav']" v-el:nav>
|
||||||
|
<div :class="barClasses" :style="barStyle"></div>
|
||||||
|
<div :class="tabCls(item)" v-for="item in navList" @click="handleChange($index)">
|
||||||
|
<Icon v-if="item.icon !== ''" :type="item.icon"></Icon>
|
||||||
|
{{ item.label }}
|
||||||
|
<Icon v-if="closable && type === 'card'" type="ios-close-empty" @click.stop="handleRemove($index)"></Icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="contentClasses" :style="contentStyle"><slot></slot></div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import Icon from '../icon/icon.vue';
|
||||||
|
import { oneOf, getStyle } from '../../utils/assist';
|
||||||
|
|
||||||
|
const prefixCls = 'ivu-tabs';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {},
|
components: { Icon },
|
||||||
data () {
|
props: {
|
||||||
return {}
|
activeKey: {
|
||||||
|
type: [String, Number]
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
validator (value) {
|
||||||
|
return oneOf(value, ['line', 'card']);
|
||||||
|
},
|
||||||
|
default: 'line'
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
validator (value) {
|
||||||
|
return oneOf(value, ['small', 'default']);
|
||||||
|
},
|
||||||
|
default: 'default'
|
||||||
|
},
|
||||||
|
animated: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
closable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {},
|
data () {
|
||||||
methods: {}
|
return {
|
||||||
|
prefixCls: prefixCls,
|
||||||
|
navList: [],
|
||||||
|
barWidth: 0,
|
||||||
|
barOffset: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
classes () {
|
||||||
|
return [
|
||||||
|
`${prefixCls}`,
|
||||||
|
{
|
||||||
|
[`${prefixCls}-card`]: this.type === 'card',
|
||||||
|
[`${prefixCls}-mini`]: this.size === 'small' && this.type === 'line',
|
||||||
|
[`${prefixCls}-no-animation`]: !this.animated
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
contentClasses () {
|
||||||
|
return [
|
||||||
|
`${prefixCls}-content`,
|
||||||
|
`${prefixCls}-content-animated`
|
||||||
|
]
|
||||||
|
},
|
||||||
|
barClasses () {
|
||||||
|
return [
|
||||||
|
`${prefixCls}-ink-bar`,
|
||||||
|
`${prefixCls}-ink-bar-animated`
|
||||||
|
]
|
||||||
|
},
|
||||||
|
contentStyle () {
|
||||||
|
const x = this.navList.findIndex((nav, index) => nav.key === this.activeKey);
|
||||||
|
const p = x === 0 ? '0%' : `-${x}00%`;
|
||||||
|
|
||||||
|
let style = {};
|
||||||
|
if (x > -1) {
|
||||||
|
style = {
|
||||||
|
transform: `translateX(${p}) translateZ(0px)`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
},
|
||||||
|
barStyle () {
|
||||||
|
let style = {
|
||||||
|
display: 'none',
|
||||||
|
width: `${this.barWidth}px`,
|
||||||
|
transform: `translate3d(${this.barOffset}px, 0px, 0px)`
|
||||||
|
};
|
||||||
|
if (this.type === 'line') style.display = 'block';
|
||||||
|
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getTabs () {
|
||||||
|
return this.$children.filter(item => item.$options.name === 'TabPane');
|
||||||
|
},
|
||||||
|
updateNav () {
|
||||||
|
this.navList = [];
|
||||||
|
this.getTabs().forEach((pane, index) => {
|
||||||
|
this.navList.push({
|
||||||
|
label: pane.label,
|
||||||
|
icon: pane.icon || '',
|
||||||
|
key: pane.key || index,
|
||||||
|
disabled: pane.disabled
|
||||||
|
});
|
||||||
|
if (!pane.key) pane.key = index;
|
||||||
|
if (index === 0) {
|
||||||
|
if (!this.activeKey) this.activeKey = pane.key || index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.updateBar();
|
||||||
|
},
|
||||||
|
updateBar () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const index = this.navList.findIndex((nav, index) => nav.key === this.activeKey);
|
||||||
|
const prevTabs = this.$els.nav.querySelectorAll(`.${prefixCls}-tab`);
|
||||||
|
const tab = prevTabs[index];
|
||||||
|
this.barWidth = parseFloat(getStyle(tab, 'width'));
|
||||||
|
|
||||||
|
if (index > 0) {
|
||||||
|
let offset = 0;
|
||||||
|
const gutter = this.size === 'small' ? 0 : 16;
|
||||||
|
for (let i = 0; i < index; i++) {
|
||||||
|
offset += parseFloat(getStyle(prevTabs[i], 'width')) + gutter;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.barOffset = offset;
|
||||||
|
} else {
|
||||||
|
this.barOffset = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
tabCls (item) {
|
||||||
|
return [
|
||||||
|
`${prefixCls}-tab`,
|
||||||
|
{
|
||||||
|
[`${prefixCls}-tab-disabled`]: item.disabled,
|
||||||
|
[`${prefixCls}-tab-active`]: item.key === this.activeKey
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
handleChange (index) {
|
||||||
|
const nav = this.navList[index];
|
||||||
|
if (nav.disabled) return;
|
||||||
|
this.activeKey = nav.key;
|
||||||
|
this.$emit('on-click', nav.key);
|
||||||
|
},
|
||||||
|
handleRemove (index) {
|
||||||
|
const tabs = this.getTabs();
|
||||||
|
const tab = tabs[index];
|
||||||
|
tab.$destroy(true);
|
||||||
|
|
||||||
|
if (tab.key === this.activeKey) {
|
||||||
|
const newTabs = this.getTabs();
|
||||||
|
let activeKey = -1;
|
||||||
|
|
||||||
|
if (newTabs.length) {
|
||||||
|
const leftNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex < index);
|
||||||
|
const rightNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex > index);
|
||||||
|
|
||||||
|
if (rightNoDisabledTabs.length) {
|
||||||
|
activeKey = rightNoDisabledTabs[0].key;
|
||||||
|
} else if (leftNoDisabledTabs.length) {
|
||||||
|
activeKey = leftNoDisabledTabs[leftNoDisabledTabs.length - 1].key;
|
||||||
|
} else {
|
||||||
|
activeKey = newTabs[0].key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.activeKey = activeKey;
|
||||||
|
}
|
||||||
|
this.$emit('on-tab-remove', tab.key);
|
||||||
|
this.updateNav();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
compiled () {
|
||||||
|
this.updateNav();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
activeKey () {
|
||||||
|
this.updateBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -1 +1,205 @@
|
||||||
@tabs-prefix-cls: ~"@{css-prefix}tabs";
|
@tabs-prefix-cls: ~"@{css-prefix}tabs";
|
||||||
|
|
||||||
|
.@{tabs-prefix-cls} {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
color: @text-color;
|
||||||
|
.clearfix;
|
||||||
|
|
||||||
|
&-bar {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-ink-bar {
|
||||||
|
height: 2px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: @primary-color;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 1px;
|
||||||
|
z-index: 1;
|
||||||
|
transition: transform .3s @ease-in-out;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-bar {
|
||||||
|
border-bottom: 1px solid @border-color-base;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-nav-container {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
line-height: @line-height-base;
|
||||||
|
font-size: @font-size-base;
|
||||||
|
box-sizing: border-box;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
.clearfix;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-nav-container-scrolling {
|
||||||
|
padding-left: 32px;
|
||||||
|
padding-right: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-nav-wrap {
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-nav-scroll {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-nav {
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 0;
|
||||||
|
float: left;
|
||||||
|
list-style: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
transition: transform 0.5s @ease-in-out;
|
||||||
|
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
display: table;
|
||||||
|
content: " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tabs-prefix-cls}-tab-disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: default;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tabs-prefix-cls}-tab {
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin-right: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
position: relative;
|
||||||
|
transition: color .3s @ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @link-hover-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
color: @link-active-color;
|
||||||
|
}
|
||||||
|
.@{css-prefix-iconfont} {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tabs-prefix-cls}-tab-active {
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-mini &-nav-container {
|
||||||
|
font-size: @font-size-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-mini &-tab {
|
||||||
|
margin-right: 0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& {
|
||||||
|
.@{tabs-prefix-cls}-content-animated {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
will-change: transform;
|
||||||
|
transition: transform .3s @ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tabs-prefix-cls}-tabpane {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 100%;
|
||||||
|
transition: opacity .3s;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tabs-prefix-cls}-tabpane-inactive {
|
||||||
|
opacity: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// card style
|
||||||
|
&&-card > &-bar &-nav-container {
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
&&-card > &-bar &-ink-bar {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
&&-card > &-bar &-tab {
|
||||||
|
margin: 0;
|
||||||
|
margin-right: 4px;
|
||||||
|
height: 31px;
|
||||||
|
padding: 5px 16px 4px;
|
||||||
|
border: 1px solid @border-color-base;
|
||||||
|
border-bottom: 0;
|
||||||
|
border-radius: @btn-border-radius @btn-border-radius 0 0;
|
||||||
|
transition: all 0.3s @ease-in-out;
|
||||||
|
background: @table-thead-bg;
|
||||||
|
}
|
||||||
|
&&-card > &-bar &-tab-active {
|
||||||
|
height: 32px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
background: #fff;
|
||||||
|
transform: translateZ(0);
|
||||||
|
border-color: @border-color-base;
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
&&-card > &-bar &-nav-wrap {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
&&-card > &-bar &-tab .@{css-prefix-iconfont}-ios-close-empty {
|
||||||
|
width: 0;
|
||||||
|
height: 22px;
|
||||||
|
font-size: 22px;
|
||||||
|
margin-right: 0;
|
||||||
|
color: @legend-color;
|
||||||
|
text-align: right;
|
||||||
|
vertical-align: middle;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
transform-origin: 100% 50%;
|
||||||
|
transition: all 0.3s @ease-in-out;
|
||||||
|
&:hover {
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&&-card > &-bar &-tab-active .@{css-prefix-iconfont}-ios-close-empty,
|
||||||
|
&&-card > &-bar &-tab:hover .@{css-prefix-iconfont}-ios-close-empty {
|
||||||
|
width: 14px;
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tabs-prefix-cls}-no-animation{
|
||||||
|
.@{tabs-prefix-cls}-content {
|
||||||
|
&-animated {
|
||||||
|
transform: none!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .@{tabs-prefix-cls}-tabpane-inactive {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,7 @@ li + li {
|
||||||
<li><a v-link="'/transfer'">Transfer</a></li>
|
<li><a v-link="'/transfer'">Transfer</a></li>
|
||||||
<li><a v-link="'/table'">Table</a></li>
|
<li><a v-link="'/table'">Table</a></li>
|
||||||
<li><a v-link="'/dropdown'">Dropdown</a></li>
|
<li><a v-link="'/dropdown'">Dropdown</a></li>
|
||||||
|
<li><a v-link="'/tabs'">Tabs</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
|
|
|
@ -112,6 +112,11 @@ router.map({
|
||||||
component: function (resolve) {
|
component: function (resolve) {
|
||||||
require(['./routers/dropdown.vue'], resolve);
|
require(['./routers/dropdown.vue'], resolve);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'/tabs': {
|
||||||
|
component: function (resolve) {
|
||||||
|
require(['./routers/tabs.vue'], resolve);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<i-table border :columns="columns1" :data="data1">
|
<i-table border :content="self" :columns="columns7" :data="data6"></i-table>
|
||||||
<div slot="header">表格标题</div>
|
|
||||||
<div slot="footer">表格页脚</div>
|
|
||||||
</i-table>
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
columns1: [
|
self: this,
|
||||||
|
columns7: [
|
||||||
{
|
{
|
||||||
title: '姓名',
|
title: '姓名',
|
||||||
key: 'name'
|
key: 'name',
|
||||||
|
render (row, column, index) {
|
||||||
|
return `<Icon type="person"></Icon> <strong>${row.name}</strong>`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '年龄',
|
title: '年龄',
|
||||||
|
@ -20,9 +21,18 @@
|
||||||
{
|
{
|
||||||
title: '地址',
|
title: '地址',
|
||||||
key: 'address'
|
key: 'address'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
width: 150,
|
||||||
|
align: 'center',
|
||||||
|
render (row, column, index) {
|
||||||
|
return `<i-button type="primary" size="small" @click="show(${index})">查看</i-button> <i-button type="error" size="small" @click="remove(${index})">删除</i-button>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data1: [
|
data6: [
|
||||||
{
|
{
|
||||||
name: '王小明',
|
name: '王小明',
|
||||||
age: 18,
|
age: 18,
|
||||||
|
@ -45,6 +55,17 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
show (index) {
|
||||||
|
this.$Modal.info({
|
||||||
|
title: '用户信息',
|
||||||
|
content: `姓名:${this.data6[index].name}<br>年龄:${this.data6[index].age}<br>地址:${this.data6[index].address}`
|
||||||
|
})
|
||||||
|
},
|
||||||
|
remove (index) {
|
||||||
|
this.data6.splice(index, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
34
test/routers/tabs.vue
Normal file
34
test/routers/tabs.vue
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<template>
|
||||||
|
<i-button @click="toggleClose" :animated="false">closable</i-button>
|
||||||
|
|
||||||
|
<Tabs type="line" :closable="closable" :animated="false">
|
||||||
|
<Tab-pane label="Tab 1">Tab1 content</Tab-pane>
|
||||||
|
<Tab-pane label="Tab 2" icon="ionic">Tab2 content</Tab-pane>
|
||||||
|
<Tab-pane label="Tab 3" disabled>Tab3 content</Tab-pane>
|
||||||
|
<Tab-pane label="Tab 4">Tab4 content</Tab-pane>
|
||||||
|
<Tab-pane label="Tab 5">Tab5 content</Tab-pane>
|
||||||
|
<Tab-pane label="Tab 6" icon="ionic">Tab6 content</Tab-pane>
|
||||||
|
<Tab-pane label="Tab 7" disabled>Tab7 content</Tab-pane>
|
||||||
|
<Tab-pane label="Tab 888888888">Tab8 content</Tab-pane>
|
||||||
|
</Tabs>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
closable: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleClose () {
|
||||||
|
this.closable = !this.closable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
Loading…
Add table
Reference in a new issue