Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
京东前端
nutui-react
提交
627d3609
N
nutui-react
项目概览
京东前端
/
nutui-react
通知
0
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
N
nutui-react
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
627d3609
编写于
7月 11, 2022
作者:
A
ailululu
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 级联组件
上级
778d36ff
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
566 addition
and
1 deletion
+566
-1
src/config.json
src/config.json
+10
-0
src/packages/cascader/cascader.scss
src/packages/cascader/cascader.scss
+72
-0
src/packages/cascader/cascader.tsx
src/packages/cascader/cascader.tsx
+203
-0
src/packages/cascader/cascaderItem.tsx
src/packages/cascader/cascaderItem.tsx
+85
-0
src/packages/cascader/demo.tsx
src/packages/cascader/demo.tsx
+115
-0
src/packages/cascader/doc.md
src/packages/cascader/doc.md
+74
-0
src/packages/cascader/index.ts
src/packages/cascader/index.ts
+3
-0
src/packages/collapse/collapse.tsx
src/packages/collapse/collapse.tsx
+4
-1
未找到文件。
src/config.json
浏览文件 @
627d3609
...
...
@@ -392,6 +392,16 @@
"sort"
:
1
,
"show"
:
true
,
"author"
:
"swag~jun"
},
{
"version"
:
"0.1.0"
,
"name"
:
"Cascader"
,
"type"
:
"component"
,
"cName"
:
"级联选择器"
,
"desc"
:
"级联选择,用于多层级数据的选择,典型场景为省市区选择。"
,
"sort"
:
1
,
"show"
:
true
,
"author"
:
"ailululu"
}
]
},
...
...
src/packages/cascader/cascader.scss
0 → 100644
浏览文件 @
627d3609
@import
'../popup/popup.scss'
;
.nut-cascader
{
width
:
100%
;
font-size
:
14px
;
line-height
:
22px
;
.nut-popup
{
padding
:
0
;
&
.popup-bottom.round
{
padding
:
0
!
important
;
}
}
&
__title
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
padding
:
24px
20px
17px
;
text-align
:
center
;
font-weight
:
bold
;
line-height
:
20px
;
color
:
#1a1a1a
;
font-size
:
18px
;
}
.nut-tabs__titles
{
padding
:
0
10px
;
background
:
#fff
;
}
.nut-tabs__titles-item
{
flex
:
initial
;
min-width
:
auto
;
width
:
auto
;
padding
:
0
10px
;
white-space
:
nowrap
;
}
.nut-tabpane
{
padding
:
0
;
}
&
-pane
{
display
:
block
;
padding
:
0
;
margin
:
0
;
width
:
100%
;
padding-top
:
10px
;
height
:
342px
;
overflow-y
:
auto
;
-webkit-overflow-scrolling
:
touch
;
}
&
-item
{
display
:
flex
;
align-items
:
center
;
padding
:
10px
20px
;
margin
:
0
;
cursor
:
pointer
;
font-size
:
14px
;
color
:
#1a1a1a
;
&
.active
{
&
:not
(
.disabled
)
{
color
:
#fa2c19
;
}
.nut-cascader-item__icon-check
{
visibility
:
visible
;
color
:
#fa2c19
;
}
}
}
&
-item__title
{
flex
:
1
;
}
.nut-icon-checklist
{
margin-left
:
10px
;
visibility
:
hidden
;
}
}
src/packages/cascader/cascader.tsx
0 → 100644
浏览文件 @
627d3609
import
React
,
{
FunctionComponent
,
useState
,
useEffect
}
from
'
react
'
import
Popup
from
'
@/packages/popup
'
import
{
Tabs
}
from
'
@/packages/tabs/tabs
'
import
{
TabPane
}
from
'
@/packages/tabpane/tabpane
'
import
{
Icon
}
from
'
@/packages/icon/icon
'
import
{
CascaderItem
}
from
'
./cascaderItem
'
import
classNames
from
'
classnames
'
import
bem
from
'
@/utils/bem
'
interface
OptiosData
{
nodes
:
OptiosInfo
[]
selectedNode
:
OptiosInfo
|
null
paneKey
:
string
}
interface
OptiosInfo
{
text
:
string
value
:
string
paneKey
:
string
disabled
?:
boolean
loading
?:
boolean
children
:
OptiosInfo
[]
}
export
interface
CascaderProps
{
visible
:
boolean
options
:
OptiosInfo
[]
title
:
string
value
:
[]
onClose
?:
()
=>
void
onChange
:
(
params
:
any
)
=>
void
}
const
defaultProps
=
{
visible
:
false
,
options
:
[],
title
:
''
,
value
:
[],
onClose
:
()
=>
{},
onChange
:
(
params
)
=>
{},
}
as
CascaderProps
export
const
Cascader
:
FunctionComponent
<
Partial
<
CascaderProps
>
&
React
.
HTMLAttributes
<
HTMLDivElement
>
>
=
(
props
)
=>
{
const
[
tabvalue
,
setTabvalue
]
=
useState
(
'
c1
'
)
const
[
optiosData
,
setOptiosData
]
=
useState
<
OptiosData
[]
>
([])
const
[
checkedIndexList
,
setCheckedIndexList
]
=
useState
<
Array
<
number
>>
([])
const
[
selectValue
,
setSelectValue
]
=
useState
([])
const
{
visible
,
options
,
title
,
value
,
// 选中值
onClose
,
onChange
,
}
=
{
...
defaultProps
,
...
props
}
const
b
=
bem
(
'
cascader
'
)
const
classes
=
classNames
(
b
(
''
))
const
classesPane
=
classNames
({
[
`
${
b
(
''
)}
-pane`
]:
true
,
})
useEffect
(()
=>
{
initData
()
},
[])
const
initData
=
()
=>
{
console
.
log
(
'
options
'
,
options
)
let
newOptions
:
any
[]
=
[]
let
newOptionsChild
:
any
[]
=
[]
options
.
forEach
((
item
:
any
,
index
:
number
)
=>
{
console
.
log
(
'
item
'
,
item
)
item
.
paneKey
=
'
c
'
+
(
index
+
1
)
// item.checked = false
newOptionsChild
.
push
(
item
)
})
newOptions
=
[
{
nodes
:
newOptionsChild
,
selectedNode
:
null
,
paneKey
:
'
c1
'
,
},
]
setOptiosData
([...
newOptions
])
console
.
log
(
'
newOptions
'
,
newOptions
)
console
.
log
(
'
optiosData 2
'
,
optiosData
)
}
const
close
=
()
=>
{
onClose
&&
onClose
()
}
// const choose = (param: string) => {
// close()
// onChoose && onChoose(param)
// }
const
closePopup
=
()
=>
{
close
()
}
const
chooseItem
=
(
item
:
any
,
checked
:
boolean
,
index
:
number
,
tabIndex
:
number
)
=>
{
console
.
log
(
'
item:
'
,
item
)
console
.
log
(
'
checked
'
,
checked
,
'
index
'
,
index
,
tabIndex
)
let
newOptiosData
=
[...
optiosData
]
let
newCheckedIndexList
=
[...
checkedIndexList
]
// 当前状态为true,被选中,滑到下一个
// 当前状态为false,没被选中
newCheckedIndexList
[
tabIndex
]
=
index
+
1
newOptiosData
[
tabIndex
].
selectedNode
=
item
// newOptiosData = newOptiosData.slice(0, level);
if
(
item
.
children
&&
item
.
children
.
length
>
0
)
{
newOptiosData
.
push
({
nodes
:
item
.
children
||
[],
selectedNode
:
null
,
paneKey
:
'
c
'
+
(
tabIndex
+
2
),
})
}
// console.log('newOptiosData 3', newOptiosData)
// console.log('tabIndex', tabIndex)
setCheckedIndexList
(
newCheckedIndexList
)
setOptiosData
([...
newOptiosData
])
// 滑到下一个
if
(
item
.
children
&&
item
.
children
.
length
>
0
)
{
setTabvalue
(
'
c
'
+
(
tabIndex
+
2
))
}
else
{
const
pathNodes
=
optiosData
.
map
((
item
)
=>
item
.
selectedNode
)
const
optionParams
=
pathNodes
.
map
((
item
)
=>
item
.
value
)
console
.
log
(
'
optionParams
'
,
optionParams
)
onChange
(
optionParams
)
close
()
}
}
return
(
<>
<
div
className
=
{
classes
}
>
{
console
.
log
(
'
dom optiosData 2
'
,
optiosData
)
}
<
Popup
visible
=
{
visible
}
position
=
"bottom"
round
closeable
onClickOverlay
=
{
closePopup
}
onClickCloseIcon
=
{
closePopup
}
style
=
{
{
padding
:
'
30px 50px
'
}
}
>
<
div
className
=
"nut-cascader__title"
>
{
title
}
</
div
>
<
Tabs
value
=
{
tabvalue
}
titleNode
=
{
()
=>
{
return
optiosData
.
map
((
pane
)
=>
(
<
div
onClick
=
{
()
=>
setTabvalue
(
pane
.
paneKey
)
}
className
=
{
`nut-tabs__titles-item
${
tabvalue
==
pane
.
paneKey
?
'
active
'
:
''
}
`
}
key
=
{
pane
.
paneKey
}
>
<
span
className
=
"nut-tabs__titles-item__text"
>
{
pane
?.
selectedNode
?.
text
?
pane
.
selectedNode
.
text
:
'
请选择
'
}
</
span
>
<
span
className
=
"nut-tabs__titles-item__line"
/>
</
div
>
))
}
}
>
{
optiosData
.
map
((
pane
,
tabIndex
)
=>
(
<
TabPane
key
=
{
pane
.
paneKey
}
paneKey
=
{
pane
.
paneKey
}
>
<
div
className
=
{
classesPane
}
>
{
pane
.
nodes
&&
pane
.
nodes
.
map
((
item
:
any
,
index
:
number
)
=>
(
<
CascaderItem
key
=
{
index
}
{
...
props
}
data
=
{
item
}
checked
=
{
checkedIndexList
[
tabIndex
]
==
index
+
1
}
index
=
{
index
}
tabIndex
=
{
tabIndex
}
chooseItem
=
{
(
item
:
any
,
checked
:
boolean
,
index
:
number
,
tabIndex
:
number
)
=>
chooseItem
(
item
,
checked
,
index
,
tabIndex
)
}
/>
))
}
</
div
>
</
TabPane
>
))
}
</
Tabs
>
</
Popup
>
</
div
>
</>
)
}
Cascader
.
defaultProps
=
defaultProps
Cascader
.
displayName
=
'
NutCascader
'
src/packages/cascader/cascaderItem.tsx
0 → 100644
浏览文件 @
627d3609
import
React
,
{
FunctionComponent
,
useState
,
useEffect
}
from
'
react
'
import
Popup
from
'
@/packages/popup
'
import
{
Tabs
}
from
'
@/packages/tabs/tabs
'
import
{
TabPane
}
from
'
@/packages/tabpane/tabpane
'
import
{
Icon
}
from
'
@/packages/icon/icon
'
import
classNames
from
'
classnames
'
import
bem
from
'
@/utils/bem
'
export
interface
CascaderItemProps
{
data
:
Object
index
:
number
tabIndex
:
number
checked
:
boolean
options
:
[]
chooseItem
:
(
data
:
any
,
checked
:
boolean
,
index
:
number
,
tabIndex
:
number
)
=>
void
}
const
defaultProps
=
{
data
:
{},
index
:
0
,
tabIndex
:
0
,
checked
:
false
,
options
:
[],
// chooseItem: () => {},
}
as
CascaderItemProps
export
const
CascaderItem
:
FunctionComponent
<
Partial
<
CascaderItemProps
>
&
React
.
HTMLAttributes
<
HTMLDivElement
>
>
=
(
props
)
=>
{
const
[
showBasic
,
setShowBasic
]
=
useState
(
false
)
const
[
tabvalue
,
setTabvalue
]
=
useState
(
'
c1
'
)
const
[
optiosData
,
setOptiosData
]
=
useState
([])
const
{
pane
,
data
,
index
,
tabIndex
,
checked
,
options
,
chooseItem
}
=
{
...
defaultProps
,
...
props
,
}
const
b
=
bem
(
'
cascader-item
'
)
const
classes
=
classNames
(
{
active
:
checked
,
},
b
(
''
)
)
const
classesTitle
=
classNames
({
[
`
${
b
(
''
)}
__title`
]:
true
,
})
useEffect
(()
=>
{
initData
()
},
[])
const
initData
=
()
=>
{
// options.forEach((item, index: number) => {
// item.title = item.text
// item.paneKey = 'c'+ (index + 1)
// optiosData.push(item)
// })
}
return
(
<>
<
div
className
=
{
classes
}
onClick
=
{
()
=>
{
chooseItem
(
data
,
checked
,
index
,
tabIndex
)
}
}
>
<
div
className
=
{
classesTitle
}
>
{
data
.
text
}
</
div
>
{
checked
?
<
Icon
className
=
{
`
${
checked
?
b
(
'
icon-check
'
)
:
''
}
`
}
name
=
"checklist"
/>
:
''
// <Icon v-if="node.loading" className="nut-cascader-item__icon-loading" name="loading" />
}
</
div
>
</>
)
}
// Cascader.defaultProps = defaultProps
CascaderItem
.
displayName
=
'
NutCascaderItem
'
src/packages/cascader/demo.tsx
0 → 100644
浏览文件 @
627d3609
/*
* @Author: your name
* @Date: 2021-12-23 11:15:50
* @LastEditTime: 2021-12-27 10:16:31
* @LastEditors: Please set LastEditors
* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
* @FilePath: /nutui-react/src/packages/calendar/demo.tsx
*/
import
React
,
{
useState
}
from
'
react
'
import
{
Cascader
}
from
'
./cascader
'
import
{
Cell
}
from
'
@/packages/cell/cell
'
import
{
Input
}
from
'
@/packages/input/input
'
const
CascaderDemo
=
()
=>
{
const
[
date
,
setDate
]
=
useState
(
''
)
const
[
dateWeek
,
setDateWeek
]
=
useState
(
''
)
const
[
isVisible
,
setIsVisible
]
=
useState
(
false
)
const
[
value
,
setValue
]
=
useState
([])
const
[
options
,
setOptions
]
=
useState
([
{
value
:
'
浙江
'
,
text
:
'
浙江
'
,
children
:
[
{
value
:
'
杭州
'
,
text
:
'
杭州
'
,
disabled
:
true
,
children
:
[
{
value
:
'
西湖区
'
,
text
:
'
西湖区
'
},
{
value
:
'
余杭区
'
,
text
:
'
余杭区
'
},
],
},
{
value
:
'
温州
'
,
text
:
'
温州
'
,
children
:
[
{
value
:
'
鹿城区
'
,
text
:
'
鹿城区
'
},
{
value
:
'
瓯海区
'
,
text
:
'
瓯海区
'
},
],
},
],
},
{
value
:
'
湖南
'
,
text
:
'
湖南
'
,
disabled
:
true
,
children
:
[
{
value
:
'
长沙
'
,
text
:
'
长沙
'
,
disabled
:
true
,
children
:
[
{
value
:
'
西湖区
'
,
text
:
'
西湖区
'
},
{
value
:
'
余杭区
'
,
text
:
'
余杭区
'
},
],
},
{
value
:
'
温州
'
,
text
:
'
温州
'
,
children
:
[
{
value
:
'
鹿城区
'
,
text
:
'
鹿城区
'
},
{
value
:
'
瓯海区
'
,
text
:
'
瓯海区
'
},
],
},
],
},
{
value
:
'
福建
'
,
text
:
'
福建
'
,
children
:
[
{
value
:
'
福州
'
,
text
:
'
福州
'
,
children
:
[
{
value
:
'
鼓楼区
'
,
text
:
'
鼓楼区
'
},
{
value
:
'
台江区
'
,
text
:
'
台江区
'
},
],
},
],
},
])
const
openSwitch
=
()
=>
{
setIsVisible
(
true
)
}
const
closeSwitch
=
()
=>
{
setIsVisible
(
false
)
}
const
change
=
(
value
)
=>
{
// console.log('value1', ...value)
// console.log('value2', value)
setValue
(
value
)
}
return
(
<>
<
div
className
=
"demo"
>
<
h2
>
基础用法
</
h2
>
<
Cell
title
=
"选择地址"
desc
=
{
value
?
value
:
'
请选择
'
}
onClick
=
{
openSwitch
}
></
Cell
>
<
Cascader
visible
=
{
isVisible
}
value
=
{
value
}
onClose
=
{
closeSwitch
}
title
=
"地址选择"
options
=
{
options
}
onChange
=
{
change
}
/>
</
div
>
</>
)
}
export
default
CascaderDemo
src/packages/cascader/doc.md
0 → 100644
浏览文件 @
627d3609
# Cascader 级联选择
### 介绍
级联选择器,用于多层级数据的选择,典型场景为省市区选择。
### 安装
```
js
import
{
Cascader
,
Tabs
,
TabPane
}
from
'
@nutui/nutui-react
'
;
const
app
=
createApp
();
app
.
use
(
Cascader
)
.
use
(
Tabs
)
.
use
(
TabPane
);
```
### 基础用法
传入
`options`
列表。
:::demo
```
jsx
import
React
from
"
react
"
;
import
{
Collapse
,
CollapseItem
}
from
'
@nutui/nutui-react
'
;
const
App
=
()
=>
{
return
(
<>
<
Cell
title
=
"选择地址"
desc
=
{
value
?
value
:
'
请选择
'
}
onClick
=
{
openSwitch
}
>
</
Cell
>
<
Cascader
visible
=
{
isVisible
}
value
=
{
value
}
onClose
=
{
closeSwitch
}
title
=
"地址选择"
options
=
{
options
}
onChange
=
{
change
}
/>
</>
);
};
export
default
App
;
```
:::
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| ------------- | ----------------------------------------------------- | -------- | ------ |
| v-model | 选中值,双向绑定 | Array | - |
| options | 级联数据 | Array | - |
| lazy | 是否开启动态加载 | Boolean | - |
| lazy-load | 动态加载回调,开启动态加载时生效 | Function | - |
| value-key | 自定义
`options`
结构中
`value`
的字段 | String | - |
| text-key | 自定义
`options`
结构中
`text`
的字段 | String | - |
| children-key | 自定义
`options`
结构中
`children`
的字段 | String | - |
| convert-config | 当options为可转换为树形结构的扁平结构时,配置转换规则 | Object | - |
| title | 标题 | String | '' |
| close-icon-position | 取消按钮位置,继承 Popup 组件 | String | "top-right" |
| close-icon | 自定义关闭按钮,继承 Popup 组件 | String | "close" |
| closeable | 是否显示关闭按钮,继承 Popup 组件 | Boolean | true |
### Events
| 事件名 | 说明 | 回调参数 |
| ---------- | ---------------- | ------------------ |
| change | 选中值改变时触发 | (value, pathNodes) |
| pathChange | 选中项改变时触发 | (pathNodes) |
src/packages/cascader/index.ts
0 → 100644
浏览文件 @
627d3609
import
{
Cascader
}
from
'
./cascader
'
export
default
Cascader
src/packages/collapse/collapse.tsx
浏览文件 @
627d3609
...
...
@@ -51,11 +51,14 @@ export const Collapse: FunctionComponent<Partial<CollapseProps>> = memo((props)
const
colBem
=
bem
(
'
collapse
'
)
useEffect
(()
=>
{
const
activeArr
=
handleActiveName
()
console
.
log
(
'
activeArr
'
,
activeArr
)
setDefaultOpenIndex
(
activeArr
)
},
[
activeName
])
const
onToggle
=
(
isOpen
:
boolean
,
name
:
string
)
=>
{
let
newOpenIndex
=
[...
defaultOpenIndex
]
console
.
log
(
'
newOpenIndex
'
,
newOpenIndex
)
console
.
log
(
'
isOpen
'
,
isOpen
,
name
)
if
(
isOpen
)
{
// 当前状态为true,则变为false,闭合
const
removeIndex
=
newOpenIndex
.
findIndex
((
value
)
=>
{
...
...
@@ -71,7 +74,7 @@ export const Collapse: FunctionComponent<Partial<CollapseProps>> = memo((props)
}
}
setDefaultOpenIndex
(
newOpenIndex
)
change
&&
change
(
!
isOpen
,
name
)
//
change && change(!isOpen, name)
}
return
(
<
div
className
=
{
colBem
()
}
>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录