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.

339 lines
9.3 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { ReloadOutlined, SettingOutlined } from '@ant-design/icons-vue'
import type { TableColumnType } from 'ant-design-vue'
import { Tooltip } from 'ant-design-vue'
import type { ListToolBarProps } from '../ListToolBar'
import ListToolBar from '../ListToolBar'
// import ColumnSetting from '../ColumnSetting'
import './index.less'
import FullScreenIcon from './FullscreenIcon'
import DensityIcon from './DensityIcon'
import type { ActionType, ProTableProps, OptionSearchProps, LabelTooltipType } from '../../typing'
import { useIntl } from '#/provider'
import type { IntlType } from '#/provider'
import { VueNodeOrRenderPropType, VueNodePropType } from '#/types'
import type { VueKey, VueNode, VueNodeOrRender } from '#/types'
import { computed, defineComponent, watchEffect } from 'vue'
import type { ExtractPropTypes, PropType, Ref } from 'vue'
import type { ChangeEvent } from 'ant-design-vue/es/_util/EventInterface'
import omitUndefined from '../../../utils/omitUndefined'
import { useContainer } from '#/table/container'
import { getRender } from '#/layout/utils'
import type { ToolBarRender } from '#/table/renderTypes'
import ColumnSetting from '#/table/components/ColumnSetting'
export type SettingOptionType = {
draggable?: boolean
checkable?: boolean
checkedReset?: boolean
listsHeight?: number
extra?: VueNode
children?: VueNode
}
export type OptionConfig = {
density?: boolean
fullScreen?: OptionsType
reload?: OptionsType
setting?: boolean | SettingOptionType
search?: (OptionSearchProps & { name?: string }) | boolean
}
export type OptionsFunctionType = (e: MouseEvent, action?: ActionType) => void
export type OptionsType = OptionsFunctionType | boolean
export const toolBarProps = () => ({
headerTitle: VueNodeOrRenderPropType as PropType<VueNodeOrRender>,
tooltip: [String, Object] as PropType<LabelTooltipType>,
toolbar: Object as PropType<ListToolBarProps>,
toolBarRender: Function as PropType<ToolBarRender>,
action: {
type: Object as PropType<Ref<ActionType | undefined>>,
default: () => ref({})
},
options: {
type: [Object, Boolean] as PropType<OptionConfig | false>,
default: undefined
},
selectedRowKeys: {
type: Array as PropType<VueKey[]>,
default: () => []
},
selectedRows: {
type: Array as PropType<unknown[]>
},
onSearch: Function as PropType<(keyWords: string) => void>,
columns: {
type: Array as PropType<TableColumnType<unknown>[]>,
default: () => []
}
})
export type ToolBarProps<T = unknown> = {
headerTitle?: VueNode
tooltip?: string | LabelTooltipType
toolbar?: ListToolBarProps
toolBarRender?: ToolBarRender<T>
action: Ref<ActionType | undefined>
options?: OptionConfig | false
selectedRowKeys?: (string | number)[]
selectedRows?: T[]
onSearch?: (keyWords: string) => void
columns: TableColumnType<T>[]
}
function getButtonText({
intl
}: OptionConfig & {
intl: IntlType
}) {
return {
reload: {
text: intl.getMessage('tableToolBar.reload', '刷新'),
icon: <ReloadOutlined />
},
density: {
text: intl.getMessage('tableToolBar.density', '表格密度'),
icon: <DensityIcon />
},
setting: {
text: intl.getMessage('tableToolBar.columnSetting', '列设置'),
icon: <SettingOutlined />
},
fullScreen: {
text: intl.getMessage('tableToolBar.fullScreen', '全屏'),
icon: <FullScreenIcon />
}
}
}
/**
* 渲染默认的 工具栏
*/
function renderDefaultOption<T>(
options: OptionConfig,
defaultOptions: OptionConfig & {
intl: IntlType
},
actions: Ref<ActionType | undefined>,
columns: TableColumnType<T>[]
) {
return Object.keys(options)
.filter(item => item)
.map(key => {
// @ts-ignore
const value = options[key]
if (!value) {
return null
}
let onClick: OptionsFunctionType =
// @ts-ignore
value === true ? defaultOptions[key] : event => value?.(event, actions.value)
if (typeof onClick !== 'function') {
onClick = () => ({})
}
if (key === 'setting') {
// @ts-ignore
return <ColumnSetting {...options[key]} columns={columns} key={key} />
}
if (key === 'fullScreen') {
return (
<span key={key} onClick={onClick}>
<FullScreenIcon />
</span>
)
}
// @ts-ignore
const optionItem = getButtonText(defaultOptions)[key]
if (optionItem) {
return (
<span key={key} onClick={onClick}>
<Tooltip title={optionItem.text}>{optionItem.icon}</Tooltip>
</span>
)
}
return null
})
.filter(item => item)
}
// eslint-disable-next-line vue/one-component-per-file
const ToolBar = defineComponent({
name: 'ToolBar',
props: toolBarProps(),
setup(props, { slots }) {
const counter = useContainer()!
const intl = useIntl()
const defaultOptions = computed(() => ({
reload: () => props.action?.value?.reload(),
density: true,
setting: true,
search: false,
fullScreen: () => props.action?.value?.fullScreen?.()
}))
const searchConfig = computed(() => {
if (!props.options) {
return false
}
if (!props.options.search) return false
/** 受控的value 和 onChange */
const defaultSearchConfig = {
value: counter.keyWords.value,
onChange: (e: ChangeEvent) => counter.setKeyWords(e.target.value)
}
if (props.options.search === true) return defaultSearchConfig
return {
...defaultSearchConfig,
...props.options.search
}
})
watchEffect(() => {
if (counter.keyWords === undefined) {
props.onSearch?.('')
}
})
const optionDom = computed(() => {
if (props.options === false) {
return []
}
const options = {
...defaultOptions.value,
fullScreen: true,
...props.options
}
return renderDefaultOption(
options,
{
...defaultOptions.value,
intl
},
props.action,
props.columns
)
})
// 操作列表
const actions = computed(() => {
const toolBarRender = getRender<ToolBarRender>(props, slots, 'toolBarRender')
return toolBarRender
? toolBarRender(props.action.value, {
selectedRowKeys: props.selectedRowKeys,
selectedRows: props.selectedRows
})
: []
})
const titleDom = computed(() => {
const headerRender = getRender<VueNode>(props, slots, 'headerTitle')
if (typeof headerRender === 'function') {
// @ts-ignore
return headerRender()
} else {
return headerRender
}
})
return () => (
<ListToolBar
title={titleDom.value}
tooltip={props.tooltip}
search={searchConfig.value}
onSearch={props.onSearch}
actions={actions.value}
settings={optionDom.value}
{...toolbar}
/>
)
}
})
const toolbarRenderProps = () => ({
hideToolbar: { type: Boolean, default: false },
onFormSearchSubmit: Function as PropType<(params: any) => void>,
searchNode: VueNodePropType as PropType<VueNode>,
tableColumn: { type: Array as PropType<any[]>, default: () => [] },
tooltip: [String, Object] as PropType<string | LabelTooltipType>,
selectedRows: Array as PropType<any[]>,
selectedRowKeys: Array as PropType<VueKey[]>,
headerTitle: VueNodeOrRenderPropType as PropType<VueNodeOrRender>,
toolbar: Object as PropType<ProTableProps['toolbar']>,
options: [Object, Boolean] as PropType<ProTableProps['options']>,
toolBarRender: Function as PropType<ToolBarProps['toolBarRender']>,
actionRef: Object as PropType<Ref<ActionType | undefined>>
})
export type ToolbarRenderProps = Partial<ExtractPropTypes<ReturnType<typeof toolbarRenderProps>>>
/** 这里负责与table交互并且减少 render次数 */
// eslint-disable-next-line vue/one-component-per-file
const ToolbarRender = defineComponent({
name: 'ToolbarRender',
props: toolbarRenderProps(),
setup(props, { slots }) {
const onSearch = (keyword: string) => {
if (!props.options || !props.options.search) {
return
}
const { name = 'keyword' } = props.options.search === true ? {} : props.options.search
/** 如果传入的 onSearch 返回值为 false应该直接拦截请求 */
const success = (props.options.search as OptionSearchProps)?.onSearch?.(keyword)
if (success === false) return
// 查询的时候的回到第一页
props.actionRef?.value?.setPageInfo?.({
current: 1
})
props.onFormSearchSubmit?.(
omitUndefined({
_timestamp: Date.now(),
[name]: keyword
})
)
}
return () => {
// 不展示 toolbar
if (props.hideToolbar) {
return null
}
return (
<ToolBar
tooltip={props.tooltip}
columns={props.tableColumn}
options={props.options}
headerTitle={props.headerTitle}
action={props.actionRef}
onSearch={onSearch}
selectedRows={props.selectedRows}
selectedRowKeys={props.selectedRowKeys}
toolBarRender={props.toolBarRender}
toolbar={{
filter: props.searchNode,
...toolbar
}}
>
{slots}
</ToolBar>
)
}
}
})
export default ToolbarRender