Merge branch 'feat/selectTreev2' into develop

This commit is contained in:
NICE CODE BY DEV 2024-06-05 18:14:30 +08:00
commit d7a29f51b7
22 changed files with 691 additions and 28 deletions

View File

@ -1,12 +0,0 @@
import React from 'react'
import { Button } from '@zhst/meta'
import { useThrottleFn } from '@zhst/hooks'
export default () => {
const { run } = useThrottleFn(() => console.log('123'))
return (
<Button onClick={() => run()} ></Button>
)
}

View File

@ -3,7 +3,7 @@ import { IRecord } from '../../../WarningRecordCard';
import { ViewLargerImageModalRef } from '../../../ViewLargerImageModal';
import WarningRecordCard from '../../../WarningRecordCard';
import ViewLargerImageModal from '../../../ViewLargerImageModal';
import { Empty, Space, Spin } from 'antd';
import { Empty, Space, Spin } from '@zhst/meta';
import { pxToRem } from '@zhst/func'
import { LoadingOutlined } from '@ant-design/icons';
import "./index.less"

View File

@ -1,7 +1,7 @@
import React from 'react';
import VideoPlayerCard from '../../../VideoPlayerCard';
import { VideoPlayerCardProps } from '../../../VideoPlayerCard';
import { Row, Col, Segmented, theme } from 'antd';
import { Row, Col, Segmented, theme } from '@zhst/meta';
import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
import { pxToRem } from '@zhst/func'
import './index.less'

View File

@ -1,5 +1,5 @@
import React, { useImperativeHandle, useRef, useState, forwardRef, useContext } from 'react';
import { Modal, ModalProps, Space, SpaceProps, theme } from 'antd';
import { Modal, ModalProps, Space, SpaceProps, theme } from '@zhst/meta';
import { DownloadOutlined } from '@ant-design/icons';
import { ConfigProvider, CropperImage } from '@zhst/meta';

View File

@ -1,4 +1,4 @@
import { Card, Space, Divider, CardProps, theme } from 'antd';
import { Card, Space, Divider, CardProps, theme } from '@zhst/meta';
import React, { useContext } from 'react';
import dayjs from 'dayjs';
import { ConfigProvider, CropperImage } from '@zhst/meta';

View File

@ -1,9 +1,10 @@
import React, { FC, useState, useContext, ReactNode } from 'react';
import { ModalFormProps } from '@ant-design/pro-components'
import { Input, Dropdown } from 'antd'
import {
ButtonProps,
ConfigProvider,
Input,
Dropdown,
Tooltip,
Button,
Divider,

View File

@ -21,6 +21,8 @@ export type { ViewLargerImageModalRef, ViewLargerImageModalProps } from './ViewL
export { default as ViewLargerImageModal, useViewLargerImageModal } from './ViewLargerImageModal'
export type { VideoPlayerCardProps } from './VideoPlayerCard'
export { default as VideoPlayerCard } from './VideoPlayerCard'
export type { TreePanelProps, TreePanelRefProps } from './treePanel'
export { default as TreePanel } from './treePanel'
export { default as RealTimeMonitor } from './RealTimeMonitor'
export { default as InfiniteList } from './infiniteList'
export type { InfiniteListProps, InfiniteListRefProps } from './infiniteList'

View File

@ -3,10 +3,9 @@
*/
import React, { forwardRef, ReactNode, useContext, useEffect, useImperativeHandle, useRef } from 'react'
import { ConfigProvider } from '@zhst/meta';
import { ConfigProvider, Spin, SpinProps } from '@zhst/meta';
import classNames from 'classnames';
import InfiniteScroll from 'react-infinite-scroll-component';
import { Spin, SpinProps } from 'antd';
import { useSize } from '@zhst/hooks';
import './index.less'

View File

@ -1,6 +1,5 @@
import React, { forwardRef, useContext, useImperativeHandle, useRef } from 'react';
import { Button, Modal, ModalProps, Select, SelectProps, Space, theme } from 'antd';
import { ConfigProvider, CropperImage, Scanner, CropperImageProps, CropperImageRefProps } from '@zhst/meta'
import { ConfigProvider, CropperImage, Scanner, CropperImageProps, CropperImageRefProps, Button, Modal, ModalProps, Select, SelectProps, Space, theme } from '@zhst/meta'
import { IconFont } from '@zhst/icon'
import classNames from 'classnames';
import './index.less'

View File

@ -1,4 +1,4 @@
import { TreeDataNode } from 'antd';
import { TreeDataNode } from '@zhst/meta';
import BoxTree from './boxTree';
export interface TreeData extends TreeDataNode {

View File

@ -0,0 +1,267 @@
import React, { FC, useContext, ReactNode } from 'react';
import {
ConfigProvider,
Input,
Dropdown,
Tooltip,
Button,
DataNode as TreeDataNode,
Tree as BoxTree,
TreeProps as BoxTreeProps,
TreeProps,
InputProps,
DropDownProps,
SelectProps,
Select
} from '@zhst/meta';
import { IconFont } from '@zhst/icon';
import classNames from 'classnames';
import './index.less'
interface IOption {
label: string
key: string
icon?: string | ReactNode
disabled?: boolean;
showTooltip?: boolean;
onClick?: () => void
className?: string;
dropdownConfig?: DropDownProps
}
interface ITag {
label: string
value: string
icon?: ReactNode
parentNode?: string
children?: ITag[]
}
export interface BoxPanelProps {
treeType?: 'directory' | 'normal'
searchInputProps?: InputProps
showOptions?: boolean
showSelectBar?: boolean // 显示搜索框
filterSelectProps?: SelectProps
onSelect?: SelectProps['onChange']
treeProps?: Partial<BoxTreeProps>
data: TreeDataNode[]
onSearch?: (e: any) => void
onItemCheck?: TreeProps['onCheck']
onItemSelect?: TreeProps['onSelect']
customImport?: ReactNode | string // 自定义搜索栏边上的过滤图标
extra?: ReactNode | string // 搜索栏下面的插槽
prefixCls?: string
showSearchBar?: boolean // 是否显示搜索栏
noFilter?: boolean // 是否显示搜索拓展 icon
filterList?: IOption[]
optionList?: IOption[]
showTagPanel?: boolean // 标签插槽
tagList?: ITag[] // 标签列表
tagExpandAll?: boolean // 展开所有
onTagCheck?: (value: string, tag: ITag) => void;
checkedTags?: string[]
onResetTags?: () => void
onTagExpand?: (e: any) => void
tagFootRender?: ReactNode
}
const { ConfigContext } = ConfigProvider
const { DirectoryTree } = BoxTree
const BoxPanel: FC<BoxPanelProps> = (props) => {
const {
treeType = 'directory',
searchInputProps,
showOptions = true,
showSelectBar,
filterSelectProps,
extra,
noFilter,
data = [],
treeProps,
onSelect,
onSearch,
onItemCheck,
onItemSelect,
showSearchBar = true,
optionList = [],
filterList = [],
showTagPanel,
tagList,
tagExpandAll,
onTagExpand,
checkedTags = [],
onTagCheck,
onResetTags,
tagFootRender,
prefixCls: customizePrefixCls,
customImport: customFilter
} = props
const { getPrefixCls } = useContext(ConfigContext);
const componentName = getPrefixCls('biz-tree-panel', customizePrefixCls);
const CurrentTree = treeType === 'directory' ? DirectoryTree : BoxTree
/**
* filter
* @param _list
* @returns
*/
const initFilter = (_list?: BoxPanelProps['filterList']) => {
const WithDropdown = (dom: ReactNode, isShow?: boolean, _config?: DropDownProps) => {
if (!isShow) {
return dom
}
return (
<Dropdown placement="bottomLeft" arrow {..._config}>
{dom}
</Dropdown>
)
}
return _list?.map(item => (
<Tooltip
title={item.label}
open={item.showTooltip}
>
{WithDropdown(
<Button className={classNames(componentName + '-search-btns-btn')} type="text" onClick={item.onClick} icon={item.icon} />,
item.type === 'dropdown',
item.dropdownConfig
)}
</Tooltip>
))
}
/**
* filter
* @param _list
* @returns
*/
const initOptions = (_list?: BoxPanelProps['optionList']) => {
return _list?.map((item, idx) => (
<>
{/* @ts-ignore */}
<div key={idx} className={classNames(componentName + '-btns-btn')}>
{item.icon}
<span className={classNames(componentName + '-btns-btn-label', item.className)}>{item.label}</span>
</div>
{idx % 2 !== 0 && (<br/>)}
</>
))
}
/**
*
* @param _tagList
* @param sort
* @returns
*/
const initTagPanel = (_tagList: BoxPanelProps['tagList'], sort?: boolean) => {
// 正常标签渲染
const commonTag = (_tagProps: ITag) => (
<span
className={classNames(
componentName + '-tags-tag_common',
{[componentName + '-tags-tag_checked']: checkedTags.includes(_tagProps.value)}
)}
key={_tagProps.value}
onClick={() => onTagCheck?.(_tagProps.value, _tagProps)}
>{_tagProps.label}</span>
)
// 包装父级标签
const _withFather = (tag: ITag) => (
<div key={tag.value}>
<span className={classNames(componentName + '-tags-tag')}>{tag.label}</span>
{tag.children?.map?.(_tag => commonTag(_tag))}
</div>
)
return _tagList?.map(tag => {
if (tag.children?.length && sort) {
return _withFather(tag)
} else {
return commonTag(tag)
}
})
}
return (
<div className={componentName}>
{/* 搜索栏 */}
{showSearchBar && (
<div className={classNames(componentName + '-search')}>
<Input
className={classNames(componentName + '-search-input')}
onChange={(e) => onSearch?.(e)}
placeholder='请输入盒子名称'
{...searchInputProps}
/>
</div>
)}
{/* 搜索栏 */}
{showSelectBar && (
<div className={classNames(componentName + '-search')}>
<Select
className={classNames(componentName + '-search-input')}
onChange={onSelect}
{...filterSelectProps}
/>
{customFilter || (!noFilter && (
<div
className={classNames(componentName + '-search-btns')}
>
{/* @ts-ignore */}
{initFilter(filterList)}
</div>
))}
</div>
)}
{showTagPanel && (
<div className={classNames(componentName + '-tags')}>
<div className={classNames(componentName + '-tags-title')}>
<div
className={classNames(
componentName + '-tags-tag_option',
)}
>
{tagExpandAll && (
<span
className={classNames(componentName + '-tags-tag_option-btn')}
onClick={onResetTags}
></span>
)}
<span
className={classNames(componentName + '-tags-tag_option-btn')}
onClick={onTagExpand}
>{tagExpandAll ? '收起' : '更多'}<IconFont icon={tagExpandAll ? 'icon-shangjiantou' : 'icon-xiajiantou'} /></span>
</div>
</div>
{initTagPanel(tagList, tagExpandAll)}
{tagFootRender}
</div>
)}
{/* 默认操作按钮 */}
{showOptions && (
<div className={classNames(componentName + '-btns')}>
{initOptions(optionList)}
</div>
)}
{extra}
<CurrentTree
className={classNames(componentName + '-tree')}
treeData={data}
showIcon={false}
blockNode
onSelect={onItemSelect}
onCheck={onItemCheck}
{...treeProps}
/>
</div>
)
}
export default BoxPanel

View File

View File

@ -0,0 +1,135 @@
import React, { useState, useRef } from 'react';
import { TreePanel } from '@zhst/biz';
import { Badge, Checkbox } from '@zhst/meta'
import { treeData, boxDataSource } from './mock'
import { ImportOutlined, FolderAddOutlined, CloseCircleOutlined, FilterOutlined } from '@ant-design/icons';
const demo = () => {
const [checkedTags, setCheckedTags] = useState<string[]>([]);
const [tagExpandAll, setTagExpandAll] = useState(false);
const [showTagPanel, setShowTagPanel] = useState(true);
return (
<div style={{ padding: '12px', width: '240px', border: '1px solid #09f' }}>
<TreePanel
data={boxDataSource}
showTagPanel={showTagPanel}
tagExpandAll={tagExpandAll}
showSelectBar
onTagCheck={(value) => setCheckedTags(pre => {
if (pre.includes(value)) {
return pre.filter(item => item !== value)
} else {
return [...pre, value]
}
})}
onResetTags={() => setCheckedTags([])}
checkedTags={checkedTags}
filterList={[
{
label: '过滤',
key: 'multi',
icon: <FilterOutlined />,
type: 'dropdown',
showTooltip: false,
dropdownConfig: {
menu: {
// 自定义返回项
_internalRenderMenuItem: (originNode, menuItemProps, stateProps) => {
return (
<div>
{originNode}
</div>
)
},
selectable: true,
items: [
{
label: <p style={{ margin: '0', textAlign: 'center' }} ></p>,
key: 'all',
onClick: () => console.log('多选1')
},
{
icon: <Badge status="success" />,
label: '多选1',
key: 'multi1',
onClick: () => console.log('多选1')
},
{
label: '多选2',
icon: <Badge status='error' />,
key: 'multi2',
},
],
}
}
},
]}
extra={(
<div>
<span><Checkbox></Checkbox></span>
<a style={{ float: 'right', color: '#09f' }} ></a>
</div>
)}
optionList={[
{
label: '导入盒子',
key: 'import',
icon: <ImportOutlined />,
},
{
label: '新建组',
key: 'add',
icon: <FolderAddOutlined />,
},
{
label: '删除',
key: 'del',
icon: <CloseCircleOutlined />,
props: {
danger: true
}
},
]}
tagList={[
{
label: '标签组1',
value: '1',
children: [
{
label: '标签1-1',
value: '1-1',
},
{
label: '标签1-2',
value: '1-2',
}
]
},
{
label: '标签组2',
value: '2',
children: [
{
label: '标签2-1',
value: '2-1',
},
{
label: '标签2-2',
value: '2-2',
}
]
},
]
}
onTagExpand={() => {
setTagExpandAll(pre => !pre)
setCheckedTags([])
}}
/>
</div>
);
};
export default demo;

View File

@ -0,0 +1,54 @@
import { TreeData } from "@zhst/biz";
export const boxDataSource: TreeData[] = [
{
title: '全部盒子',
key: '0-0',
children: [
{
title: '盒子组1',
key: '0-0-0',
children: [
{
title: '摄像头1',
key: '0-0-0-0',
isCamera: true
},
{
title: '摄像头2',
key: '0-0-0-1',
isCamera: true
},
],
},
{
title: '盒子组2',
key: '0-0-1',
children: [
{
title: '摄像头4',
key: '0-0-1-0',
isCamera: true
}
],
},
],
},
];
export const treeData: TreeData[] = [
{ key: '0-1-0', title: '分组0-1-0', isLeaf: true, checkable: false },
{ key: '0-1-1', title: '分组0-1-1', isLeaf: true, checkable: false },
{ key: '0-1-2', title: '分组0-1-2', isLeaf: true, checkable: false },
{
key: '0-1-3',
title: '分组0-1-3',
isLeaf: false,
children: [
{ key: '0-1-3-1', title: '分组0-1-3-1', isLeaf: true, isCamera: true },
{ key: '0-1-3-2', title: '分组0-1-3-2', isLeaf: true, isCamera: true },
{ key: '0-1-3-3', title: '分组0-1-3-3', isLeaf: true, isCamera: true },
],
},
];

View File

View File

@ -0,0 +1,129 @@
.zhst-biz-tree-panel {
&-search {
margin-bottom: 16px;
display: flex;
align-items: center;
&-input {
flex: 1;
margin-right: 4px;
}
&-btns {
flex: none;
}
}
&-btns {
&-btn {
margin-right: 16px;
margin-bottom: 12px;
display: inline-block;
cursor: pointer;
font-size: 14px;
transition: .3s ease all;
&:hover {
opacity: 0.7;
}
&-label {
margin-left: 4px;
}
}
&-common {
flex: 1;
padding: 4px 8px;
}
&-import {
padding: 4px 8px;
}
}
&-tags {
margin-bottom: 12px;
position: relative;
height: 100%;
padding: 12px;
font-size: 0;
border-radius: 4px;
background-color: #f7f7f7;
&-title {
font-size: 12px;
color: #595959;
line-height: 20px;
font-weight: 500;
&::after {
position: absolute;
content: '';
clear: both;
}
}
&-tag {
margin-right: 8px;
font-weight: bold;
color: #595959;
font-size: 14px;
transition:.3s ease all;
&_common {
margin-top: 6px;
display: inline-block;
margin-right: 8px;
padding: 2px 8px;
font-size: 12px;
cursor: pointer;
color: #191919;
background-color: #EBEBEB;
transition: .3s ease all;
border-radius: 3px;
box-sizing: content-box;
&:hover {
color: #fff;
background-color: #09f;
opacity: 0.7;
}
}
&_checked {
color: #fff;
background-color: #09f;
}
&_fz12 {
font-size: 12px;
}
&_option {
float: right;
&-btn {
margin-left: 12px;
color: #09f;
cursor: pointer;
transition: .3s ease all;
&:hover {
opacity: 0.7;
}
}
&::after {
position: absolute;
content: '';
clear: both;
}
}
&_absolute {
position: absolute;
top: 12px;
right: 0;
}
}
}
&-tree {
padding: 8px 0;
}
}

View File

@ -0,0 +1,81 @@
---
category: Components
title: TreePanel 树面板
toc: content
demo:
cols: 2
group:
title: 数据展示
---
## 代码演示
<code src="./demo/basic.tsx">基本用法</code>
## API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| treeType | 树的类型 | 'directory' 'normal' | directory | --- |
| searchInputProps | antd-inputProps | --- | --- | --- |
| showOptions | --- | boolean | --- | --- |
| treeProps | --- | antd-treeProps | --- | --- |
| data | --- | TreeDataNode[] | [] | --- |
| onSearch | --- | (e: any) => void | - | --- |
| onItemCheck | --- | TreeProps['onCheck'] | - | --- |
| onItemSelect | --- | TreeProps['onSelect'] | - | --- |
| customImport | 自定义搜索栏边上的过滤图标 | ReactNode 、string | - | --- |
| extra | 数组件上方插槽 | ReactNode 、string | --- | --- |
| prefixCls | class前缀用于覆盖class | string | --- | --- |
| showSelectBar | 显示搜索框 | boolean | false | --- |
| filterSelectProps | 搜索框 | antd-SelectProps | - | --- |
| showSearchBar | 显示搜索框 | boolean | false | --- |
| noFilter | 是否显示搜索拓展 | boolean | false | --- |
| filterList | 过滤插槽列表 | IOption[] | [] | --- |
| optionList | 操作按钮列表 | IOption[] | [] | --- |
| showTagPanel | 显示标签插槽 | boolean | false | --- |
| tagList | 标签列表 | ITag[] | [] | --- |
| onSelect | 搜索选中事件 | SelectProps['onChange'] | - | --- |
| tagExpandAll | 标签展开状态 | boolean | false | --- |
| onTagCheck | 标签点击事件 | (value: string, tag: ITag) => void; | false | --- |
| checkedTags | 标签选中状态 | string[] | [] | --- |
| onResetTags | 重置标签事件 | () => void | - | --- |
| onTagExpand | 标签展开事件 | (e: any) => void | - | --- |
| tagFootRender | 标签展开状态 | ReactNode string | false | --- |
### IOption
```ts
interface IOption {
label: string
key: string
icon?: string | ReactNode
disabled?: boolean;
showTooltip?: boolean;
onClick?: () => void
className?: string;
dropdownConfig?: DropDownProps
}
```
### ITag
```ts
interface ITag {
label: string
value: string
icon?: ReactNode
parentNode?: string
children?: ITag[]
}
```
## 组件设计
该组件包含以下功能:
1. 顶部按钮支持
2. 输入框单行展示
3. 选择框和筛选框同一行
4. 按钮列表

View File

@ -0,0 +1,7 @@
/**
* Created by jiangzhixiong on 2024/06/04
*/
import TreePanel from './TreePanel'
export type { TreePanelProps, TreePanelRefProps } from './TreePanel'
export default TreePanel

View File

@ -1,6 +1,5 @@
import React, { ReactNode } from 'react';
import { Button, ConfigProvider, theme, Flex, InputProps, TabsProps, Tabs, ButtonProps, Tree, TreeProps, DataNode as TreeDataNode } from '@zhst/meta'
import { Input } from 'antd'
import { Button, Input, ConfigProvider, theme, Flex, InputProps, TabsProps, Tabs, ButtonProps, Tree, TreeProps, DataNode as TreeDataNode } from '@zhst/meta'
import { IconFont } from '@zhst/icon'
import './index.less'

View File

@ -1,5 +1,5 @@
import React, { FC, useState } from 'react';
import { Modal, ModalProps, Radio, RadioGroupProps, Select, SelectProps, TransferProps, TreeDataNode, TreeProps } from 'antd';
import { Modal, ModalProps, Radio, RadioGroupProps, Select, SelectProps, TransferProps, TreeDataNode, TreeProps } from '@zhst/meta';
import TreeTransfer from '../treeTransfer';
import { TreeTransferProps } from '../treeTransfer'

View File

@ -31,6 +31,8 @@ export type {
DirectoryTreeExpandAction,
DirectoryTreeProps
} from './tree';
export { default as Spin } from './spin'
export type { SpinProps, SpinSize, SpinType } from './spin'
export { default as message } from './message'
export { default as Button } from './button'
export type { ArgsProps } from './message'

View File

@ -26,8 +26,8 @@ const seedToken: SeedToken = {
colorWarning: '#FAAD14',
colorError: '#FF4D4F',
colorInfo: '#0099FF',
colorLink: '',
colorTextBase: '',
colorLink: '#0099FF',
colorTextBase: '#191919',
colorBgBase: '',
@ -63,7 +63,7 @@ const seedToken: SeedToken = {
sizePopupArrow: 16,
// Control Base
controlHeight: 32,
controlHeight: 36,
// zIndex
zIndexBase: 0,