diff --git a/packages/pro/search/demo/CustomLabel.md b/packages/pro/search/demo/CustomLabel.md new file mode 100644 index 000000000..dd06c660d --- /dev/null +++ b/packages/pro/search/demo/CustomLabel.md @@ -0,0 +1,16 @@ +--- +order: 5 +title: + zh: 自定义Label + en: Custom Label +--- + +## zh + +使用 `customNameLabel` 自定义 `name` 下拉选择框的 label。 +使用 `searchField.customOperatorLabel` 自定义 `operator` 下拉选择框的 label。 + +## en + +Customize label of `name` select panel via `customNameLabel`. +Customize label of `operator` select panel via `searchField.customOperatorLabel`. diff --git a/packages/pro/search/demo/CustomLabel.vue b/packages/pro/search/demo/CustomLabel.vue new file mode 100644 index 000000000..af324c121 --- /dev/null +++ b/packages/pro/search/demo/CustomLabel.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/packages/pro/search/docs/Api.zh.md b/packages/pro/search/docs/Api.zh.md index 7a8919a68..ed026e62e 100644 --- a/packages/pro/search/docs/Api.zh.md +++ b/packages/pro/search/docs/Api.zh.md @@ -9,6 +9,7 @@ | `v-model:errors` | 校验错误 | `{ index: number, message: string }` | - | - | - | | `clearable` | 是否可清除 | `boolean` | `true` | ✅ | - | | `clearIcon` | 清除图标 | `string \| VNode \| #clearIcon` | `close-circle` | ✅ | - | +| `customNameLabel` | 自定义搜索项名称下拉选择label | `string \| ((searchField: SearchField) => VNodeChild \| #nameLabel` | - | - | - | | `disabled` | 是否禁用 | `boolean` | `false` | - | - | | `overlayContainer` | 自定义浮层容器节点 | `string \| HTMLElement \| (trigger?: Element) => string \| HTMLElement` | - | ✅ | - | | `placeholder` | 默认文本 | `string` | - | - | - | @@ -65,6 +66,7 @@ interface SearchItemConfirmContext extends Partial> | `operators` | 搜索条件的中间操作符 | `string[]` | - | - | 提供时,会在搜索词条名称中间增加一个操作符,如 `'='`, `'!='` | | `defaultOperator` | 默认的操作符 | `string` | - | - | 提供时,会自动填入默认的操作符 | | `defaultValue` | 默认值 | - | - | - | 提供时,会自动填入默认值 | +| `customOperatorLabel` | 自定义操作符下拉选择label | `string \| ((operator: string) => VNodeChild)` | - | - | - | | `inputClassName` | 输入框class | `string` | - | - | 用于自定义输入框样式 | | `placeholder` | 输入框placeholder | `string` | - | - | 搜索值输入框的占位符 | | `validator` | 搜索项校验函数 | `(value: SearchValue) => { message?: string } | undefined` | - | - | 返回错误信息 | diff --git a/packages/pro/search/src/ProSearch.tsx b/packages/pro/search/src/ProSearch.tsx index 122fe3bf5..dc9762efd 100644 --- a/packages/pro/search/src/ProSearch.tsx +++ b/packages/pro/search/src/ProSearch.tsx @@ -134,7 +134,6 @@ export default defineComponent({ provide(proSearchContext, { props, - slots, locale: locale.search, mergedPrefixCls, commonOverlayProps, @@ -184,7 +183,7 @@ export default defineComponent({ />
{searchItems.value?.map(item => ( - + ))}
{searchValueEmpty.value && !activeSegment.value && ( diff --git a/packages/pro/search/src/composables/useSearchItem.ts b/packages/pro/search/src/composables/useSearchItem.ts index 5dc7211af..820b79568 100644 --- a/packages/pro/search/src/composables/useSearchItem.ts +++ b/packages/pro/search/src/composables/useSearchItem.ts @@ -38,7 +38,12 @@ export function useSearchItems( ) const searchField = searchFields?.find(field => field.key === searchState.fieldKey) const operatorSegment = searchField && createOperatorSegment(mergedPrefixCls.value, searchField) - const nameSegment = createNameSegment(mergedPrefixCls.value, searchFields, !operatorSegment) + const nameSegment = createNameSegment( + mergedPrefixCls.value, + searchFields, + props.customNameLabel, + !operatorSegment, + ) return { key: searchState.key, diff --git a/packages/pro/search/src/panel/SelectPanel.tsx b/packages/pro/search/src/panel/SelectPanel.tsx index 1fdcb7456..601819399 100644 --- a/packages/pro/search/src/panel/SelectPanel.tsx +++ b/packages/pro/search/src/panel/SelectPanel.tsx @@ -29,7 +29,7 @@ import { filterDataSource, matchRule } from '../utils/selectData' export default defineComponent({ props: proSearchSelectPanelProps, - setup(props) { + setup(props, { slots }) { const { locale, mergedPrefixCls } = inject(proSearchContext)! const [activeValue, setActiveValue] = useState(undefined) const partiallySelected = computed(() => props.value && props.value.length > 0 && !props.allSelected) @@ -159,7 +159,7 @@ export default defineComponent({ return (
evt.preventDefault()}> {renderSelectAll()} - + {renderFooter()}
) diff --git a/packages/pro/search/src/searchItem/SearchItem.tsx b/packages/pro/search/src/searchItem/SearchItem.tsx index b82790752..df8ad91d6 100644 --- a/packages/pro/search/src/searchItem/SearchItem.tsx +++ b/packages/pro/search/src/searchItem/SearchItem.tsx @@ -17,7 +17,7 @@ import { searchItemProps } from '../types' export default defineComponent({ props: searchItemProps, - setup(props) { + setup(props, { slots }) { const context = inject(proSearchContext)! const { props: proSearchProps, mergedPrefixCls, activeSegment } = context @@ -63,6 +63,7 @@ export default defineComponent({ {segmentRenderDatas.value.map(segment => ( () @@ -189,6 +189,7 @@ export default defineComponent({ const renderContent = () => { const contentNode = props.segment.panelRenderer?.({ + slots, input: props.input ?? '', value: props.value, cancel: handleCancel, diff --git a/packages/pro/search/src/segments/CreateCascaderSegment.tsx b/packages/pro/search/src/segments/CreateCascaderSegment.tsx index dd8a1438f..7ea4a9ce8 100644 --- a/packages/pro/search/src/segments/CreateCascaderSegment.tsx +++ b/packages/pro/search/src/segments/CreateCascaderSegment.tsx @@ -88,7 +88,7 @@ export function createCascaderSegment( expandIcon={expandIcon} expandTrigger={expandTrigger} fullPath={fullPath ?? defaultFullPath} - strategy={cascaderStrategy} + strategy={mergedCascaderStrategy} separator={pathSeparator} multiple={multiple} virtual={virtual} @@ -197,8 +197,6 @@ function getKeyByLabels( const checkedKeys = checkedKeysResolver.appendKeys([], keys) - console.log(keys, checkedKeys) - if (!pathSeparator) { return checkedKeys } @@ -211,7 +209,6 @@ function getKeyByLabels( keys.unshift((currentKey = parentKeyMap.get(currentKey)!)) } - console.log('getKeys', keys) return keys } diff --git a/packages/pro/search/src/segments/CreateNameSegment.tsx b/packages/pro/search/src/segments/CreateNameSegment.tsx index 44efa41d2..7cc1bbf78 100644 --- a/packages/pro/search/src/segments/CreateNameSegment.tsx +++ b/packages/pro/search/src/segments/CreateNameSegment.tsx @@ -5,7 +5,10 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import type { PanelRenderContext, SearchField, Segment } from '../types' +import type { PanelRenderContext, SearchField, Segment, SelectPanelData } from '../types' +import type { VNodeChild } from 'vue' + +import { isString } from 'lodash-es' import { type VKey, convertArray } from '@idux/cdk/utils' @@ -17,11 +20,12 @@ export const defaultNameSegmentEndSymbol = ':' export function createNameSegment( prefixCls: string, searchFields: SearchField[] | undefined, + customNameLabel: string | ((searchField: SearchField) => VNodeChild) | undefined, showEndSymbol: boolean, ): Segment { const names = getSearchOptionNameList(searchFields ?? []) const panelRenderer = (context: PanelRenderContext) => { - const { input, value, setValue, ok, setOnKeyDown } = context + const { slots, input, value, setValue, ok, setOnKeyDown } = context const handleChange = (value: VKey[]) => { setValue(value[0]) ok() @@ -31,9 +35,27 @@ export function createNameSegment( return } + const renderNameLabel = (key: VKey, renderer?: (searchField: SearchField) => VNodeChild) => { + if (!renderer) { + return undefined + } + + const searchField = searchFields!.find(field => field.key === key)! + return renderer(searchField) + } + + const _customNameLabel = customNameLabel ?? 'nameLabel' + + const panelSlots = { + optionLabel: isString(_customNameLabel) + ? (option: SelectPanelData) => renderNameLabel(option.key, slots[_customNameLabel]) + : (option: SelectPanelData) => renderNameLabel(option.key, _customNameLabel), + } + return ( | undefined { - if (!searchField.operators || searchField.operators.length <= 0) { + const { operators, defaultOperator, customOperatorLabel } = searchField + if (!operators || operators.length <= 0) { return } const panelRenderer = (context: PanelRenderContext) => { - const { value, setValue, ok, setOnKeyDown } = context + const { slots, value, setValue, ok, setOnKeyDown } = context const handleChange = (value: string[]) => { setValue(value[0]) ok() } + + const panelSlots = { + optionLabel: + customOperatorLabel && + (isString(customOperatorLabel) + ? (option: SelectPanelData) => slots[customOperatorLabel]?.(option.key) + : (option: SelectPanelData) => customOperatorLabel?.(option.key as string)), + } + return ( ({ key: operator, label: operator }))} multiple={false} @@ -41,7 +54,7 @@ export function createOperatorSegment( return { name: 'operator', inputClassName: `${prefixCls}-operator-segment-input`, - defaultValue: searchField.operators.find(op => op === searchField.defaultOperator), + defaultValue: operators.find(op => op === defaultOperator), parse: input => parseInput(input, searchField), format: value => value ?? '', panelRenderer, diff --git a/packages/pro/search/src/token.ts b/packages/pro/search/src/token.ts index afb34503c..0deb6b7ad 100644 --- a/packages/pro/search/src/token.ts +++ b/packages/pro/search/src/token.ts @@ -14,7 +14,7 @@ import type { SegmentStatesContext } from './composables/useSegmentStates' import type { ProSearchProps } from './types' import type { ɵOverlayProps } from '@idux/components/_private/overlay' import type { ProSearchLocale } from '@idux/pro/locales' -import type { ComputedRef, InjectionKey, Slots } from 'vue' +import type { ComputedRef, InjectionKey } from 'vue' export interface ProSearchContext extends SearchStateContext, @@ -22,7 +22,6 @@ export interface ProSearchContext ActiveSegmentContext, SearchTriggerContext { props: ProSearchProps - slots: Slots locale: ProSearchLocale mergedPrefixCls: ComputedRef commonOverlayProps: ComputedRef<ɵOverlayProps> diff --git a/packages/pro/search/src/types/proSearch.ts b/packages/pro/search/src/types/proSearch.ts index 9d10c2443..04c5b9646 100644 --- a/packages/pro/search/src/types/proSearch.ts +++ b/packages/pro/search/src/types/proSearch.ts @@ -10,7 +10,7 @@ import type { SearchItemConfirmContext, SearchItemError } from './searchItem' import type { SearchValue } from './searchValue' import type { ExtractInnerPropTypes, ExtractPublicPropTypes, MaybeArray } from '@idux/cdk/utils' import type { OverlayContainerType } from '@idux/components/utils' -import type { DefineComponent, HTMLAttributes, PropType, VNode } from 'vue' +import type { DefineComponent, HTMLAttributes, PropType, VNode, VNodeChild } from 'vue' export const proSearchProps = { value: Array as PropType, @@ -19,6 +19,7 @@ export const proSearchProps = { default: undefined, }, clearIcon: [String, Object] as PropType, + customNameLabel: [String, Function] as PropType VNodeChild)>, maxLabel: { type: [Number, String] as PropType, default: 'responsive' }, searchIcon: [String, Object] as PropType, disabled: { diff --git a/packages/pro/search/src/types/searchFields.ts b/packages/pro/search/src/types/searchFields.ts index 3301123ab..1a4a40292 100644 --- a/packages/pro/search/src/types/searchFields.ts +++ b/packages/pro/search/src/types/searchFields.ts @@ -26,6 +26,7 @@ interface SearchFieldBase { defaultValue?: V inputClassName?: string placeholder?: string + customOperatorLabel?: string | ((operator: string) => VNodeChild) validator?: (value: SearchValue) => Omit | undefined onPanelVisibleChange?: (visible: boolean) => void } diff --git a/packages/pro/search/src/types/segment.ts b/packages/pro/search/src/types/segment.ts index eb8809e0b..8161a42e6 100644 --- a/packages/pro/search/src/types/segment.ts +++ b/packages/pro/search/src/types/segment.ts @@ -6,12 +6,13 @@ */ import type { ExtractInnerPropTypes, VKey } from '@idux/cdk/utils' -import type { PropType, VNodeChild } from 'vue' +import type { PropType, Slots, VNodeChild } from 'vue' export type InputFormater = (value: V) => string export type InputParser = (input: string) => V | null export interface PanelRenderContext { + slots: Slots input: string value: V ok: () => void