From 570f183382278fbb6d421cc11438542cc74253cd Mon Sep 17 00:00:00 2001 From: jiangzhixiong <710328466@qq.com> Date: Tue, 21 May 2024 10:37:47 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(biz=E6=97=A0=E9=99=90=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E7=BB=84=E4=BB=B6):=20=E6=B7=BB=E5=8A=A0=E5=B1=8F?= =?UTF-8?q?=E5=B9=95=E8=87=AA=E9=80=82=E5=BA=94=E6=92=91=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/commonCard/CommonCard.tsx} | 63 ++++---- .../components/commonCard}/index.less | 32 ++++- .../components/commonCard/index.tsx | 5 + .../biz/src/CustomCard/demo/commonCard.tsx | 42 ++++++ packages/biz/src/CustomCard/index.md | 31 ++++ packages/biz/src/CustomCard/index.tsx | 6 + packages/biz/src/index.tsx | 5 + .../biz/src/infiniteList/InfiniteList.tsx | 134 ++++++++---------- .../biz/src/infiniteList/components/index.tsx | 6 - packages/biz/src/infiniteList/demo/basic.tsx | 30 ++-- packages/biz/src/infiniteList/demo/custom.tsx | 10 +- packages/biz/src/infiniteList/index.md | 29 ++-- packages/biz/src/infiniteList/type.ts | 0 13 files changed, 247 insertions(+), 146 deletions(-) rename packages/biz/src/{infiniteList/components/SearchCard.tsx => CustomCard/components/commonCard/CommonCard.tsx} (54%) rename packages/biz/src/{infiniteList/components => CustomCard/components/commonCard}/index.less (67%) create mode 100644 packages/biz/src/CustomCard/components/commonCard/index.tsx create mode 100644 packages/biz/src/CustomCard/demo/commonCard.tsx create mode 100644 packages/biz/src/CustomCard/index.md create mode 100644 packages/biz/src/CustomCard/index.tsx delete mode 100644 packages/biz/src/infiniteList/components/index.tsx create mode 100644 packages/biz/src/infiniteList/type.ts diff --git a/packages/biz/src/infiniteList/components/SearchCard.tsx b/packages/biz/src/CustomCard/components/commonCard/CommonCard.tsx similarity index 54% rename from packages/biz/src/infiniteList/components/SearchCard.tsx rename to packages/biz/src/CustomCard/components/commonCard/CommonCard.tsx index af4f9e2..a4feece 100644 --- a/packages/biz/src/infiniteList/components/SearchCard.tsx +++ b/packages/biz/src/CustomCard/components/commonCard/CommonCard.tsx @@ -2,14 +2,14 @@ * Created by jiangzhixiong on 2024/04/28 */ -import React, { forwardRef, useContext, useImperativeHandle } from 'react' +import React, { forwardRef, MouseEventHandler, ReactNode, useContext, useImperativeHandle } from 'react' import { ConfigProvider, EMPTY_BASE64 } from '@zhst/meta' import { Flex, Image } from 'antd'; import './index.less' const { ConfigContext } = ConfigProvider -export interface Idata { +export interface IData { id?: string | number; url?: string; sort?: number; @@ -17,24 +17,25 @@ export interface Idata { subtitle?: string; } -export interface SearchCardProps extends Idata { +export interface CommonCardProps extends IData { prefixCls?: string; - data?: Idata + data?: IData width?: string; height?: string; onCreateTxt?: string; - onCreate?: (data: any) => void; + onCreate?: () => void; onAddTxt?: string; onAdd?: (data: any) => void; onRemoveTxt?: string; onRemove?: (data: any) => void; - customOptionRender?: React.ReactNode + actions?: ReactNode[] + onItemClick?: (data: any, e: React.MouseEvent) => void; } -export interface SearchCardRefProps { +export interface CommonCardRefProps { } -const SearchCard = forwardRef((props, ref) => { +const CommonCard = forwardRef((props, ref) => { const { prefixCls: customizePrefixCls, url, @@ -43,26 +44,29 @@ const SearchCard = forwardRef((props, ref) subtitle, sort, data, - onCreate, - onCreateTxt = '创建检索', - onAddTxt = '添加目标', - onRemoveTxt = '移除轨迹', - onAdd, - onRemove, - customOptionRender, + actions = [], width = '184px', - height = '100%' + height = '100%', + onItemClick } = props const { getPrefixCls } = useContext(ConfigContext) const componentName = getPrefixCls('biz-search-card', customizePrefixCls); - const stopBumble = (e: React.MouseEvent, fn?: ((data: Idata) => void), data?: Idata) => { - e.stopPropagation() - fn?.(data!) + const optionListRender = (_actions: ReactNode[]) => { + return _actions.map((action, i) => ( + // eslint-disable-next-line react/no-array-index-key +
  • + {action} + {i !== _actions.length - 1 && } +
  • + )) + } + + const handleItemClick = (e: React.MouseEvent, _data: IData | undefined) => { + onItemClick?.(data, e) } useImperativeHandle(ref, () => ({ - })) return ( @@ -72,9 +76,10 @@ const SearchCard = forwardRef((props, ref) width, height }} + onClick={e => handleItemClick(e, data)} >
    - {id || sort} + {sort || id} ((props, ref) preview={false} fallback={EMPTY_BASE64} /> - - {customOptionRender || ( - <> - stopBumble(e, onCreate, data)}>{onCreateTxt} - | - stopBumble(e, onAdd, data)}>{onAddTxt} - | - stopBumble(e, onRemove, data)}>{onRemoveTxt} - - )} - +
      + {optionListRender(actions)} +

    {title || data?.title}

    @@ -102,4 +99,4 @@ const SearchCard = forwardRef((props, ref) ) }) -export default SearchCard +export default CommonCard diff --git a/packages/biz/src/infiniteList/components/index.less b/packages/biz/src/CustomCard/components/commonCard/index.less similarity index 67% rename from packages/biz/src/infiniteList/components/index.less rename to packages/biz/src/CustomCard/components/commonCard/index.less index c9c413d..acd6da6 100644 --- a/packages/biz/src/infiniteList/components/index.less +++ b/packages/biz/src/CustomCard/components/commonCard/index.less @@ -1,18 +1,23 @@ .zhst-biz-search-card { + display: inline-block; border: 2px solid transparent; transition: .1s ease-in all; cursor: pointer; + overflow: hidden; &:hover { + transition: .5s ease all; border: 2px solid #09f; .zhst-biz-search-card-main-opt { display: flex; + align-items: center; } } &-main { position: relative; + overflow: hidden; &-num { position: absolute; @@ -32,11 +37,17 @@ &-img { width: 100%; - height: 240px; + overflow: hidden; + + &:hover { + transition: .5s ease-in all; + transform: scale(1.05); + } } &-opt { display: none; + margin: 0; position: absolute; padding: 6px 3px; left: 0; @@ -48,11 +59,28 @@ box-sizing: border-box; transition: .2s ease-in all; + li { + position: relative; + list-style: none; + flex: 1; + text-align: center; + } + + &-action-split { + position: absolute; + top: 50%; + right: 0; + width: 1px; + height: 80%; + transform: translateY(-50%); + background-color: #fff; + } + a { color: #fff; &:hover { - opacity: 0.9; + opacity: 0.88; } } } diff --git a/packages/biz/src/CustomCard/components/commonCard/index.tsx b/packages/biz/src/CustomCard/components/commonCard/index.tsx new file mode 100644 index 0000000..390b0af --- /dev/null +++ b/packages/biz/src/CustomCard/components/commonCard/index.tsx @@ -0,0 +1,5 @@ +import CommonCard from './CommonCard' + +export type { CommonCardProps, CommonCardRefProps } from './CommonCard' + +export default CommonCard diff --git a/packages/biz/src/CustomCard/demo/commonCard.tsx b/packages/biz/src/CustomCard/demo/commonCard.tsx new file mode 100644 index 0000000..8c48276 --- /dev/null +++ b/packages/biz/src/CustomCard/demo/commonCard.tsx @@ -0,0 +1,42 @@ +import React, { useEffect, useState } from 'react' +import { CommonCard } from '@zhst/biz' +import { uniqueId } from '@zhst/func' + +export default () => { + const [data, setData] = useState([]) + + useEffect(() => { + fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo') + .then((res) => res.json()) + .then((body) => { + let res = body.results.map((o, index) => { + return { + id: uniqueId(), + sort: index + 1, + title: o.name.first, + subtitle: o.name.last, + url: o.picture.large + } + }) + setData(res); + }) + }, []) + + return ( +
    + {data?.map(item => ( + 创建检索, + 创建布控, + 删除点位 + ]} + /> + ))} +
    + ) +} diff --git a/packages/biz/src/CustomCard/index.md b/packages/biz/src/CustomCard/index.md new file mode 100644 index 0000000..758b53b --- /dev/null +++ b/packages/biz/src/CustomCard/index.md @@ -0,0 +1,31 @@ +--- +category: Components +title: CustomCard 定制化卡片 +toc: content +group: + title: 数据展示 +--- + +定制化卡片 + +## 代码演示 + +基本卡片 + +## API + +### CommonCardProps + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --- | --- | --- | --- | --- | +| data | 数据源 | IData[] | [] | - | +| prefixCls | 数据源 | string | [] | - | +| width | 宽度 | string | [] | - | +| height | 高度 | string | [] | - | +| onCreateTxt | 创建方法文字 | string | [] | - | +| onCreate | 创建方法 | () => void | [] | - | +| onAddTxt | 数据源 | string | [] | - | +| onAdd | 数据源 | () => void | [] | - | +| onRemoveTxt | 数据源 | string | [] | - | +| onRemove | 数据源 | () => void | [] | - | +| customOptionRender | 数据源 | React.ReactNode | - | - | diff --git a/packages/biz/src/CustomCard/index.tsx b/packages/biz/src/CustomCard/index.tsx new file mode 100644 index 0000000..67afe2c --- /dev/null +++ b/packages/biz/src/CustomCard/index.tsx @@ -0,0 +1,6 @@ +/** + * Created by jiangzhixiong on 2024/04/28 + */ + +export { default as CommonCard } from './components/commonCard' +export type { CommonCardProps, CommonCardRefProps } from './components/commonCard' diff --git a/packages/biz/src/index.tsx b/packages/biz/src/index.tsx index 1c67317..f9d89b3 100644 --- a/packages/biz/src/index.tsx +++ b/packages/biz/src/index.tsx @@ -9,6 +9,11 @@ export type { TreeTransferProps } from './treeTransfer' export { default as TreeTransferModal } from './treeTransferModal' export type { TreeTransferModalProps } from './treeTransferModal' export { default as WarningRecordCard } from './WarningRecordCard' +export { CommonCard } from './CustomCard' +export type { + CommonCardProps, + CommonCardRefProps +} from './CustomCard' export type { IRecord, WarningRecordCardProps } from './WarningRecordCard' export { default as OdModal } from './odModal' export type { ODModalProps } from './odModal' diff --git a/packages/biz/src/infiniteList/InfiniteList.tsx b/packages/biz/src/infiniteList/InfiniteList.tsx index b8f5bba..53c6097 100644 --- a/packages/biz/src/infiniteList/InfiniteList.tsx +++ b/packages/biz/src/infiniteList/InfiniteList.tsx @@ -2,24 +2,30 @@ * Created by jiangzhixiong */ -import React, { forwardRef, ReactNode, useContext, useImperativeHandle, useRef } from 'react' +import React, { forwardRef, ReactNode, useContext, useEffect, useImperativeHandle, useRef } from 'react' import { ConfigProvider } from '@zhst/meta'; -import { Divider, Flex } from 'antd'; import classNames from 'classnames'; import InfiniteScroll from 'react-infinite-scroll-component'; -import { SearchCard, SearchCardProps } from './components'; +import { Spin, SpinProps } from 'antd'; +import { useSize } from '@zhst/hooks'; import './index.less' -import { Idata } from './components/SearchCard'; const { ConfigContext } = ConfigProvider -export interface InfiniteListProps { +export interface IData { + key: string; + title: string; + index?: number; + [k: string]: any; +} + +export interface InfiniteListProps extends React.HtmlHTMLAttributes { type?: 'custom' | 'auto' prefixCls?: string; height?: number; - itemRender?: (data?: any) => React.ReactNode + itemRender?: (data?: IData, index?: number) => React.ReactNode loading?: boolean; // - data: Idata[]; + data: IData[]; targetId?: string; // 滚动列表 ID loadMore?: (data?: any) => any; params?: { @@ -28,101 +34,79 @@ export interface InfiniteListProps { hasMore: boolean; endMessage?: ReactNode loadingMessage?: ReactNode - onItemClick?: (data: any) => void; - searchCardProps?: SearchCardProps + loadingProps?: SpinProps } export interface InfiniteListRefProps { - + scrollViewSize?: { width: number; height: number } + listSize?: { width: number; height: number } } const InfiniteList = forwardRef((props, ref) => { const { prefixCls: customizePrefixCls, - height, + height = 600, + loading, type = 'auto', loadingMessage =

    加载中...

    , targetId = 'scrollableDiv', - itemRender, + itemRender = (data) =>
    {data?.title}
    , hasMore, - onItemClick, loadMore, data = [], - endMessage = 没有更多数据了...🤐, - searchCardProps + endMessage =
    没有更多数据了...🤐
    , + style, + loadingProps, + className } = props const { getPrefixCls } = useContext(ConfigContext); const componentName = getPrefixCls('biz-infinite-list', customizePrefixCls); const listRef = useRef(null); + const scrollRef = useRef(null); + const scrollViewSize = useSize(listRef.current) || { width: 0, height: 0 }; // 无限滚动视窗大小 + // @ts-ignore + const listSize = useSize(scrollRef.current?._infScroll) || { width: 0, height: 0 } // 无限滚动列表大小 + useEffect(() => { + // 当数据不够一屏时继续加载 + if (listSize.height < scrollViewSize.height) { + loadMore?.() + } + }, [listSize.height]) useImperativeHandle(ref, () => ({ - + scrollViewSize, + listSize })) return ( -
    - {/* {loading ? ( -

    加载中...

    - ) : ( - - {data?.list?.map((item) => ( - itemRender?.(item) || ( -
    - -
    - ) - ))} -
    - )} */} - {}} - hasMore={hasMore} - loader={loadingMessage} - endMessage={endMessage} - scrollableTarget={targetId} + +
    - + {}} + hasMore={hasMore} + loader={loadingMessage} + endMessage={endMessage} + scrollableTarget={targetId} + > {data?.map((item, idx) => ( - itemRender?.(item) || ( -
    { - onItemClick?.(item) - }} - > - -
    - ) + itemRender?.({ ...item, index: idx}) ))} -
    - - {/*
    - {!noMore && ( - - )} - - {noMore && 没有更多数据了} -
    */} -
    +
    +
    + ) }) diff --git a/packages/biz/src/infiniteList/components/index.tsx b/packages/biz/src/infiniteList/components/index.tsx deleted file mode 100644 index a096de9..0000000 --- a/packages/biz/src/infiniteList/components/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Created by jiangzhixiong on 2024/04/28 - */ - -export { default as SearchCard } from './SearchCard' -export type { SearchCardProps, SearchCardRefProps } from './SearchCard' diff --git a/packages/biz/src/infiniteList/demo/basic.tsx b/packages/biz/src/infiniteList/demo/basic.tsx index 1abb269..005efe6 100644 --- a/packages/biz/src/infiniteList/demo/basic.tsx +++ b/packages/biz/src/infiniteList/demo/basic.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react' -import { InfiniteList } from '@zhst/biz' +import { InfiniteList, CommonCard } from '@zhst/biz' +import { uniqueId } from '@zhst/func' export default () => { const [data, setData] = useState([]) @@ -13,8 +14,10 @@ export default () => { fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo') .then((res) => res.json()) .then((body) => { - let res = body.results.map(o => { + let res = body.results.map((o, index) => { return { + id: uniqueId(), + sort: index + 1, title: o.name.first, subtitle: o.name.last, url: o.picture.large @@ -36,14 +39,23 @@ export default () => { console.log('item点击:', _data)} - searchCardProps={{ - onAdd: (_data) => console.log('新增', _data), - onCreate: (_data) => console.log('创建', _data), - onRemove: (_data) => console.log('删除', _data), + itemRender={(item) => { + return ( + 创建检索, + 创建布控, + 删除点位 + ]} + /> + ) }} /> ) diff --git a/packages/biz/src/infiniteList/demo/custom.tsx b/packages/biz/src/infiniteList/demo/custom.tsx index 38310b7..ca698d7 100644 --- a/packages/biz/src/infiniteList/demo/custom.tsx +++ b/packages/biz/src/infiniteList/demo/custom.tsx @@ -3,11 +3,11 @@ import { InfiniteList } from '@zhst/biz' import { Button, Input, Space } from 'antd' export default () => { - const [data, setData] = useState([]) + const [data, setData] = useState([]) const [loading, setLoading] = useState(false) const [params, setParams] = useState({}) - const loadMoreData = (params?: { name: string; age?: number; sex: string; tel: number }) => { + const loadMoreData = (params?: any) => { if (loading) { return; } @@ -15,7 +15,7 @@ export default () => { fetch('https://randomuser.me/api/?results=10&inc=id,key,name,gender,email,nat,picture&noinfo') .then((res) => res.json()) .then((body) => { - let res = body.results.map(o => { + let res = body.results.map((o: { name: { first: any; last: any }; picture: { large: any } }) => { return { title: o.name.first, subtitle: o.name.last, @@ -35,7 +35,7 @@ export default () => { }, []); return ( - + setParams(pre => ({ ...pre, name: e.target.value }))} style={{ width: '120px' }} /> setParams(pre => ({ ...pre, age: e.target.value }))} style={{ width: '120px' }} /> @@ -49,9 +49,7 @@ export default () => { height={300} hasMore={data.length < 100} data={data} - type="custom" loadingMessage={} - onItemClick={data => console.log('item点击:', data)} /> ) diff --git a/packages/biz/src/infiniteList/index.md b/packages/biz/src/infiniteList/index.md index 186315c..2135cf3 100644 --- a/packages/biz/src/infiniteList/index.md +++ b/packages/biz/src/infiniteList/index.md @@ -18,21 +18,20 @@ group: | 参数 | 说明 | 类型 | 默认值 | 版本 | | --- | --- | --- | --- | --- | -| data | 数据源 | Idata[] | [] | - | +| data | 数据源 | IData[] | [] | - | +| height | 无限滚动列表可视区高度 | number | 600 | - | | loading | 数据源 | Array[] | [] | - | -| data | 数据源 | Array[] | [] | - | -| data | 数据源 | Array[] | [] | - | -| data | 数据源 | Array[] | [] | - | -| data | 数据源 | Array[] | [] | - | -| data | 数据源 | Array[] | [] | - | +| dataLength | 数据数量 | number | [] | - | +| next | 下一页方法 | function | () => {} | - | +| hasMore | 是否还有更多 | boolean | false | - | +| loadingProps | 参考 antd-spin | spinProps | [] | - | +| itemRender | 自定义渲染项 | (IData) => ReactNode | - | - | -## Idata +## 设计思路 -```js -interface Idata { - id?: string | number; - url?: string; // 链接 - title?: string; // 标题 - subtitle?: string; // 副标题 -} -``` +无限滚动,同时支持: + +1. 自动、主动加载更多 +2. 一屏没加载完,继续加载,直到填满屏幕: + - 需要第二次加载的内容是否为空,为空则停止加载 + - 通过整体的page-height 和 浏览器可视区域 diff --git a/packages/biz/src/infiniteList/type.ts b/packages/biz/src/infiniteList/type.ts new file mode 100644 index 0000000..e69de29 From d02a2b201532022e04371c96a6c1dc8d7cc051c1 Mon Sep 17 00:00:00 2001 From: jiangzhixiong <710328466@qq.com> Date: Tue, 21 May 2024 10:44:25 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat(biz):=20=E6=8F=90=E4=BA=A4=E6=89=93?= =?UTF-8?q?=E5=8C=85=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/biz/CHANGELOG.md | 6 ++++++ packages/biz/package.json | 2 +- packages/material/CHANGELOG.md | 7 +++++++ packages/material/package.json | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/biz/CHANGELOG.md b/packages/biz/CHANGELOG.md index 903e306..109ce61 100644 --- a/packages/biz/CHANGELOG.md +++ b/packages/biz/CHANGELOG.md @@ -1,5 +1,11 @@ # @zhst/biz +## 0.23.0 + +### Minor Changes + +- feat(biz 无限滚动组件): 添加无限滚动组件,屏幕自适应撑开 + ## 0.22.2 ### Patch Changes diff --git a/packages/biz/package.json b/packages/biz/package.json index c45796c..463d3f2 100644 --- a/packages/biz/package.json +++ b/packages/biz/package.json @@ -1,6 +1,6 @@ { "name": "@zhst/biz", - "version": "0.22.2", + "version": "0.23.0", "description": "业务库", "keywords": [ "business", diff --git a/packages/material/CHANGELOG.md b/packages/material/CHANGELOG.md index 5e46d4d..bc06eec 100644 --- a/packages/material/CHANGELOG.md +++ b/packages/material/CHANGELOG.md @@ -1,5 +1,12 @@ # @zhst/material +## 0.18.3 + +### Patch Changes + +- Updated dependencies + - @zhst/biz@0.23.0 + ## 0.18.2 ### Patch Changes diff --git a/packages/material/package.json b/packages/material/package.json index b9504df..e4a246c 100644 --- a/packages/material/package.json +++ b/packages/material/package.json @@ -1,6 +1,6 @@ { "name": "@zhst/material", - "version": "0.18.2", + "version": "0.18.3", "description": "物料库", "keywords": [ "business", From da3c1714c5a88dbfbf101e95ca8fa2d3aa16c95b Mon Sep 17 00:00:00 2001 From: jiangzhixiong <710328466@qq.com> Date: Thu, 23 May 2024 15:14:17 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(@zhst/biz):=20=E6=A0=91=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=94=AF=E6=8C=81tag=E9=9D=A2=E6=9D=BF=E3=80=81?= =?UTF-8?q?=E4=BC=98=E5=8C=96filter=E4=BC=A0=E5=8F=82=E3=80=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96option=E4=BC=A0=E5=8F=82=E3=80=81=E5=BA=9F=E5=BC=83?= =?UTF-8?q?=E4=B9=8B=E5=89=8D=E7=9A=84=E5=AE=9A=E5=88=B6=E5=8C=96=E6=96=B9?= =?UTF-8?q?=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/biz/CHANGELOG.md | 6 + packages/biz/package.json | 2 +- .../biz/src/boxSelectTree/boxSelectTree.tsx | 43 +- .../components/boxPanel/constants.ts | 0 .../components/boxPanel/index.less | 57 +++ .../components/boxPanel/index.tsx | 398 +++++++++++------- packages/biz/src/boxSelectTree/demo/basic.tsx | 3 +- .../src/boxSelectTree/demo/customFilter.tsx | 68 +++ .../src/boxSelectTree/demo/customOptions.tsx | 33 ++ .../biz/src/boxSelectTree/demo/extraBtns.tsx | 17 +- .../biz/src/boxSelectTree/demo/noFilter.tsx | 27 ++ .../biz/src/boxSelectTree/demo/noOptions.tsx | 3 +- .../src/boxSelectTree/demo/withTagPanel.tsx | 84 ++++ packages/biz/src/boxSelectTree/index.md | 46 +- packages/biz/src/tree/boxTree.tsx | 44 +- packages/biz/src/tree/index.less | 14 +- packages/material/CHANGELOG.md | 7 + packages/material/package.json | 2 +- 18 files changed, 606 insertions(+), 248 deletions(-) create mode 100644 packages/biz/src/boxSelectTree/components/boxPanel/constants.ts create mode 100644 packages/biz/src/boxSelectTree/demo/customFilter.tsx create mode 100644 packages/biz/src/boxSelectTree/demo/customOptions.tsx create mode 100644 packages/biz/src/boxSelectTree/demo/noFilter.tsx create mode 100644 packages/biz/src/boxSelectTree/demo/withTagPanel.tsx diff --git a/packages/biz/CHANGELOG.md b/packages/biz/CHANGELOG.md index 109ce61..cea2db4 100644 --- a/packages/biz/CHANGELOG.md +++ b/packages/biz/CHANGELOG.md @@ -1,5 +1,11 @@ # @zhst/biz +## 0.24.0 + +### Minor Changes + +- 树组件支持 tag 面板、优化 filter 传参、优化 option 传参、废弃之前的定制化方案 + ## 0.23.0 ### Minor Changes diff --git a/packages/biz/package.json b/packages/biz/package.json index 463d3f2..08240e5 100644 --- a/packages/biz/package.json +++ b/packages/biz/package.json @@ -1,6 +1,6 @@ { "name": "@zhst/biz", - "version": "0.23.0", + "version": "0.24.0", "description": "业务库", "keywords": [ "business", diff --git a/packages/biz/src/boxSelectTree/boxSelectTree.tsx b/packages/biz/src/boxSelectTree/boxSelectTree.tsx index b8b7484..e460842 100644 --- a/packages/biz/src/boxSelectTree/boxSelectTree.tsx +++ b/packages/biz/src/boxSelectTree/boxSelectTree.tsx @@ -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 = (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 = (props) => { {...tabsProps} /> + {footer}
    ); }; diff --git a/packages/biz/src/boxSelectTree/components/boxPanel/constants.ts b/packages/biz/src/boxSelectTree/components/boxPanel/constants.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/biz/src/boxSelectTree/components/boxPanel/index.less b/packages/biz/src/boxSelectTree/components/boxPanel/index.less index 0567c7f..73c02e3 100644 --- a/packages/biz/src/boxSelectTree/components/boxPanel/index.less +++ b/packages/biz/src/boxSelectTree/components/boxPanel/index.less @@ -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; } diff --git a/packages/biz/src/boxSelectTree/components/boxPanel/index.tsx b/packages/biz/src/boxSelectTree/components/boxPanel/index.tsx index 2168c40..299b2b1 100644 --- a/packages/biz/src/boxSelectTree/components/boxPanel/index.tsx +++ b/packages/biz/src/boxSelectTree/components/boxPanel/index.tsx @@ -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 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 = (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: , + onClick: () => onImport?.() + }, + { + label: '新建组', + key: 'add', + icon: , + onClick: () => onCreate?.() + }, + { + label: '删除', + key: 'del', + icon: , + onClick: () => onBoxBatchDelete?.(), + props: { + danger: true + } + } + ], + filterList = [ + { + label: '多选', + key: 'multi', + icon: isTreeCheckable ? : , + onClick: () => onBatch?.() || handleCheckable() + }, + { + label: '时钟', + key: 'clock', + icon: , + 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([]); - const [boxChoiceOpen, setBoxChoiceOpen] = useState(false) - const [checkedKeys, setCheckedKeys] = useState([]); - const createFormRef = useRef< - ProFormInstance<{ - name: string; - boxList?: any[]; - }> - >() /** * 修改选择状态 @@ -77,151 +164,154 @@ const BoxPanel: FC = (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 ( + + {dom} + + ) + } + + return _list?.map(item => ( + + {item.type === 'dropdown' ? ( + WithDropdown( + + + {idx !== _list.length - 1 && } + + )) + } + + /** + * 初始化标签面板 + * @param _tagList + * @param sort 是否分类 + * @returns + */ + const initTagPanel = (_tagList: BoxPanelProps['tagList'], sort?: boolean) => { + // 正常标签渲染 + const commonTag = (_tagProps: ITag) => ( + onTagCheck?.(_tagProps.value, _tagProps)} + >{_tagProps.label} + ) + // 包装父级标签 + const _withFather = (tag: ITag) => ( +
    + {tag.label} + {tag.children?.map?.(_tag => commonTag(_tag))} +
    + ) + + 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 (
    - {/* 盒子选择弹框 */} - 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} // 右侧点击删除事件 - /> -
    - onSearch?.(e)} - placeholder='请输入盒子名称' - {...searchInputProps} - /> - {customImport || ( -
    -
    - )} -
    - {/* 是否显示操作按钮 */} + {/* 搜索栏 */} + {showSearchBar && ( +
    + onSearch?.(e)} + placeholder='请输入盒子名称' + {...searchInputProps} + /> + {customFilter || (!noFilter && ( +
    + {/* @ts-ignore */} + {initFilter(filterList)} +
    + ))} +
    + )} + {/* 默认操作按钮 */} {showOptions && ( <>
    - - - {onCreate ? - ( - - ) : ( - - className={classNames(componentName + '-create-modal')} - open={onCreate ? false : undefined} - formRef={createFormRef} - title="新建组" - modalProps={{ destroyOnClose: true }} - layout='horizontal' - labelCol={{ span: 6 }} - wrapperCol={{ span: 18 }} - trigger={} - submitter={{ - searchConfig: { - submitText: '确定', - resetText: '取消', - }, - }} - onFinish={onCreateSubmit} - > - - - { - createFormRef.current?.setFieldValue('boxList', null) - onBoxChoiceReset() - }} >恢复默认 - setBoxChoiceOpen(true)}>范围选择 - - ) - }} - /> - - ) - } - - {/* @ts-ignore */} - + {initOptions(optionList)}
    )} {extraBtns} + {showTagPanel && ( +
    + 标签: + {initTagPanel(tagList, tagExpandAll)} +
    + {tagExpandAll && ( + 重置 + )} + {tagExpandAll ? '收起' : '更多'} +
    + {tagFootRender} +
    + )} { onItemRenameFinish: async (val, data) => console.log('盒子重命名提交(返回boolean,控制弹框显示\隐藏)', val, data), checkedKeys, }} + footer={} />
    ); diff --git a/packages/biz/src/boxSelectTree/demo/customFilter.tsx b/packages/biz/src/boxSelectTree/demo/customFilter.tsx new file mode 100644 index 0000000..73b9c76 --- /dev/null +++ b/packages/biz/src/boxSelectTree/demo/customFilter.tsx @@ -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([]); + + return ( +
    + , + type: 'dropdown', + showTooltip: false, + dropdownConfig: { + menu: { + // 自定义返回项 + _internalRenderMenuItem: (originNode, menuItemProps, stateProps) => { + return ( +
    + {originNode} +
    + ) + }, + selectable: true, + items: [ + { + label:

    全部

    , + key: 'all', + onClick: () => console.log('多选1') + }, + { + icon: , + label: '多选1', + key: 'multi1', + onClick: () => console.log('多选1') + }, + { + label: '多选2', + icon: , + key: 'multi2', + }, + ], + } + } + }, + ]} + /> +
    + ); +}; + +export default demo; diff --git a/packages/biz/src/boxSelectTree/demo/customOptions.tsx b/packages/biz/src/boxSelectTree/demo/customOptions.tsx new file mode 100644 index 0000000..85197ab --- /dev/null +++ b/packages/biz/src/boxSelectTree/demo/customOptions.tsx @@ -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([]); + + return ( +
    + , + } + ]} + /> +
    + ); +}; + +export default demo; diff --git a/packages/biz/src/boxSelectTree/demo/extraBtns.tsx b/packages/biz/src/boxSelectTree/demo/extraBtns.tsx index d889b8b..2c01e2b 100644 --- a/packages/biz/src/boxSelectTree/demo/extraBtns.tsx +++ b/packages/biz/src/boxSelectTree/demo/extraBtns.tsx @@ -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([]); + const [isTreeCheckable, setIsTreeCheckable] = useState(false) return (
    @@ -13,10 +15,23 @@ const demo = () => { data={activeKey === '1' ? treeData : boxDataSource} boxDataSource={boxDataSource} showOptions={false} - extraBtns={} + extraBtns={
    Hello Lambo
    } tabsProps={{ activeKey, }} + filterList={[ + { + label: '多选', + key: 'multi', + icon: isTreeCheckable ? : , + onClick: () => setIsTreeCheckable(pre => !pre) + }, + { + label: '时钟', + key: 'clock', + icon: , + } + ]} treeProps={{ checkedKeys }} diff --git a/packages/biz/src/boxSelectTree/demo/noFilter.tsx b/packages/biz/src/boxSelectTree/demo/noFilter.tsx new file mode 100644 index 0000000..349e87d --- /dev/null +++ b/packages/biz/src/boxSelectTree/demo/noFilter.tsx @@ -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([]); + + return ( +
    + +
    + ); +}; + +export default demo; diff --git a/packages/biz/src/boxSelectTree/demo/noOptions.tsx b/packages/biz/src/boxSelectTree/demo/noOptions.tsx index 304bb73..9b4b0b0 100644 --- a/packages/biz/src/boxSelectTree/demo/noOptions.tsx +++ b/packages/biz/src/boxSelectTree/demo/noOptions.tsx @@ -30,7 +30,7 @@ const demo = () => { tabsProps={{ activeKey, }} - customImport={