未验证 提交 e781363b 编写于 作者: S shadow-Fiend 提交者: GitHub

feat: timeselect (#149)

上级 28a11074
......@@ -744,8 +744,38 @@
"sort": 6,
"show": true,
"author": "songsong"
},
{
"version": "1.0.0",
"name": "TimePannel",
"type": "component",
"cName": "配送时间",
"desc": "配送时间日期选择",
"sort": 7,
"show": false,
"author": "zhaoqian16"
},
{
"version": "1.0.0",
"name": "TimeDetail",
"type": "component",
"cName": "配送时间",
"desc": "配送时间上门时间选择",
"sort": 8,
"show": false,
"author": "zhaoqian16"
},
{
"version": "1.0.0",
"name": "TimeSelect",
"type": "component",
"cName": "配送时间",
"desc": "用于配送时间选择",
"sort": 20,
"show": true,
"author": "zhaoqian16"
}
]
}
]
}
}
\ No newline at end of file
import React from 'react'
import { TimeDetail } from './timedetail'
const TimeDetailDemo = () => {
return (
<>
<div className="demo">
<h2>基础用法</h2>
<TimeDetail></TimeDetail>
</div>
</>
)
}
export default TimeDetailDemo
# TimeDetail组件
### 介绍
基于 xxxxxxx
### 安装
## 代码演示
### 基础用法1
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| name | 图标名称或图片链接 | String | - |
| color | 图标颜色 | String | - |
| size | 图标大小,如 '20px' '2em' '2rem' | String | - |
| class-prefix | 类名前缀,用于使用自定义图标 | String | 'nutui-iconfont' |
| tag | HTML 标签 | String | 'i' |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| click | 点击图标时触发 | event: Event |
import { TimeDetail, TimeType } from './timedetail'
export default TimeDetail
export type { TimeType }
.nut-timedetail {
display: flex;
align-content: flex-start;
flex-wrap: wrap;
padding: 0 0 50px 13px;
&__item {
width: 100px;
height: 50px;
line-height: 50px;
text-align: center;
margin-right: 10px;
margin-bottom: 10px;
background-color: #f6f7f9;
border-radius: 5px;
color: #333;
font-size: 14px;
border: 1px solid transparent;
font-weight: 700;
}
&__item-active {
background-color: #fa2c1926;
border: 1px solid #fa2c19;
color: #fa2c19;
}
}
import React, { FunctionComponent, useEffect, useState } from 'react'
import './timedetail.scss'
import bem from '@/utils/bem'
import { useConfig } from '@/packages/configprovider'
export interface TimeType {
key?: string | number
list: string[]
}
export interface TimeDetailProps {
className?: string
currentKey: string | number
currentTime: TimeType[]
times: TimeType[]
select: (time: string) => void
}
const defaultProps = {
className: '',
currentKey: 0,
currentTime: [] as TimeType[],
times: [],
select: () => null,
} as TimeDetailProps
export const TimeDetail: FunctionComponent<
Partial<TimeDetailProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { locale } = useConfig()
const { children, times, className, currentKey, currentTime, select } = {
...defaultProps,
...props,
}
const b = bem('timedetail')
const [renderData, setRenderData] = useState<string[]>([])
useEffect(() => {
const currentData = times.find(
(timesItem: TimeType) => String(timesItem.key) === String(currentKey)
)
const renderData = currentData ? currentData.list : []
// 根据选中的日期回显当前日期可配送的时间
setRenderData(renderData)
}, [times, currentKey])
// 选中时间的回调
const handleTime = (time: string) => {
select(time)
}
// 选中的配送时间增加 active 类名
const getDetailClass = (item: string): string => {
let initClass = 'nut-timedetail__item'
const curTimeData = currentTime.find(
(item: TimeType) => String(item.key) === String(currentKey)
)
if (curTimeData && curTimeData.list && curTimeData.list.includes(item)) {
initClass += ' nut-timedetail__item-active'
}
return initClass
}
return (
<div className={`${b()} ${className || ''}`}>
{renderData.map((item: string, index: number) => (
<span className={getDetailClass(item)} key={item} onClick={() => handleTime(item)}>
{item}
</span>
))}
</div>
)
}
TimeDetail.defaultProps = defaultProps
TimeDetail.displayName = 'NutTimeDetail'
import React from 'react'
import { TimePannel } from './timepannel'
const TimePannelDemo = () => {
return (
<>
<div className="demo">
<h2>基础用法</h2>
<TimePannel></TimePannel>
</div>
</>
)
}
export default TimePannelDemo
# TimePannel组件
### 介绍
基于 xxxxxxx
### 安装
## 代码演示
### 基础用法1
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| name | 图标名称或图片链接 | String | - |
| color | 图标颜色 | String | - |
| size | 图标大小,如 '20px' '2em' '2rem' | String | - |
| class-prefix | 类名前缀,用于使用自定义图标 | String | 'nutui-iconfont' |
| tag | HTML 标签 | String | 'i' |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| click | 点击图标时触发 | event: Event |
import { TimePannel } from './timepannel'
export default TimePannel
.nut-timepannel {
height: 40px;
line-height: 40px;
text-align: center;
color: #666;
font-size: 14px;
}
import React, { FunctionComponent } from 'react'
import './timepannel.scss'
import bem from '@/utils/bem'
import { useConfig } from '@/packages/configprovider'
export interface TimePannelProps {
date: string
curKey: string | number
className?: string
change: (curKey: string | number) => void
}
const defaultProps = {
className: '',
date: '',
curKey: 0,
} as TimePannelProps
export const TimePannel: FunctionComponent<
Partial<TimePannelProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { locale } = useConfig()
const { children, className, date, curKey, change } = { ...defaultProps, ...props }
const b = bem('timepannel')
return (
<div className={`${b()} ${className || ''}`} onClick={() => change(curKey)}>
{date}
</div>
)
}
TimePannel.defaultProps = defaultProps
TimePannel.displayName = 'NutTimePannel'
import React, { useState } from 'react'
import { TimeSelect } from './timeselect'
import { Cell } from '../cell/cell'
import Toast from '../toast'
const TimeSelectDemo = () => {
const [visible1, SetVisible1] = useState(false)
const currentKey = 0
const dates = [
{
'pannel-key': '0',
date: '5月20日(今天)',
},
{
'pannel-key': '1',
date: '5月21日(星期三)',
},
]
const times = [
{
key: '0',
list: ['9:00-10:00', '10:00-11:00', '11:00-12:00'],
},
{
key: '1',
list: ['9:00-10:00', '10:00-11:00'],
},
]
const handleClick = () => {
SetVisible1(true)
}
// 点击弹层 X 或者弹层外区域触发事件
const handleSelect = (selectTimeData) => {
SetVisible1(false)
Toast.text(`您选择了: ${JSON.stringify(selectTimeData)}`)
}
// 选择日期触发回调事件
const handlePannelChange = (pannelKey, selectTimeData) => {
console.log('pannelKey, selectTimeData: ', pannelKey, selectTimeData)
}
// 选择配送时间触发回调事件
const handleTimeChange = (time, selectTimeData) => {
console.log('time, selectTimeData: ', time, selectTimeData)
}
return (
<>
<div className="demo">
<h2>用法</h2>
<Cell title="请选择配送时间" click={handleClick} />
<TimeSelect
visible={visible1}
height="50%"
title="取件时间 1"
multiple
currentKey={currentKey}
dates={dates}
times={times}
select={handleSelect}
pannelChange={handlePannelChange}
timeChange={handleTimeChange}
/>
</div>
</>
)
}
export default TimeSelectDemo
# TimeSelect 配送时间
### 介绍
用于配送时间选择
### 安装
``` javascript
import { TimeSelect } from '@nutui/nutui-react';
```
### 基本用法
:::demo
```tsx
import React, { useState } from 'react'
import { TimeSelect, Cell, Toast } from '@nutui/nutui-react'
const TimeSelectDemo = () => {
const [visible1, SetVisible1] = useState(false)
const currentKey = 0
const dates = [
{
'pannel-key': '0',
date: '5月20日(今天)',
},
{
'pannel-key': '1',
date: '5月21日(星期三)',
},
]
const times = [
{
key: '0',
list: ['9:00-10:00', '10:00-11:00', '11:00-12:00'],
},
{
key: '1',
list: ['9:00-10:00', '10:00-11:00'],
},
]
const handleClick = () => {
SetVisible1(true)
}
const handleSelect = (selectTimeData) => {
SetVisible1(false)
Toast.text(`您选择了: ${JSON.stringify(selectTimeData)}`)
}
const handlePannelChange = (pannelKey, selectTimeData) => {
console.log('pannelKey, selectTimeData: ', pannelKey, selectTimeData)
}
const handleTimeChange = (time, selectTimeData) => {
console.log('time, selectTimeData: ', time, selectTimeData)
}
return (
<>
<div className="demo">
<h2>用法</h2>
<Cell title="请选择配送时间" click={handleClick} />
<TimeSelect
visible={visible1}
height="50%"
title="取件时间 1"
multiple
currentKey={currentKey}
dates={dates}
times={times}
select={handleSelect}
pannelChange={handlePannelChange}
timeChange={handleTimeChange}
/>
</div>
</>
)
}
export default TimeSelectDemo
```
:::
## API
### TimeSelect Prop
| 字段 | 说明 | 是否必传 | 类型 | 默认值 |
|------------------------|----------------------------------------------------|--------|------------|---------|
| visible | 是否显示弹层 | 是 | Boolean | `false`
| height | 弹层的高度 | 否 | String | `20%`
| title | 弹层标题 | 否 | String | `取件时间`
| multiple | 是否选择多个日期时间 | 否 | String | `false`
| currentKey | 唯一标识 | 否 | String、Number | `0` |
| dates | 选择日期面板的数据 | 是 | [] | `` |
| times | 选择时间面板的数据 | 是 | [] | `` |
### dates
| 字段 | 说明 | 类型 | 默认值 |
|------------------------|----------------------------------------------------------------|---------|------|
| date | 显示的名称 | String | ``
| pannel-key | 唯一标识,和 currentKey 一起标识当前选择的天 | Number、String | `0`
### times
| 字段 | 说明 | 类型 | 默认值 |
|------------------------|----------------------------------------------------------------|---------|------|
| key | 唯一标识,和 pannel-key、currentKey 一起标识当前选择的天 | Array | `[]`
| list | 可选时间列表 | Array | `[]`
### TimeSelect Event
| 名称 | 说明 | 是否必传 | 回调参数 |
|-------|----------|--------|-------------|
| select | 关闭遮罩之后的回调 | 是 | `list: []` |
| pannelChange | 点击左栏日期回调,内部通过 setCurrentKey、setCurrentTime 更新数据 | 否 | `pannelKey: string 、number, list: []` |
| timeChange | 点击时间回调,内部通过 setCurrentKey、setCurrentTime 更新数据 | 否 | `time: string, list: []` |
# TimeSelect 配送时间
### 介绍
用于配送时间选择
### 安装
``` javascript
import { TimeSelect } from '@nutui/nutui-react';
```
### 基本用法
:::demo
```tsx
import React, { useState } from 'react'
import { TimeSelect, Cell, Toast } from '@nutui/nutui-react'
const TimeSelectDemo = () => {
const [visible1, SetVisible1] = useState(false)
const currentKey = 0
const dates = [
{
'pannel-key': '0',
date: '5月20日(今天)',
},
{
'pannel-key': '1',
date: '5月21日(星期三)',
},
]
const times = [
{
key: '0',
list: ['9:00-10:00', '10:00-11:00', '11:00-12:00'],
},
{
key: '1',
list: ['9:00-10:00', '10:00-11:00'],
},
]
const handleClick = () => {
SetVisible1(true)
}
const handleSelect = (selectTimeData) => {
SetVisible1(false)
Toast.text(`您选择了: ${JSON.stringify(selectTimeData)}`)
}
const handlePannelChange = (pannelKey, selectTimeData) => {
console.log('pannelKey, selectTimeData: ', pannelKey, selectTimeData)
}
const handleTimeChange = (time, selectTimeData) => {
console.log('time, selectTimeData: ', time, selectTimeData)
}
return (
<>
<div className="demo">
<h2>用法</h2>
<Cell title="请选择配送时间" click={handleClick} />
<TimeSelect
visible={visible1}
height="50%"
title="取件时间 1"
multiple
currentKey={currentKey}
dates={dates}
times={times}
select={handleSelect}
pannelChange={handlePannelChange}
timeChange={handleTimeChange}
/>
</div>
</>
)
}
export default TimeSelectDemo
```
:::
## API
### TimeSelect Prop
| 字段 | 说明 | 是否必传 | 类型 | 默认值 |
|------------------------|----------------------------------------------------|--------|------------|---------|
| visible | 是否显示弹层 | 是 | Boolean | `false`
| height | 弹层的高度 | 否 | String | `20%`
| title | 弹层标题 | 否 | String | `取件时间`
| multiple | 是否选择多个日期时间 | 否 | String | `false`
| currentKey | 唯一标识 | 否 | String、Number | `0` |
| dates | 选择日期面板的数据 | 是 | [] | `` |
| times | 选择时间面板的数据 | 是 | [] | `` |
### dates
| 字段 | 说明 | 类型 | 默认值 |
|------------------------|----------------------------------------------------------------|---------|------|
| date | 显示的名称 | String | ``
| pannel-key | 唯一标识,和 currentKey 一起标识当前选择的天 | Number、String | `0`
### times
| 字段 | 说明 | 类型 | 默认值 |
|------------------------|----------------------------------------------------------------|---------|------|
| key | 唯一标识,和 pannel-key、currentKey 一起标识当前选择的天 | Array | `[]`
| list | 可选时间列表 | Array | `[]`
### TimeSelect Event
| 名称 | 说明 | 是否必传 | 回调参数 |
|-------|----------|--------|-------------|
| select | 关闭遮罩之后的回调 | 是 | `list: []` |
| pannelChange | 点击左栏日期回调,内部通过 setCurrentKey、setCurrentTime 更新数据 | 否 | `pannelKey: string 、number, list: []` |
| timeChange | 点击时间回调,内部通过 setCurrentKey、setCurrentTime 更新数据 | 否 | `time: string, list: []` |
# TimeSelect 配送时间
### 介绍
用于配送时间选择
### 安装
``` javascript
import { TimeSelect } from '@nutui/nutui-react';
```
### 基本用法
:::demo
```tsx
import React, { useState } from 'react'
import { TimeSelect, Cell, Toast } from '@nutui/nutui-react'
const TimeSelectDemo = () => {
const [visible1, SetVisible1] = useState(false)
const currentKey = 0
const dates = [
{
'pannel-key': '0',
date: '5月20日(今天)',
},
{
'pannel-key': '1',
date: '5月21日(星期三)',
},
]
const times = [
{
key: '0',
list: ['9:00-10:00', '10:00-11:00', '11:00-12:00'],
},
{
key: '1',
list: ['9:00-10:00', '10:00-11:00'],
},
]
const handleClick = () => {
SetVisible1(true)
}
const handleSelect = (selectTimeData) => {
SetVisible1(false)
Toast.text(`您选择了: ${JSON.stringify(selectTimeData)}`)
}
const handlePannelChange = (pannelKey, selectTimeData) => {
console.log('pannelKey, selectTimeData: ', pannelKey, selectTimeData)
}
const handleTimeChange = (time, selectTimeData) => {
console.log('time, selectTimeData: ', time, selectTimeData)
}
return (
<>
<div className="demo">
<h2>用法</h2>
<Cell title="请选择配送时间" click={handleClick} />
<TimeSelect
visible={visible1}
height="50%"
title="取件时间 1"
multiple
currentKey={currentKey}
dates={dates}
times={times}
select={handleSelect}
pannelChange={handlePannelChange}
timeChange={handleTimeChange}
/>
</div>
</>
)
}
export default TimeSelectDemo
```
:::
## API
### TimeSelect Prop
| 字段 | 说明 | 是否必传 | 类型 | 默认值 |
|------------------------|----------------------------------------------------|--------|------------|---------|
| visible | 是否显示弹层 | 是 | Boolean | `false`
| height | 弹层的高度 | 否 | String | `20%`
| title | 弹层标题 | 否 | String | `取件时间`
| multiple | 是否选择多个日期时间 | 否 | String | `false`
| currentKey | 唯一标识 | 否 | String、Number | `0` |
| dates | 选择日期面板的数据 | 是 | [] | `` |
| times | 选择时间面板的数据 | 是 | [] | `` |
### dates
| 字段 | 说明 | 类型 | 默认值 |
|------------------------|----------------------------------------------------------------|---------|------|
| date | 显示的名称 | String | ``
| pannel-key | 唯一标识,和 currentKey 一起标识当前选择的天 | Number、String | `0`
### times
| 字段 | 说明 | 类型 | 默认值 |
|------------------------|----------------------------------------------------------------|---------|------|
| key | 唯一标识,和 pannel-key、currentKey 一起标识当前选择的天 | Array | `[]`
| list | 可选时间列表 | Array | `[]`
### TimeSelect Event
| 名称 | 说明 | 是否必传 | 回调参数 |
|-------|----------|--------|-------------|
| select | 关闭遮罩之后的回调 | 是 | `list: []` |
| pannelChange | 点击左栏日期回调,内部通过 setCurrentKey、setCurrentTime 更新数据 | 否 | `pannelKey: string 、number, list: []` |
| timeChange | 点击时间回调,内部通过 setCurrentKey、setCurrentTime 更新数据 | 否 | `time: string, list: []` |
import { TimeSelect } from './timeselect'
export default TimeSelect
.nut-timeselect {
display: flex;
flex-direction: column;
height: 100%;
&__title {
height: 50px;
line-height: 50px;
margin-bottom: 10px;
font-size: 14px;
color: #1a1a1a;
text-align: center;
}
&__content {
display: flex;
flex: 1;
}
&__content-left {
width: 140px;
min-width: 140px;
height: 100%;
overflow: auto;
background-color: #f6f7f9;
}
.nut-timepannel-active {
background-color: #fff;
color: #333;
font-weight: 700;
}
}
import React, { FunctionComponent, useEffect, useState } from 'react'
import './timeselect.scss'
import Popup from '@/packages/popup'
import TimePannel from '../timepannel'
import TimeDetail from '../timedetail'
import bem from '@/utils/bem'
import { useConfig } from '@/packages/configprovider'
import { TimeType } from '../timedetail/timedetail'
export interface DateType {
'pannel-key'?: string | number
date: string
}
export interface TimeSelectProps {
className?: string
visible?: boolean
height?: string
multiple?: boolean
title?: string
currentKey: string | number
currentTime: TimeType[]
dates: DateType[]
times: TimeType[]
select?: (selectTimeData: TimeType[]) => void
pannelChange?: (pannelKey: string | number, selectTimeData: TimeType[]) => void
timeChange?: (time: string, selectTimeData: TimeType[]) => void
}
const defaultProps = {
className: '',
visible: false,
height: '20%',
multiple: false,
currentKey: 0,
currentTime: [],
title: '取件时间',
dates: [],
times: [],
} as TimeSelectProps
export const TimeSelect: FunctionComponent<
Partial<TimeSelectProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { locale } = useConfig()
const {
children,
visible,
className,
height,
title,
currentKey,
currentTime,
dates,
times,
multiple,
select,
pannelChange,
timeChange,
} = {
...defaultProps,
...props,
}
const [activeKey, setActiveKey] = useState<string | number>(currentKey)
const [activeTime, setActiveTime] = useState<TimeType[]>(
currentTime.length
? currentTime
: [
{
key: currentKey,
list: [],
},
]
)
const [popVisible, setPopVisible] = useState(visible)
const popStyle = {
width: '100%',
height,
}
const b = bem('timeselect')
// popup 的关闭回调, 执行 select 函数更改外部 visible
const closeFun = () => {
select && select(activeTime)
}
// 选择配送时间回调
const handleSelectTime = (time: string) => {
let curTimeData = {} as TimeType
let curIndex: string | number = -1
for (let i = 0; i < activeTime.length; i++) {
if (String(activeTime[i].key) === String(activeKey)) {
curTimeData = activeTime[i]
curIndex = i
break
}
}
const curTimeIndex = curTimeData.list.findIndex((item: string) => String(item) === String(time))
if (curTimeIndex === -1) {
curTimeData.list.push(time)
} else {
curTimeData.list.splice(curTimeIndex, 1)
}
const resultTimeData = [...activeTime]
resultTimeData.splice(curIndex, 1, curTimeData)
setActiveTime(resultTimeData)
timeChange && timeChange(time, resultTimeData)
}
// 选择日期的回调
const handleChange = (pannelKey: string | number) => {
const resultTimeData: TimeType[] = [...activeTime]
if (String(pannelKey) !== String(activeKey)) {
setActiveKey?.(pannelKey)
if (multiple) {
const curTimeDataIndex = activeTime.findIndex(
(item: TimeType) => String(item.key) === String(pannelKey)
)
if (curTimeDataIndex === -1) {
resultTimeData.push({
key: pannelKey,
list: [],
} as TimeType)
setActiveTime(resultTimeData)
}
} else {
setActiveTime([
{
key: pannelKey,
list: [],
} as TimeType,
])
}
}
pannelChange && pannelChange(pannelKey, resultTimeData)
}
// 选中的日期增加 active 类名
const getTimePannelClass = (dataItem: DateType) => {
if (String(dataItem['pannel-key']) === String(activeKey)) {
return 'nut-timepannel-active'
}
return ''
}
// 根据外部传入 visible 进行组件的显隐展示
useEffect(() => {
setPopVisible(visible)
}, [visible])
return (
<>
<Popup
closeable
round
visible={popVisible}
position="bottom"
style={popStyle}
onClose={() => {
closeFun()
}}
>
<div className={`${b()} ${className || ''}`}>
<div className="nut-timeselect__title">{title}</div>
<div className="nut-timeselect__content">
<div className="nut-timeselect__content-left">
{dates.map((dataItem: DateType, index: number) => (
<TimePannel
date={dataItem.date}
className={getTimePannelClass(dataItem)}
key={String(dataItem['pannel-key'] || index)}
curKey={String(dataItem['pannel-key'] || index)}
change={handleChange}
/>
))}
</div>
<TimeDetail
times={times}
currentKey={String(activeKey)}
currentTime={activeTime}
select={handleSelectTime}
/>
</div>
</div>
</Popup>
</>
)
}
TimeSelect.defaultProps = defaultProps
TimeSelect.displayName = 'NutTimeSelect'
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册