feat(@zhst/biz): 树组件支持tag面板、优化filter传参、优化option传参、废弃之前的定制化方案

This commit is contained in:
NICE CODE BY DEV 2024-05-23 15:14:17 +08:00
parent e90dcce641
commit da3c1714c5
18 changed files with 606 additions and 248 deletions

View File

@ -1,5 +1,11 @@
# @zhst/biz
## 0.24.0
### Minor Changes
- 树组件支持 tag 面板、优化 filter 传参、优化 option 传参、废弃之前的定制化方案
## 0.23.0
### Minor Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/biz",
"version": "0.23.0",
"version": "0.24.0",
"description": "业务库",
"keywords": [
"business",

View File

@ -1,4 +1,4 @@
import React, { FC, useContext } from 'react';
import React, { FC, ReactNode, useContext } from 'react';
import { Tabs, TabsProps } from 'antd'
import { ConfigProvider } from '@zhst/meta';
import BoxPanel from './components/boxPanel';
@ -9,31 +9,18 @@ export interface BoxSelectTreeProps extends BoxPanelProps {
onTabChange?: (e: any) => void
tabsProps?: TabsProps
prefixCls?: string;
footer?: ReactNode
}
const { ConfigContext } = ConfigProvider
const BoxSelectTree: FC<BoxSelectTreeProps> = (props) => {
const {
data,
boxDataSource = [],
onTabChange,
onSearch,
onItemCheck,
onItemSelect,
onBoxBatchDelete,
onBoxDelete,
onCreateSubmit,
onClockClick,
onImport,
onCreate,
prefixCls: customizePrefixCls,
tabsProps,
searchInputProps,
treeProps,
customImport,
showOptions,
extraBtns,
prefixCls: customizePrefixCls
onTabChange,
footer,
...rest
} = props
const { getPrefixCls } = useContext(ConfigContext);
const componentName = getPrefixCls('biz-box-select-tree', customizePrefixCls);
@ -61,23 +48,9 @@ const BoxSelectTree: FC<BoxSelectTreeProps> = (props) => {
{...tabsProps}
/>
<BoxPanel
searchInputProps={searchInputProps}
boxDataSource={boxDataSource}
treeProps={treeProps}
data={data}
onCreate={onCreate}
onCreateSubmit={onCreateSubmit}
onBoxBatchDelete={onBoxBatchDelete}
onBoxDelete={onBoxDelete}
onSearch={onSearch}
onItemCheck={onItemCheck}
onItemSelect={onItemSelect}
showOptions={showOptions}
customImport={customImport}
extraBtns={extraBtns}
onClockClick={onClockClick}
onImport={onImport}
{...rest}
/>
{footer}
</div>
);
};

View File

@ -1,9 +1,11 @@
.zhst-biz-box-select-tree-panel {
&-search {
display: flex;
align-items: center;
padding: 0 12px;
&-input {
flex: 1;
margin-right: 4px;
}
@ -18,6 +20,7 @@
justify-content: space-between;
&-common {
flex: 1;
padding: 4px 8px;
}
@ -30,6 +33,60 @@
}
}
&-tags {
position: relative;
height: 100%;
margin-top: 6px;
padding: 12px;
font-size: 0;
border-top: 1px solid #09f;
border-bottom: 1px solid #09f;
&-tag {
margin-right: 6px;
font-weight: bold;
font-size: 14px;
transition:.3s ease all;
&_common{
margin-bottom: 6px;
display: inline-block;
margin-right: 6px;
padding: 6px;
font-size: 12px;
cursor: pointer;
background-color: #f6f6f6;
transition: .3s ease all;
border-radius: 3px;
box-sizing: content-box;
&:hover {
color: #09f;
background-color: rgba(0, 0, 0, 6%);
}
}
&_checked {
color: #09f;
background-color: #edf8ff;
}
&_fz12 {
font-size: 12px;
}
&_option {
display: inline-block;
}
&_absolute {
position: absolute;
top: 12px;
right: 0;
}
}
}
&-tree {
padding: 6px 0;
}

View File

@ -1,73 +1,160 @@
import React, { FC, useState, useRef, useContext } from 'react';
import{ Button, Divider, Input, Space, TreeDataNode } from 'antd'
import { ModalForm, ModalFormProps, ProFormInstance, ProFormText } from '@ant-design/pro-components'
import { ConfigProvider } from '@zhst/meta';
import React, { FC, useState, useContext, ReactNode } from 'react';
import{ Button, Divider, Dropdown, Input, TreeDataNode } from 'antd'
import { ModalFormProps } from '@ant-design/pro-components'
import { ButtonProps, ConfigProvider, Tooltip } from '@zhst/meta';
import { IconFont } from '@zhst/icon';
import { ClockCircleOutlined, CloseCircleOutlined, DiffOutlined, FolderAddOutlined, ImportOutlined, SwitcherOutlined } from '@ant-design/icons'
import type { TreeProps, InputProps } from 'antd';
import type { TreeProps, InputProps, DropDownProps } from 'antd';
import classNames from 'classnames';
import type { BoxTreeProps } from '../../../tree';
import TreeTransferModal from '../../../treeTransferModal'
import BoxTree from '../../../tree';
import './index.less'
import classNames from 'classnames';
interface IOption {
label: string
key: string
icon?: string | ReactNode
type?: ButtonProps['type'] | 'dropdown'
disabled?: boolean;
showTooltip?: boolean;
props?: ButtonProps
onClick?: () => void
dropdownConfig?: DropDownProps
}
interface ITag {
label: string
value: string
icon?: ReactNode
parentNode?: string
children?: ITag[]
}
export interface BoxPanelProps {
searchInputProps?: InputProps
showOptions?: boolean
treeProps?: Partial<BoxTreeProps>
data: TreeDataNode[]
boxDataSource: TreeDataNode[]
handleImport?: () => void
onSearch?: (e: any) => void
onItemCheck?: TreeProps['onCheck']
onItemSelect?: TreeProps['onSelect']
/**
* @deprecated 0.23.0
*/
onBoxBatchDelete?: (data?: any) => void
/**
* @deprecated 0.23.0
*/
onBoxDelete?: (data?: any) => void
/**
* @deprecated 0.23.0
*/
onCreateSubmit?: ModalFormProps['onFinish']
onClockClick?: () => void
/**
* @deprecated 0.23.0
*/
onClockClick?: () => void //
/**
* @deprecated 0.23.0
*/
onImport?: () => void
/**
* @deprecated 0.23.0
*/
onBatch?: () => void
/**
* @deprecated 0.23.0
*/
onCreate?: () => void
customImport?: any
extraBtns?: any
prefixCls?: string;
customImport?: ReactNode | string // 自定义搜索栏边上的过滤图标
extraBtns?: 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 BoxPanel: FC<BoxPanelProps> = (props) => {
const [isTreeCheckable, setIsTreeCheckable] = useState(false)
const {
searchInputProps,
showOptions = true,
extraBtns,
noFilter,
data = [],
onSearch,
treeProps,
onItemCheck,
onItemSelect,
onCreateSubmit,
onBoxBatchDelete,
onBoxDelete,
onClockClick,
onImport,
onBatch,
onCreate,
boxDataSource,
showSearchBar = true,
optionList = [
{
label: '导入盒子',
key: 'import',
icon: <ImportOutlined />,
onClick: () => onImport?.()
},
{
label: '新建组',
key: 'add',
icon: <FolderAddOutlined />,
onClick: () => onCreate?.()
},
{
label: '删除',
key: 'del',
icon: <CloseCircleOutlined />,
onClick: () => onBoxBatchDelete?.(),
props: {
danger: true
}
}
],
filterList = [
{
label: '多选',
key: 'multi',
icon: isTreeCheckable ? <SwitcherOutlined /> : <DiffOutlined />,
onClick: () => onBatch?.() || handleCheckable()
},
{
label: '时钟',
key: 'clock',
icon: <ClockCircleOutlined />,
onClick: () => onClockClick?.()
}
],
showTagPanel,
tagList,
tagExpandAll,
onTagExpand,
checkedTags = [],
onTagCheck,
onResetTags,
tagFootRender,
prefixCls: customizePrefixCls,
customImport
customImport: customFilter
} = props
const { getPrefixCls } = useContext(ConfigContext);
const componentName = getPrefixCls('biz-box-select-tree-panel', customizePrefixCls);
const [isTreeCheckable, setIsTreeCheckable] = useState(false)
const [targetItems, setTargetItems] = useState<TreeDataNode[]>([]);
const [boxChoiceOpen, setBoxChoiceOpen] = useState(false)
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
const createFormRef = useRef<
ProFormInstance<{
name: string;
boxList?: any[];
}>
>()
/**
*
@ -77,151 +164,154 @@ const BoxPanel: FC<BoxPanelProps> = (props) => {
setIsTreeCheckable(pre => !pre)
}
const onTreeCheck: TreeProps['onCheck'] = (keys: any, info) => {
let _targetItems: TreeDataNode[] = []
setCheckedKeys(keys)
info.checkedNodes.forEach(o => {
o.isLeaf && _targetItems.push(o)
})
setTargetItems(_targetItems)
/**
* filter
* @param _list
* @returns
*/
const initFilter = (_list?: BoxPanelProps['filterList']) => {
const WithDropdown = (dom: ReactNode, _config?: DropDownProps) => {
return (
<Dropdown placement="bottomLeft" arrow {..._config}>
{dom}
</Dropdown>
)
}
return _list?.map(item => (
<Tooltip
title={item.label}
open={item.showTooltip}
>
{item.type === 'dropdown' ? (
WithDropdown(
<Button className={classNames(componentName + '-search-btns-btn')} type="text" onClick={item.onClick} icon={item.icon} />,
item.dropdownConfig
)
) : (
<Button className={classNames(componentName + '-search-btns-btn')} type="text" onClick={item.onClick} icon={item.icon} />
)}
</Tooltip>
))
}
/**
*
* @param key
* @param param1
* filter
* @param _list
* @returns
*/
const onItemDelete = (key: any, { keys }: any) => {
setCheckedKeys(pre => {
const newKeys = pre.filter(_key => !keys.includes(_key))
return newKeys
const initOptions = (_list?: BoxPanelProps['optionList']) => {
return _list?.map((item, idx) => (
<>
<Tooltip
title={item.label}
>
{/* @ts-ignore */}
<Button className={classNames(componentName + '-btns-common')} type={item.type || 'text'} onClick={item.onClick} icon={item.icon} {...item?.props}>{item.label}</Button>
</Tooltip>
{idx !== _list.length - 1 && <Divider className={classNames(componentName + '-btns-divider')} type="vertical" />}
</>
))
}
/**
*
* @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)
}
})
setTargetItems(pre => pre.filter(o => o.key !== key))
}
// 盒子点击确定
const onBoxChoiceOk = async (data: any) => {
createFormRef.current?.setFieldValue('boxList', data)
createFormRef.current?.setFieldValue('boxName', 123)
console.log(createFormRef.current?.getFieldValue('boxList'))
setBoxChoiceOpen(false)
}
// 盒子选择重置
const onBoxChoiceReset = () => {
setCheckedKeys([])
setTargetItems([])
}
return (
<div className={componentName}>
{/* 盒子选择弹框 */}
<TreeTransferModal
open={boxChoiceOpen}
onCancel={() => setBoxChoiceOpen(false)}
onRadioChange={(e) => console.log('radio', e.target.value)} // 顶部 radio 事件
dataSource={boxDataSource} // 数据源
targetItems={targetItems} // 右侧选中项
checkedKeys={checkedKeys} // 左侧选中
onReset={onBoxChoiceReset} // 重置按钮事件
onOk={onBoxChoiceOk} // 确定按钮事件
onTreeCheck={onTreeCheck} // 树check选中事件
onItemDelete={onItemDelete} // 右侧点击删除事件
/>
<div className={classNames(componentName + '-search')}>
<Input
className={classNames(componentName + '-search-input')}
size='middle'
onChange={(e) => onSearch?.(e)}
placeholder='请输入盒子名称'
{...searchInputProps}
/>
{customImport || (
<div
className={classNames(componentName + '-search-btns')}
>
<Button type="text" onClick={() => onBatch?.() || handleCheckable()} icon={isTreeCheckable ? <SwitcherOutlined /> : <DiffOutlined />} />
<Button type="text" onClick={() => onClockClick?.()} icon={<ClockCircleOutlined />} />
</div>
)}
</div>
{/* 是否显示操作按钮 */}
{/* 搜索栏 */}
{showSearchBar && (
<div className={classNames(componentName + '-search')}>
<Input
className={classNames(componentName + '-search-input')}
size='middle'
onChange={(e) => onSearch?.(e)}
placeholder='请输入盒子名称'
{...searchInputProps}
/>
{customFilter || (!noFilter && (
<div
className={classNames(componentName + '-search-btns')}
>
{/* @ts-ignore */}
{initFilter(filterList)}
</div>
))}
</div>
)}
{/* 默认操作按钮 */}
{showOptions && (
<>
<div className={classNames(componentName + '-btns')}>
<Button className={classNames(componentName + '-btns-common')} type='text' onClick={() => onImport?.()} icon={<ImportOutlined />} ></Button>
<Divider className={classNames(componentName + '-btns-divider')} type="vertical" />
{onCreate ?
(
<Button className={classNames(componentName + '-btns-common')} onClick={onCreate} type='text' icon={<FolderAddOutlined />} ></Button>
) : (
<ModalForm<{
name: string
boxList?: any[]
}>
className={classNames(componentName + '-create-modal')}
open={onCreate ? false : undefined}
formRef={createFormRef}
title="新建组"
modalProps={{ destroyOnClose: true }}
layout='horizontal'
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
trigger={<Button type='text' className={classNames(componentName + '-btns-common')} icon={<FolderAddOutlined />} ></Button>}
submitter={{
searchConfig: {
submitText: '确定',
resetText: '取消',
},
}}
onFinish={onCreateSubmit}
>
<ProFormText
rules={[
{
required: true,
max: 20,
},
{
pattern: /^[^\s]*$/g,
message: '禁止输入空格'
}
]}
fieldProps={{ showCount: true }}
width="md"
name="name"
label="盒子组名称"
placeholder="请输入盒子名称"
/>
<ProFormText
width="md"
name="boxList"
label="盒子选择"
fieldProps={{
readOnly: true,
value: `已选择${createFormRef.current?.getFieldValue('boxList')?.length || 0}个盒子`,
suffix: (
<Space>
<a onClick={() => {
createFormRef.current?.setFieldValue('boxList', null)
onBoxChoiceReset()
}} ></a>
<a onClick={() => setBoxChoiceOpen(true)}></a>
</Space>
)
}}
/>
</ModalForm>
)
}
<Divider className={classNames(componentName + '-btns-divider')} type="vertical" />
{/* @ts-ignore */}
<Button className={classNames(componentName + '-btns-common')} danger type='text' icon={<CloseCircleOutlined />} disabled={treeProps?.checkedKeys?.length <= 0} onClick={onBoxBatchDelete} ></Button>
{initOptions(optionList)}
</div>
<Divider style={{ margin: 0 }} />
</>
)}
{extraBtns}
{showTagPanel && (
<div className={classNames(componentName + '-tags')}>
<span
className={classNames(componentName + '-tags-tag')}
style={tagExpandAll ? {
marginBottom: '12px',
display: tagExpandAll ? 'block' : 'inline-block'
} : {}
}
></span>
{initTagPanel(tagList, tagExpandAll)}
<div
className={classNames(
componentName + '-tags-tag_option',
{ [componentName + '-tags-tag_absolute']: tagExpandAll },
)}
>
{tagExpandAll && (
<span
className={classNames(componentName + '-tags-tag_common', componentName + '-tags-tag_fz12')}
onClick={onResetTags}
></span>
)}
<span
className={classNames(componentName + '-tags-tag_common', componentName + '-tags-tag_fz12')}
onClick={onTagExpand}
>{tagExpandAll ? '收起' : '更多'}<IconFont icon={tagExpandAll ? 'icon-shangjiantou' : 'icon-xiajiantou'} /></span>
</div>
{tagFootRender}
</div>
)}
<BoxTree
className={classNames(componentName + '-tree')}
treeCheckable={isTreeCheckable}

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { BoxSelectTree } from '@zhst/biz';
import { treeData, boxDataSource } from './mock'
import { Select, TreeProps, Modal, Checkbox } from 'antd';
import { Select, TreeProps, Modal, Checkbox, Button } from 'antd';
const { Option } = Select
@ -81,6 +81,7 @@ const demo = () => {
onItemRenameFinish: async (val, data) => console.log('盒子重命名提交(返回boolean,控制弹框显示\隐藏)', val, data),
checkedKeys,
}}
footer={<Button block ></Button>}
/>
</div>
);

View File

@ -0,0 +1,68 @@
import React, { useState } from 'react';
import { BoxSelectTree } from '@zhst/biz';
import { treeData, boxDataSource } from './mock'
import { FilterOutlined } from '@ant-design/icons';
import { Badge } from 'antd';
const demo = () => {
const [activeKey] = useState('1')
const [checkedKeys] = useState<string[]>([]);
return (
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
<BoxSelectTree
data={activeKey === '1' ? treeData : boxDataSource}
boxDataSource={boxDataSource}
showOptions={false}
tabsProps={{
activeKey,
}}
treeProps={{
checkedKeys
}}
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',
},
],
}
}
},
]}
/>
</div>
);
};
export default demo;

View File

@ -0,0 +1,33 @@
import React, { useState } from 'react';
import { BoxSelectTree } from '@zhst/biz';
import { treeData, boxDataSource } from './mock'
import { CloseCircleOutlined } from '@ant-design/icons';
const demo = () => {
const [activeKey, setActiveKey] = useState('1')
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
return (
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
<BoxSelectTree
data={activeKey === '1' ? treeData : boxDataSource}
boxDataSource={boxDataSource}
tabsProps={{
activeKey,
}}
treeProps={{
checkedKeys
}}
optionList={[
{
label: '删除',
key: 'del',
icon: <CloseCircleOutlined />,
}
]}
/>
</div>
);
};
export default demo;

View File

@ -2,10 +2,12 @@ import React, { useState } from 'react';
import { BoxSelectTree } from '@zhst/biz';
import { treeData, boxDataSource } from './mock'
import { Button } from 'antd';
import { ClockCircleOutlined, DiffOutlined, SwitcherOutlined } from '@ant-design/icons';
const demo = () => {
const [activeKey, setActiveKey] = useState('1')
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
const [isTreeCheckable, setIsTreeCheckable] = useState(false)
return (
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
@ -13,10 +15,23 @@ const demo = () => {
data={activeKey === '1' ? treeData : boxDataSource}
boxDataSource={boxDataSource}
showOptions={false}
extraBtns={<Button type="dashed" style={{ color: 'green' }}></Button>}
extraBtns={<div><Button type="dashed" style={{ color: 'red' }}></Button> Hello Lambo</div>}
tabsProps={{
activeKey,
}}
filterList={[
{
label: '多选',
key: 'multi',
icon: isTreeCheckable ? <SwitcherOutlined /> : <DiffOutlined />,
onClick: () => setIsTreeCheckable(pre => !pre)
},
{
label: '时钟',
key: 'clock',
icon: <ClockCircleOutlined />,
}
]}
treeProps={{
checkedKeys
}}

View File

@ -0,0 +1,27 @@
import React, { useState } from 'react';
import { BoxSelectTree } from '@zhst/biz';
import { treeData, boxDataSource } from './mock'
const demo = () => {
const [activeKey, setActiveKey] = useState('1')
const [checkedKeys, setCheckedKeys] = useState<string[]>([]);
return (
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
<BoxSelectTree
data={activeKey === '1' ? treeData : boxDataSource}
boxDataSource={boxDataSource}
showOptions={false}
tabsProps={{
activeKey,
}}
treeProps={{
checkedKeys
}}
noFilter
/>
</div>
);
};
export default demo;

View File

@ -30,7 +30,7 @@ const demo = () => {
tabsProps={{
activeKey,
}}
customImport={<Button type="text" icon={<FilterOutlined />} />}
customImport={<div style={{ color: 'red' }}></div>}
searchInputProps={{
addonBefore: (
<Select
@ -43,7 +43,6 @@ const demo = () => {
>
{[
{ value: '1', label: '盒子' },
{ value: '2', label: '盒子组' }
].map(item => (
<Option value={item.value}>{item.label}</Option>
))}

View File

@ -0,0 +1,84 @@
import React, { useState } from 'react';
import { BoxSelectTree } from '@zhst/biz';
import { treeData, boxDataSource } from './mock'
import { Button, Switch } from 'antd';
const demo = () => {
const [activeKey] = useState('1')
const [checkedKeys] = useState<string[]>([]);
const [checkedTags, setCheckedTags] = useState<string[]>([]);
const [tagExpandAll, setTagExpandAll] = useState(false);
const [showTagPanel, setShowTagPanel] = useState(true);
return (
<div>
<div style={{ marginBottom: '12px' }}>
<Switch value={showTagPanel} onChange={status => setShowTagPanel(status)} />
</div>
<div style={{ border: '1px solid #ccc', width: '340px', minHeight: '900px' }}>
<BoxSelectTree
data={activeKey === '1' ? treeData : boxDataSource}
boxDataSource={boxDataSource}
showOptions={false}
tabsProps={{
activeKey,
}}
treeProps={{
checkedKeys
}}
showTagPanel={showTagPanel}
tagExpandAll={tagExpandAll}
onTagExpand={() => {
setTagExpandAll(pre => !pre)
setCheckedTags([])
}}
onTagCheck={(value) => setCheckedTags(pre => {
if (pre.includes(value)) {
return pre.filter(item => item !== value)
} else {
return [...pre, value]
}
})}
onResetTags={() => setCheckedTags([])}
checkedTags={checkedTags}
tagFootRender={<Button danger>dom</Button>}
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',
}
]
},
]
}
/>
</div>
</div>
);
};
export default demo;

View File

@ -3,7 +3,7 @@ category: Components
title: BoxSelectTree 盒子树
toc: content
demo:
cols: 2
cols: 3
group:
title: 进阶组件
---
@ -15,6 +15,10 @@ group:
<code src="./demo/basic.tsx">基本用法</code>
<code src="./demo/extraBtns.tsx">自定义其它按钮</code>
<code src="./demo/noOptions.tsx">不显示其它按钮</code>
<code src="./demo/customOptions.tsx">自定义配置按钮</code>
<code src="./demo/noFilter.tsx">不显示过滤按钮</code>
<code src="./demo/customFilter.tsx">自定义过滤按钮</code>
<code src="./demo/withTagPanel.tsx">标签看板</code>
<code src="./demo/async.tsx">异步加载数据</code>
## API
@ -22,17 +26,35 @@ group:
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| data | 数据源 | Array[] | [] | - |
| tabsProps | Tabs组件的Props | 参考antd的Tabs组件 | - | - |
| searchInputProps | 搜索框的 Props | 参考antd的Input组件 | - | - |
| showOptions | 展示其它功能按钮 | boolean | true | - |
| filterList | 搜索框边上的插槽列表 | boolean | true | - |
| customImport | 自定义搜索栏边上的过滤图标 | ReactNode、string | - | - |
| extraBtns | 搜索栏下面的插槽 | ReactNode、string | - | - |
| prefixCls | class前缀 | string | - | - |
| showSearchBar | 是否显示搜索框 | boolean | true | - |
| noFilter | 是否显示搜索拓展 icon | boolean | false | - |
| filterList | 拓展列表 | IOption[] | [] | - |
| optionList | 操作按钮列表 | IOption[] | [] | - |
| showTagPanel | 是否显示标签看板 | boolean | false | - |
| tagList | 标签列表 | ITag[] | [] | - |
| tagExpandAll | 标签是否展开 | boolean | false | - |
| checkedTags | 选中的标签值 | string[] | [] | - |
| footer | 盒子树底部渲染(需要内容撑开) | ReactNode、string | - | - |
| tagFootRender | 标签看板底部自定义 | ReactNode、string | - | - |
| onResetTags | 标签重置 | () => void | [] | - |
| onTagExpand | 标签展开 | (e: any) => void | [] | - |
| onTabChange | tab切换监听 | function: (e) => void | - | - |
| onTagCheck | 标签选中事件 | (value: string, tag: ITag) => void | false | - |
| onSearch | 搜索监听 | function: (e) => void | - | - |
| onItemSelect | 树当前选中(单选) | function: (e) => void | - | - |
| onItemCheck | 树选择(支持多选) | function: (e) => void | - | - |
| tabsProps | Tabs组件的Props | antd的Tabs组件 | - | - |
| searchInputProps | 搜索框的Props | antd的Input组件 | - | - |
| onTabChange | tab切换监听 | function: (e) => void | - | - |
| onBoxDelete | 盒子删除事件 | function: (e) => void | - | - |
| onBoxBatchDelete | 盒子批量删除事件 | function: (e) => void | - | - |
| onCreateSubmit | 新建提交事件 | function: (e) => void | - | - |
| onImport | 监听导入盒子点击事件 | function: () => void | - | - |
| onClockClick | 监听时钟点击事件 | function: () => void | - | - |
| onCreate | 监听创建点击事件 | function: () => void | 如果不传默认用自带的创建事件 | - |
| showOptions | 展示其它功能按钮 | boolean | true | - |
| onBoxDelete | 盒子删除事件 | function: (e) => void | - | 0.23.0 以后弃用 |
| onBatch | 多选 | function: (e) => void | - | 0.23.0 以后弃用 |
| onBoxBatchDelete | 盒子批量删除事件 | function: (e) => void | - | 0.23.0 以后弃用 |
| onCreateSubmit | 新建提交事件 | function: (e) => void | - | 0.23.0 以后弃用 |
| onImport | 监听导入盒子点击事件 | function: () => void | - | 0.23.0 以后弃用 |
| onClockClick | 监听时钟点击事件 | function: () => void | - | 0.23.0 以后弃用 |
| onCreate | 监听创建点击事件 | function: () => void | 如果不传默认用自带的创建事件 | 0.23.0 以后弃用 |

View File

@ -1,7 +1,7 @@
import React, { FC, useState } from 'react';
import { Tree, Badge, TreeDataNode, Space, TreeProps, theme } from 'antd';
import { Tree, Badge, TreeDataNode, Space, TreeProps, theme, ConfigProvider } from 'antd';
import { CloseOutlined, EditOutlined, SettingOutlined } from '@ant-design/icons'
import { ModalForm, ProFormText } from '@ant-design/pro-components';
import classNames from 'classnames';
import './index.less'
const componentName = 'zhst-biz-tree'
@ -30,7 +30,7 @@ const boxTree: FC<BoxTreeProps> = (props) => {
showItemOption = true,
treeCheckable = false,
onItemRename,
onItemRenameFinish,
className: customClassName,
customOptions
} = props
const { token } = useToken()
@ -45,6 +45,7 @@ const boxTree: FC<BoxTreeProps> = (props) => {
return (
<Tree
className={classNames(componentName, customClassName)}
checkable={treeCheckable}
blockNode
onSelect={(selectedKeys, info) => {
@ -70,38 +71,11 @@ const boxTree: FC<BoxTreeProps> = (props) => {
<Space className={`${componentName}-item-render_right`} style={{ float:'right' }} >
{customOptions || (
<>
<ModalForm
title="重命名"
width={600}
modalProps={{ destroyOnClose: true }}
layout='horizontal'
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
trigger={<EditOutlined onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onItemRename?.(_nodeData)
}} />}
submitter={{
searchConfig: {
submitText: '确定',
resetText: '取消',
},
}}
onFinish={async (value) => onItemRenameFinish?.(value, _nodeData)}
>
<ProFormText
rules={[
{
required: true,
},
]}
width="md"
name="name"
label="盒子名称"
placeholder="请输入盒子名称"
/>
</ModalForm>
<EditOutlined onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onItemRename?.(_nodeData)
}} />
<SettingOutlined onClick={(e) => {
e.preventDefault();
e.stopPropagation();

View File

@ -1,9 +1,11 @@
.zhst-biz-tree-item-render {
&_right {
display: none;
}
.zhst-biz-tree {
&-item-render {
&_right {
display: none;
}
&:hover &_right {
display: inline-flex;
&:hover &_right {
display: inline-flex;
}
}
}

View File

@ -1,5 +1,12 @@
# @zhst/material
## 0.18.4
### Patch Changes
- Updated dependencies
- @zhst/biz@0.24.0
## 0.18.3
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/material",
"version": "0.18.3",
"version": "0.18.4",
"description": "物料库",
"keywords": [
"business",