mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2024-12-15 04:42:23 +08:00
Merge branch 'develop' of ***REMOVED*** into develop
This commit is contained in:
commit
b111a5ebf6
@ -789,7 +789,7 @@ export default {
|
||||
className: (params) => {
|
||||
let row = params.row
|
||||
if (row.age > 10) {
|
||||
return 'older'
|
||||
return 'older higher'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
@ -980,6 +980,10 @@ export default {
|
||||
methods: {
|
||||
handleClick (params) {
|
||||
alert('delete' + JSON.stringify(params))
|
||||
this.$set(this.data, params._index, {
|
||||
...params.row,
|
||||
age: 100
|
||||
})
|
||||
},
|
||||
onChange1 ({ filter, sorter, pagination, search }) {
|
||||
console.log('执行', { filter, sorter, pagination, search })
|
||||
@ -1000,4 +1004,7 @@ export default {
|
||||
.older{
|
||||
background:rgb(255, 204, 146);
|
||||
}
|
||||
.higher {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
|
@ -278,7 +278,7 @@
|
||||
>
|
||||
The key in form-model does not support the form with ' . '.<br>
|
||||
And does not init the value of parameters with 'undefined'.<br>
|
||||
ResetForm Method: only can reset the item with prop.<br>
|
||||
ResetForm Method: only can reset the item with prop. And Donnot deal the nesting form context<br>
|
||||
ValidateForm Method: support validate specified items by the second parameter in form of array.<br>
|
||||
</n-form-item>
|
||||
<n-popover>
|
||||
@ -308,7 +308,7 @@
|
||||
</n-form-item>
|
||||
<n-form-item
|
||||
prop="mutiSelect.0"
|
||||
label="Select"
|
||||
label="multi-select"
|
||||
>
|
||||
<n-select
|
||||
v-model="validateForm.mutiSelect[0]"
|
||||
@ -942,7 +942,6 @@ export default {
|
||||
console.log('unpass', e)
|
||||
})
|
||||
},
|
||||
|
||||
formReset (ref) {
|
||||
this.$refs[ref].resetForm()
|
||||
},
|
||||
|
328
demo/components/tabDemo.vue
Normal file
328
demo/components/tabDemo.vue
Normal file
@ -0,0 +1,328 @@
|
||||
<template>
|
||||
<div
|
||||
ref="doc"
|
||||
class="n-doc"
|
||||
>
|
||||
<div class="n-doc-header">
|
||||
<n-gradient-text :font-size="20">
|
||||
Tab / n-tab
|
||||
</n-gradient-text>
|
||||
</div>
|
||||
<div class="n-doc-body">
|
||||
<div class="n-doc-section">
|
||||
<div class="n-doc-section__header">
|
||||
Basic Usage / board mode
|
||||
</div>
|
||||
<div class="n-doc-section__view">
|
||||
<n-tab
|
||||
type="board"
|
||||
addable
|
||||
>
|
||||
<n-tab-panel label="Select">
|
||||
<n-form
|
||||
inline
|
||||
:label-width="80"
|
||||
>
|
||||
<n-form-item label="name">
|
||||
<n-input placeholder="Input your name" />
|
||||
</n-form-item>
|
||||
<n-form-item label="age">
|
||||
<n-input placeholder="Input your age" />
|
||||
</n-form-item>
|
||||
<n-form-item label="phone">
|
||||
<n-input placeholder="Input your phone number" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
label="Hover"
|
||||
>
|
||||
haha: <n-input />
|
||||
</n-tab-panel>
|
||||
<n-tab-panel label="Normal">
|
||||
lala: <n-icon />
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
disabled
|
||||
label="Disable"
|
||||
>
|
||||
hehe: <n-button />
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
v-for="(item, i) in panels"
|
||||
:key="i"
|
||||
:label="item.label"
|
||||
>
|
||||
{{ item.content }}
|
||||
</n-tab-panel>
|
||||
</n-tab>
|
||||
</div>
|
||||
<div class="n-doc-section__source">
|
||||
<textarea v-pre>
|
||||
<n-tab
|
||||
type="board"
|
||||
addable
|
||||
>
|
||||
<n-tab-panel label="form">
|
||||
<n-form
|
||||
inline
|
||||
:label-width="80"
|
||||
>
|
||||
<n-form-item label="name">
|
||||
<n-input placeholder="Input your name" />
|
||||
</n-form-item>
|
||||
<n-form-item label="age">
|
||||
<n-input placeholder="Input your age" />
|
||||
</n-form-item>
|
||||
<n-form-item label="phone">
|
||||
<n-input placeholder="Input your phone number" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
disabled
|
||||
label="input"
|
||||
>
|
||||
haha: <n-input />
|
||||
</n-tab-panel>
|
||||
<n-tab-panel label="icon">
|
||||
lala: <n-icon />
|
||||
</n-tab-panel>
|
||||
<n-tab-panel label="button">
|
||||
hehe: <n-button />
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
v-for="(item, i) in panels"
|
||||
:key="i"
|
||||
:label="item.label"
|
||||
>
|
||||
{{ item.content }}
|
||||
</n-tab-panel>
|
||||
</n-tab>
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="n-doc-section">
|
||||
<div class="n-doc-section__header">
|
||||
Basic Usage / name attribute
|
||||
</div>
|
||||
<div class="n-doc-section__view">
|
||||
<n-button @click="updateName">
|
||||
Change Name
|
||||
</n-button>
|
||||
<n-tab
|
||||
:name="toggleName"
|
||||
>
|
||||
<n-tab-panel
|
||||
name="a"
|
||||
label="Select"
|
||||
>
|
||||
Name: a. <br>
|
||||
We can control the element display by setting the tab's name attribute.<br>
|
||||
Also we can use tab-panel's active attribute to init the display.
|
||||
And tab-panels's active attribute is the first priority.
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
name="b"
|
||||
label="Hover"
|
||||
>
|
||||
Name: b. <br>
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
disabled
|
||||
label="Disabled"
|
||||
/>
|
||||
</n-tab>
|
||||
</div>
|
||||
<div class="n-doc-section__source">
|
||||
<textarea v-pre>
|
||||
<n-button @click="updateName">
|
||||
Change Name
|
||||
</n-button>
|
||||
<n-tab
|
||||
:name="toggleName"
|
||||
>
|
||||
<n-tab-panel
|
||||
name="a"
|
||||
label="a"
|
||||
>
|
||||
Name: a. <br>
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
name="b"
|
||||
label="b"
|
||||
>
|
||||
Name: b. <br>
|
||||
We can control the element display by setting the tab's name attribute.<br>
|
||||
Also we can use tab-panel's active attribute to init the display.
|
||||
And tab-panels's active attribute is the first priority.
|
||||
</n-tab-panel>
|
||||
</n-tab>
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
toggleName: 'b'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateName () {
|
||||
this.toggleName = this.toggleName === 'a' ? 'b' : 'a'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="n-doc-section">
|
||||
<div class="n-doc-section__header">
|
||||
Basic Usage / name attribute
|
||||
</div>
|
||||
<div class="n-doc-section__view">
|
||||
<n-tab
|
||||
type="card"
|
||||
closable
|
||||
addable
|
||||
:add-panel="addPanelItem"
|
||||
:tab-remove="tabRemove"
|
||||
:before-leave="beforeLeave"
|
||||
>
|
||||
<n-tab-panel label="closable">
|
||||
I am a input <n-input placeholder="lalala" />
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
active
|
||||
label="card mode"
|
||||
>
|
||||
I am an active button <n-button> hahaha </n-button>
|
||||
<br>Two mode supported now: normal and card.
|
||||
closable only can use in card mode.
|
||||
<br>
|
||||
Configure addable and add-panel attributes to dynamically add panels.
|
||||
<br>
|
||||
before-leave attribute: callback before tab-panel changed
|
||||
<br>
|
||||
tab-remove attribute: callback after tab removed, param is the instance of the panel.
|
||||
<br>
|
||||
</n-tab-panel>
|
||||
<n-tab-panel label="addable">
|
||||
I am a button <n-button> hahaha </n-button>
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
v-for="(i, j) in panels"
|
||||
:key="j"
|
||||
:label="i.label"
|
||||
>
|
||||
{{ i.content }}
|
||||
</n-tab-panel>
|
||||
</n-tab>
|
||||
</div>
|
||||
<div class="n-doc-section__source">
|
||||
<textarea v-pre>
|
||||
<n-tab
|
||||
type="card"
|
||||
closable
|
||||
addable
|
||||
:add-panel="addPanelItem"
|
||||
:tab-remove="tabRemove"
|
||||
:before-leave="beforeLeave"
|
||||
>
|
||||
<n-tab-panel label="closable">
|
||||
I am a input <n-input placeholder="lalala" />
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
active
|
||||
label="card mode"
|
||||
>
|
||||
I am an active button <n-button> hahaha </n-button>
|
||||
<br>Two mode supported now: normal and card.
|
||||
closable only can use in card mode.
|
||||
<br>
|
||||
Configure addable and add-panel attributes to dynamically add panels.
|
||||
<br>
|
||||
before-leave attribute: callback before tab-panel changed
|
||||
<br>
|
||||
tab-remove attribute: callback after tab removed, param is the instance of the panel.
|
||||
<br>
|
||||
</n-tab-panel>
|
||||
<n-tab-panel label="addable">
|
||||
I am a button <n-button> hahaha </n-button>
|
||||
</n-tab-panel>
|
||||
<n-tab-panel
|
||||
v-for="(i, j) in panels"
|
||||
:key="j"
|
||||
:label="i.label"
|
||||
>
|
||||
{{ i.content }}
|
||||
</n-tab-panel>
|
||||
</n-tab>
|
||||
<script>
|
||||
export {
|
||||
data () {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addPanelItem () {
|
||||
this.panels.push({
|
||||
label: this.panels.length,
|
||||
content: 'i am number' + this.panels.length
|
||||
})
|
||||
},
|
||||
tabRemove (i) {
|
||||
console.log('callback tab-remove', i)
|
||||
},
|
||||
beforeLeave (n, o) {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve('e')
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import docCodeEditorMixin from './docCodeEditorMixin'
|
||||
|
||||
export default {
|
||||
mixins: [docCodeEditorMixin],
|
||||
data () {
|
||||
return {
|
||||
panels: [],
|
||||
toggleName: 'b'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addPanelItem () {
|
||||
this.panels.push({
|
||||
label: this.panels.length,
|
||||
content: 'i am number' + this.panels.length
|
||||
})
|
||||
},
|
||||
tabRemove (i) {
|
||||
console.log('callback tab-remove', i)
|
||||
},
|
||||
updateName () {
|
||||
this.toggleName = this.toggleName === 'a' ? 'b' : 'a'
|
||||
},
|
||||
beforeLeave (n, o) {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve('e')
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -166,6 +166,10 @@ export default {
|
||||
{
|
||||
name: 'Form',
|
||||
path: '/n-form'
|
||||
},
|
||||
{
|
||||
name: 'Tab',
|
||||
path: '/n-tab'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -28,6 +28,7 @@ import inputNumberDemo from './components/inputNumberDemo'
|
||||
import nimbusIconDemo from './components/nimbusIconDemo'
|
||||
import radioDemo from './components/radioDemo'
|
||||
import formDemo from './components/formDemo'
|
||||
import tabDemo from './components/tabDemo'
|
||||
import timePickerDemo from './components/timePickerDemo'
|
||||
import confirmDemo from './components/confirmDemo'
|
||||
|
||||
@ -93,6 +94,7 @@ const routes = [
|
||||
{ path: '/n-nimbus-icon', component: nimbusIconDemo },
|
||||
{ path: '/n-radio', component: radioDemo },
|
||||
{ path: '/n-form', component: formDemo },
|
||||
{ path: '/n-tab', component: tabDemo },
|
||||
{ path: '/n-time-picker', component: timePickerDemo },
|
||||
{ path: '/n-confirm', component: confirmDemo },
|
||||
|
||||
|
2
index.js
2
index.js
@ -26,6 +26,7 @@ import DatePicker from './packages/common/DatePicker'
|
||||
import InputNumber from './packages/common/InputNumber'
|
||||
import Radio from './packages/common/Radio'
|
||||
import Form from './packages/common/Form'
|
||||
import Tab from './packages/common/Tab'
|
||||
import TimePicker from './packages/common/TimePicker'
|
||||
import ServiceCard from './packages/nimbus/ServiceCard'
|
||||
import HomeLayout from './packages/nimbus/HomeLayout'
|
||||
@ -73,6 +74,7 @@ function install (Vue) {
|
||||
Radio.install(Vue)
|
||||
Cascader.install(Vue)
|
||||
Form.install(Vue)
|
||||
Tab.install(Vue)
|
||||
TimePicker.install(Vue)
|
||||
Scrollbar.install(Vue)
|
||||
Steps.install(Vue)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "naive-ui",
|
||||
"version": "0.2.73",
|
||||
"version": "0.2.74",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -8,14 +8,11 @@ export default {
|
||||
keyName: String
|
||||
},
|
||||
render: (h, ctx) => {
|
||||
const params = {
|
||||
row: ctx.props.row,
|
||||
index: ctx.props.index
|
||||
}
|
||||
const params = ctx.props.row
|
||||
|
||||
const { keyName, render, row } = ctx.props
|
||||
if (render) {
|
||||
return h('div', [render(h, params)])
|
||||
} else return h('div', [row[keyName]])
|
||||
} else return h('div', [params.row[keyName]])
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@
|
||||
v-for="column in columns"
|
||||
:key="column.key"
|
||||
:style="computeAlign(column)"
|
||||
:class="computeTdClass(column,{row:rowData,index:i,key:column.key})"
|
||||
:class="computeTdClass(column,rowData)"
|
||||
>
|
||||
<row
|
||||
:index="i"
|
||||
@ -218,9 +218,15 @@ export default {
|
||||
// const sortIndexs = new Array(this.columns.length).fill(0).map((item, idx) => {
|
||||
// return this.columns[idx].order ? this.columns[idx].order : 0
|
||||
// })
|
||||
console.log(sortIndexs)
|
||||
// console.log(sortIndexs)
|
||||
let copyData = this.data.slice(0).map((row, idx) => {
|
||||
return {
|
||||
row,
|
||||
_index: idx
|
||||
}
|
||||
})
|
||||
return {
|
||||
copyData: this.data.slice(0),
|
||||
copyData,
|
||||
sortIndexs,
|
||||
wrapperWidth: 'unset',
|
||||
tbodyWidth: 'auto;',
|
||||
@ -342,7 +348,12 @@ export default {
|
||||
this.$emit('on-page-change', this.paginationer)
|
||||
},
|
||||
data () {
|
||||
this.copyData = this.data.slice(0)
|
||||
this.copyData = this.data.slice(0).map((row, idx) => {
|
||||
return {
|
||||
row,
|
||||
_index: idx
|
||||
}
|
||||
})
|
||||
this.searchData = this.computeShowingData()
|
||||
this.searchDataNoSort = null
|
||||
},
|
||||
@ -401,7 +412,7 @@ export default {
|
||||
} else if (typeof column.className === 'function') {
|
||||
className.push(column.className(params))
|
||||
}
|
||||
console.log(className)
|
||||
// console.log(className)
|
||||
return className
|
||||
},
|
||||
/**
|
||||
@ -487,7 +498,7 @@ export default {
|
||||
const { value, filterFn } = this.currentFilterColumn[key]
|
||||
if (value && filterFn !== 'custom') {
|
||||
data = data.filter(item => {
|
||||
return filterFn(value, item)
|
||||
return filterFn(value, item.row)
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -496,7 +507,7 @@ export default {
|
||||
if (this.currentSearchColumn && this.search.onSearch !== 'custom') {
|
||||
const { key, word } = this.currentSearchColumn
|
||||
data = data.filter(item => {
|
||||
return this.search.onSearch(key, word, item)
|
||||
return this.search.onSearch(key, word, item.row)
|
||||
})
|
||||
}
|
||||
|
||||
@ -524,6 +535,8 @@ export default {
|
||||
}
|
||||
} else {
|
||||
data = data.sort((a, b) => {
|
||||
a = a.row
|
||||
b = b.row
|
||||
if (type > 0) {
|
||||
if (column.sorter) {
|
||||
return column.sorter(a, b)
|
||||
|
@ -7,6 +7,7 @@
|
||||
}"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="n-input-number__minus-button"
|
||||
:class="{
|
||||
[`n-input-bumber__button--disabled`]: value !== null && safeMin !== null && value <= safeMin
|
||||
@ -24,6 +25,7 @@
|
||||
@keyup.enter="handleBlurOrEnter"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="n-input-number__add-button"
|
||||
:class="{
|
||||
[`n-input-bumber__button--disabled`]: value !== null && safeMax !== null && value >= safeMax
|
||||
|
9
packages/common/Tab/index.js
Normal file
9
packages/common/Tab/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
import Tab from './src/main.vue'
|
||||
import NTabPanel from './src/TabPanel.vue'
|
||||
|
||||
Tab.install = function (Vue) {
|
||||
Vue.component(Tab.name, Tab)
|
||||
Vue.component(NTabPanel.name, NTabPanel)
|
||||
}
|
||||
|
||||
export default Tab
|
87
packages/common/Tab/src/TabPanel.vue
Normal file
87
packages/common/Tab/src/TabPanel.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="unDelete"
|
||||
ref="tab-panel"
|
||||
:class="cls"
|
||||
:style="style"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'NTabPanel',
|
||||
inject: [ 'NTab' ],
|
||||
props: {
|
||||
label: {
|
||||
type: [String, Number],
|
||||
default: undefined
|
||||
},
|
||||
name: {
|
||||
type: String || Number,
|
||||
default: undefined
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
unDelete: true,
|
||||
isShow: false,
|
||||
style: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cls () {
|
||||
return this.isShow ? 'n-tab-panel n-tab-panel_active' : 'n-tab-panel'
|
||||
},
|
||||
offset () {
|
||||
return this.NTab.offset
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
offset: {
|
||||
handler (n) {
|
||||
this.setTransfer(n)
|
||||
this.$forceUpdate()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.NTab.updateLabels()
|
||||
if (this._NaiveTabOrder === this.NTab.active) {
|
||||
this.updateIsShow(true)
|
||||
}
|
||||
this.$on('display-none', this.setDisplayNone)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$off('display-none', this.setDisplayNone)
|
||||
},
|
||||
methods: {
|
||||
updateIsShow (flag) {
|
||||
this.isShow = flag
|
||||
this.$forceUpdate()
|
||||
},
|
||||
setTransfer (per) {
|
||||
// 这里可以优化, 直接在上层div做整体移动, 不需要对子元素移动
|
||||
this.style.transform = 'translateX(' + per + ')'
|
||||
},
|
||||
setDisplayNone (i) {
|
||||
if (i === this._NaiveTabOrder) {
|
||||
this.unDelete = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
242
packages/common/Tab/src/main.vue
Normal file
242
packages/common/Tab/src/main.vue
Normal file
@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<div
|
||||
:class="tabCls"
|
||||
>
|
||||
<div
|
||||
:class="addable ? 'n-tab--label_addable n-tab--label' : 'n-tab--label'"
|
||||
>
|
||||
<div style="display: inline-flex;margin-bottom: -1px;">
|
||||
<div
|
||||
v-for="(label, i) in labels"
|
||||
:key="i"
|
||||
:class="getClass(i)"
|
||||
@click="clickChange($event, i)"
|
||||
>
|
||||
<span class="n-tab--label-content">
|
||||
<span class="n-tab--label-text">{{ label }}</span>
|
||||
<n-icon
|
||||
class="n-tab--label-delete"
|
||||
type="ios-close"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<n-icon
|
||||
type="ios-add-circle-outline"
|
||||
class="n-tab--label-add"
|
||||
@click="addPanelItem"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref="slot"
|
||||
class="n-tab--slot-panel"
|
||||
>
|
||||
<div class="n-tab--slot">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Emitter from '../../../mixins/emitter'
|
||||
|
||||
export default {
|
||||
name: 'NTab',
|
||||
provide () {
|
||||
return {
|
||||
NTab: this
|
||||
}
|
||||
},
|
||||
mixins: [ Emitter ],
|
||||
props: {
|
||||
name: {
|
||||
type: String || Number,
|
||||
default: undefined
|
||||
},
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'normal' // card board
|
||||
},
|
||||
addable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
addPanel: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
beforeLeave: {
|
||||
type: Function,
|
||||
default: () => { return true }
|
||||
},
|
||||
// tabClick: {
|
||||
// type: Function,
|
||||
// default: () => {}
|
||||
// },
|
||||
tabRemove: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
// tabAdd: {
|
||||
// type: Function,
|
||||
// default: () => {}
|
||||
// },
|
||||
// edit: {
|
||||
// type: Function,
|
||||
// default: () => {}
|
||||
// }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
labels: [],
|
||||
names: [],
|
||||
active: null, // number
|
||||
offset: null,
|
||||
prefix: 'n-tab--label'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tabCls () {
|
||||
let type = ['normal', 'card', 'board']
|
||||
let cls = 'n-tab '
|
||||
return type.indexOf(this.type) > -1 ? cls + 'n-tab_' + this.type + '' : cls + 'n-tab_normal '
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
name (n, o) {
|
||||
let i = this.names.indexOf(n)
|
||||
if (i === -1) {
|
||||
return
|
||||
}
|
||||
this.specPanel(i)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.updateOrder()
|
||||
this.initOffset()
|
||||
},
|
||||
mounted () {
|
||||
if (this.active === null) {
|
||||
this.active = 0
|
||||
let panel = this.$children.find(i => { return i.$options.name === 'NTabPanel' })
|
||||
panel.updateIsShow(true)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getClass (i) {
|
||||
let panel = this.getTabPanel(i) || {}
|
||||
let cName = this.prefix + '-item '
|
||||
let type = ['normal', 'card', 'board']
|
||||
cName += (panel.disabled ? this.prefix + '-item_disabled ' : '')
|
||||
cName += (this.active === i ? this.prefix + '-item_active ' : '')
|
||||
cName += type.indexOf(this.type) > -1 ? this.prefix + '-item_' + this.type + '' : ''
|
||||
cName += type.indexOf(this.type) && (this.closable || panel.closable) ? this.prefix + '-item_close ' : ''
|
||||
|
||||
return cName
|
||||
},
|
||||
updateLabels () {
|
||||
let labels = []
|
||||
let j = 0
|
||||
this.names = []
|
||||
this.$children.forEach((i) => {
|
||||
if (i.$options.name === 'NTabPanel') {
|
||||
let l = i.label === undefined ? '' : i.label
|
||||
labels.push(l)
|
||||
this.names.push(i.name)
|
||||
i._NaiveTabOrder = j++
|
||||
}
|
||||
})
|
||||
this.$set(this, 'labels', labels)
|
||||
},
|
||||
initActive () {
|
||||
this.active = this.labels.length - 1
|
||||
},
|
||||
clickChange (e, i) {
|
||||
let oldName = this.names[this.active]
|
||||
let newName = this.names[i]
|
||||
let eName = e.target.className
|
||||
let eParentName = e.target.parentElement.className
|
||||
let ePPName = ((e.target.parentElement || {}).parentElement || {}).className
|
||||
let isLabel = eName.indexOf('n-tab--label-item') > -1 || eParentName.indexOf('n-tab--label-item') > -1 || ePPName.indexOf('n-tab--label-item') > -1
|
||||
if (isLabel) {
|
||||
Promise.resolve(this.beforeLeave(newName, oldName)).then(res => {
|
||||
if (res) {
|
||||
this.updateActive(e, i)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.updateActive(e, i)
|
||||
}
|
||||
},
|
||||
updateActive (e, i) {
|
||||
let eName = e.target.className
|
||||
let eParentName = e.target.parentElement.className
|
||||
let ePPName = ((e.target.parentElement || {}).parentElement || {}).className
|
||||
let isLabel = eName.indexOf('n-tab--label-item') > -1 || eParentName.indexOf('n-tab--label-item') > -1 || ePPName.indexOf('n-tab--label-item') > -1
|
||||
if (eName.indexOf('n-tab--label-delete') > -1) {
|
||||
// steps while deleting, need to set display none to label and the tab panel and then init the display
|
||||
this.labels.splice(i, 1)
|
||||
this.names.splice(i, 1)
|
||||
this.broadcast('NTabPanel', 'display-none', i)
|
||||
this.updateOrder(false)
|
||||
if (this.active === i) {
|
||||
this.active = 0
|
||||
this.getTabPanel(this.active).updateIsShow(true)
|
||||
} else {
|
||||
this.active = i > this.active ? this.active : this.active - 1
|
||||
}
|
||||
this.offset = '-' + this.active * 100 + '%'
|
||||
this.tabRemove(this.getTabPanel(i))
|
||||
} else if (isLabel && i !== this.active) {
|
||||
this.specPanel(i)
|
||||
}
|
||||
},
|
||||
specPanel (i) {
|
||||
this.getTabPanel(this.active).updateIsShow(false)
|
||||
this.active = i
|
||||
this.getTabPanel(i).updateIsShow(true)
|
||||
this.offset = '-' + this.active * 100 + '%'
|
||||
},
|
||||
updateOrder (init = true) {
|
||||
// method duplicate with updateLabels
|
||||
let j = 0
|
||||
let arr = init ? this.$slots.default : this.$children
|
||||
arr.forEach((i) => {
|
||||
if (init || i.unDelete) {
|
||||
i._NaiveTabOrder = j++
|
||||
} else {
|
||||
delete i._NaiveTabOrder
|
||||
}
|
||||
})
|
||||
},
|
||||
initOffset () {
|
||||
// method duplicate with updateLabels
|
||||
let order = this.$slots.default.findIndex((i) => {
|
||||
let props = i.componentOptions.propsData
|
||||
if ([undefined, false].indexOf(props.active) === -1) {
|
||||
return true
|
||||
}
|
||||
if (this.name === props.name && this.name !== undefined) {
|
||||
return true
|
||||
}
|
||||
})
|
||||
this.active = order > -1 ? order : 0
|
||||
this.offset = '-' + this.active * 100 + '%'
|
||||
},
|
||||
getTabPanel (i) {
|
||||
if (i === undefined) return
|
||||
return this.$children.find(c => {
|
||||
return c._NaiveTabOrder === i
|
||||
})
|
||||
},
|
||||
addPanelItem () {
|
||||
this.addPanel()
|
||||
this.updateOrder(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,6 +1,6 @@
|
||||
function broadcast (componentName, eventName, params) {
|
||||
this.$children.forEach(child => {
|
||||
let name = child.$options.componentName
|
||||
let name = child.$options.name
|
||||
if (name === componentName) {
|
||||
child.$emit.apply(child, [eventName].concat(params))
|
||||
} else {
|
||||
|
@ -27,8 +27,22 @@ const getObjValue = (obj, keys) => {
|
||||
return keys.reduce((res, n) => (res !== undefined && res[n] !== undefined ? res[n] : null), obj)
|
||||
}
|
||||
|
||||
/**
|
||||
* transfer function into promise
|
||||
*/
|
||||
const funcToPromise = (fn, receiver) => {
|
||||
return (...args) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fn.apply(receiver, [...args, (err, res) => {
|
||||
return err ? reject(err) : resolve(res)
|
||||
}])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getScrollParent,
|
||||
deepClone,
|
||||
getObjValue
|
||||
getObjValue,
|
||||
funcToPromise
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
display: block;
|
||||
padding-top: 0;
|
||||
overflow-wrap: unset;
|
||||
padding-top: 0;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
}
|
||||
|
214
styles/Tab.scss
Normal file
214
styles/Tab.scss
Normal file
@ -0,0 +1,214 @@
|
||||
@import './mixins/mixins.scss';
|
||||
@import './theme/default.scss';
|
||||
|
||||
@include b(tab) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 9px;
|
||||
background-color: rgb(23, 29, 51);
|
||||
.n-tab--label-add {
|
||||
display: none;
|
||||
font-size: 24px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.n-tab--label_addable {
|
||||
.n-tab--label-add {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.n-tab--label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
.n-tab--label-delete {
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
margin-left: 9px;
|
||||
}
|
||||
}
|
||||
&.n-tab_card {
|
||||
// background-color: rgb(75, 81, 106);
|
||||
padding-top: 6px;
|
||||
padding-bottom: 16px;
|
||||
.n-tab--label {
|
||||
padding: 0 8px;
|
||||
// margin-bottom: 12px;
|
||||
.n-tab--label-item:not(:last-child) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.n-tab--label-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
color: #ffffff;
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
border-radius: 6px 6px 0 0;
|
||||
.n-tab--label-delete {
|
||||
color: #F2F2F4;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
.n-tab--label-item_active {
|
||||
.n-tab--label-content {
|
||||
color: #50E3C2;
|
||||
background-color: rgba(71, 228, 194, 0.1);
|
||||
}
|
||||
}
|
||||
.n-tab--label-item_disabled {
|
||||
pointer-events: none;
|
||||
.n-tab--label-content {
|
||||
opacity: 0.25;
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
.n-tab--label-delete {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
.n-tab--label-text:hover {
|
||||
color: #50E3C2;
|
||||
}
|
||||
.n-tab--label-delete:hover {
|
||||
color: #63E2B7;
|
||||
}
|
||||
}
|
||||
.n-tab--slot-panel {
|
||||
padding: 12px 24px 16px;
|
||||
}
|
||||
.n-tab--slot {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
&.n-tab_board {
|
||||
background-color: rgb(23, 29, 51);
|
||||
.n-tab--label {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .3);
|
||||
.n-tab--label-item:not(:last-child) {
|
||||
margin-right: 3px;
|
||||
}
|
||||
.n-tab--label-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px 6px 0 0;
|
||||
border: 1px solid rgba(255, 255, 255, .5);
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
color: #ffffff;
|
||||
.n-tab--label-delete {
|
||||
color: #F2F2F4;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
.n-tab--label-item_active {
|
||||
.n-tab--label-content {
|
||||
color: #63E2B7;
|
||||
border-color: #63E2B7;
|
||||
border-bottom: none;
|
||||
background: rgb(23, 29, 51);
|
||||
}
|
||||
&:after {
|
||||
display: block;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #171D33;
|
||||
}
|
||||
}
|
||||
.n-tab--label-item_disabled {
|
||||
pointer-events: none;
|
||||
.n-tab--label-content {
|
||||
opacity: 0.3;
|
||||
}
|
||||
.n-tab--label-delete {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
.n-tab--label-text:hover {
|
||||
color: #50E3C2;
|
||||
}
|
||||
.n-tab--label-delete:hover {
|
||||
color: #63E2B7;
|
||||
}
|
||||
}
|
||||
.n-tab--slot-panel {
|
||||
padding: 13px 4px 16px 16px;
|
||||
border-radius: 0px 0px 9px 9px;
|
||||
}
|
||||
.n-tab--slot {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
&.n-tab_normal {
|
||||
padding: 14px 23px 20px;
|
||||
background-color: rgb(31, 38, 62);
|
||||
.n-tab--label-add {
|
||||
margin-right: -15px;
|
||||
}
|
||||
.n-tab--label {
|
||||
padding-bottom: 8px;
|
||||
.n-tab--label-item:not(:last-child) {
|
||||
margin-right: 35px;
|
||||
}
|
||||
.n-tab--label-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
padding-bottom: 8px;
|
||||
color: #ffffff;
|
||||
.n-tab--label-delete {
|
||||
color: #F2F2F4;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
.n-tab--label-item_active {
|
||||
.n-tab--label-content {
|
||||
color: #63E2B7;
|
||||
border-bottom: 2px solid #63E2B7;
|
||||
}
|
||||
}
|
||||
.n-tab--label-item_disabled {
|
||||
pointer-events: none;
|
||||
.n-tab--label-content {
|
||||
opacity: 0.3;
|
||||
}
|
||||
.n-tab--label-delete {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
.n-tab--label-text:hover {
|
||||
color: #50E3C2;
|
||||
}
|
||||
.n-tab--label-delete:hover {
|
||||
color: #63E2B7;
|
||||
}
|
||||
}
|
||||
.n-tab--slot {
|
||||
// margin: 12px 24px 16px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include b(tab-panel) {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
visibility: hidden;
|
||||
transition: transform 0.2s;
|
||||
height: 0;
|
||||
&.n-tab-panel_active {
|
||||
visibility: visible;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@
|
||||
@import './Radio.scss';
|
||||
@import './Form.scss';
|
||||
@import './TimePicker.scss';
|
||||
@import './Tab.scss';
|
||||
@import './Scrollbar.scss';
|
||||
@import './Steps.scss';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user