You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

296 lines
7.8 KiB
TypeScript

1 year ago
import type { TabPaneProps } from 'ant-design-vue'
import { Tooltip, Space, Tabs } from 'ant-design-vue'
import type { ListToolBarHeaderMenuProps } from './HeaderMenu'
import HeaderMenu from './HeaderMenu'
import './index.less'
import type { CSSProperties, FunctionalComponent } from 'vue'
import type { VueKey, VueNode } from '#/types'
import useMediaQuery from '../../../utils/hooks/useMediaQuery'
import type { LabelTooltipType, SearchProps } from '../../typing'
import { getPrefixCls } from '#/layout/RouteContext'
import { computed } from 'vue'
import LabelIconTip from '../../../utils/components/LabelIconTip'
import { isValidElement } from '#/utils/isValidElement'
export type ListToolBarSetting = {
icon: VueNode
tooltip?: LabelTooltipType | string
key?: string
onClick?: (key?: string) => void
}
/** Antd 默认直接导出了 rc 组件中的 Tab.Pane 组件。 */
type TabPane = TabPaneProps & {
key?: string
}
export type ListToolBarTabs = {
activeKey?: string
onChange?: (activeKey: VueKey) => void
items?: TabPane[]
}
export type ListToolBarMenu = ListToolBarHeaderMenuProps
type SearchPropType = SearchProps | VueNode | boolean
type SettingPropType = VueNode | ListToolBarSetting
export type ListToolBarProps = {
prefixCls?: string
/** 标题 */
title?: VueNode
/** 副标题 */
subTitle?: VueNode
/** 标题提示 */
tooltip?: string | LabelTooltipType
/** 搜索输入栏相关配置 */
search?: SearchPropType
/** 搜索回调 */
onSearch?: (keyWords: string) => void
/** 工具栏右侧操作区 */
actions?: VueNode[]
/** 工作栏右侧设置区 */
settings?: SettingPropType[]
/** 是否多行展示 */
multipleLine?: boolean
/** 过滤区,通常配合 LightFilter 使用 */
filter?: VueNode
/** 标签页配置,仅当 `multipleLine` 为 true 时有效 */
tabs?: ListToolBarTabs
/** 菜单配置 */
menu?: ListToolBarMenu
}
/**
* DOM Item
*
* @param setting
*/
function getSettingItem(setting: SettingPropType) {
if (isValidElement(setting)) {
return setting
}
if (setting) {
const settingConfig: ListToolBarSetting = setting as ListToolBarSetting
const { icon, tooltip, onClick, key } = settingConfig
if (icon && tooltip) {
return (
<Tooltip title={tooltip as VueNode}>
<span
key={key}
onClick={() => {
if (onClick) {
onClick(key)
}
}}
>
{icon}
</span>
</Tooltip>
)
}
return icon
}
return null
}
const ListToolBarTabBar: FunctionalComponent<{
prefixCls: string
filtersNode: VueNode
multipleLine: boolean
tabs: ListToolBarProps['tabs']
}> = ({ prefixCls, tabs = {}, multipleLine, filtersNode }) => {
if (!multipleLine) return null
return (
<div class={`${prefixCls}-extra-line`}>
{tabs.items && tabs.items.length ? (
<Tabs activeKey={tabs.activeKey} onChange={tabs.onChange} tabBarExtraContent={filtersNode}>
{tabs.items.map((tab, index) => (
<Tabs.TabPane key={tab.key || index} {...tab} />
))}
</Tabs>
) : (
filtersNode
)}
</div>
)
}
const ListToolBar: FunctionalComponent<ListToolBarProps> = (
{
prefixCls: customizePrefixCls,
title,
subTitle,
tooltip,
// search,
// onSearch,
multipleLine = false,
filter,
actions = [],
settings = [],
tabs = {},
menu
},
{ attrs }
) => {
// const intl = useIntl()
const colSize = useMediaQuery()
const isMobile = computed(() => colSize.value === 'sm' || colSize.value === 'xs')
// const placeholder = intl.getMessage('tableForm.inputPlaceholder', '请输入')
/**
* DOM
*
* @param search
*/
const searchNode = null
// const searchNode = useMemo(() => {
// if (!search) {
// return null
// }
// if (React.isValidElement(search)) {
// return search
// }
// return (
// <Input.Search
// style={{ width: 200 }}
// placeholder={placeholder}
// {...(search as SearchProps)}
// onSearch={(...restParams) => {
// onSearch?.(restParams?.[0])
// ;(search as SearchProps).onSearch?.(...restParams)
// }}
// />
// )
// }, [placeholder, onSearch, search])
const prefixCls = getPrefixCls('pro-table-list-toolbar', customizePrefixCls)
/** 轻量筛选组件 */
const filtersNode = computed(() => {
if (filter) return <div class={`${prefixCls}-filter`}>{filter}</div>
return null
})
/** 有没有 title需要结合多个场景判断 */
const hasTitle = computed(() => menu || title || subTitle || tooltip)
/** 没有 key 的时候帮忙加一下 key 不加的话很烦人 */
const actionDom = computed(() => {
if (!Array.isArray(actions)) {
return actions
}
if (actions.length < 1) {
return null
}
return <Space align="center">{actions}</Space>
})
const hasRight = computed(() => {
return (
(hasTitle.value && searchNode) ||
(!multipleLine && filtersNode) ||
actionDom ||
settings?.length
)
})
const hasLeft = computed(
() => tooltip || title || subTitle || menu || (!hasTitle.value && searchNode)
)
const leftTitleDom = computed(() => {
// 保留dom是为了占位不然 right 就变到左边了
if (!hasLeft.value && hasRight.value) {
return <div class={`${prefixCls}-left`} />
}
// 减少 space 的dom渲染的时候能节省点性能
if (!menu && (hasTitle.value || !searchNode)) {
return (
<div class={`${prefixCls}-left`}>
<div class={`${prefixCls}-title`}>
<LabelIconTip tooltip={tooltip} label={title} subTitle={subTitle} />
</div>
</div>
)
}
return (
<Space class={`${prefixCls}-left`}>
{hasTitle.value && !menu && (
<div class={`${prefixCls}-title`}>
<LabelIconTip tooltip={tooltip} label={title} subTitle={subTitle} />
</div>
)}
{menu && <HeaderMenu {...menu} prefixCls={prefixCls} />}
{!hasTitle.value && searchNode ? (
<div class={`${prefixCls}-search`}>{searchNode}</div>
) : null}
</Space>
)
})
const rightTitleDom = computed(() => {
if (!hasRight.value) return null
return (
<Space
class={`${prefixCls}-right`}
direction={isMobile.value ? 'vertical' : 'horizontal'}
size={16}
align={isMobile.value ? 'end' : 'center'}
>
{hasTitle.value && searchNode ? (
<div class={`${prefixCls}-search`}>{searchNode}</div>
) : null}
{!multipleLine ? filtersNode.value : null}
{actionDom.value}
{settings?.length ? (
<Space size={12} align="center" class={`${prefixCls}-setting-items`}>
{settings.map((setting, index) => {
const settingItem = getSettingItem(setting)
return (
<div key={index} class={`${prefixCls}-setting-item`}>
{settingItem}
</div>
)
})}
</Space>
) : null}
</Space>
)
})
const titleNode = computed(() => {
if (!hasRight.value && !hasLeft.value) return null
const containerClassName = [
`${prefixCls}-container`,
{
[`${prefixCls}-container-mobile`]: isMobile.value
}
]
return (
<div class={containerClassName}>
{leftTitleDom.value}
{rightTitleDom.value}
</div>
)
})
return (
<div style={attrs.style as CSSProperties} class={[`${prefixCls}`, attrs.class]}>
{titleNode.value}
<ListToolBarTabBar
filtersNode={filtersNode.value}
prefixCls={prefixCls}
tabs={tabs}
multipleLine={multipleLine}
/>
</div>
)
}
export default ListToolBar