Skip to content

Commit

Permalink
feat(pro:search): add validate function
Browse files Browse the repository at this point in the history
  • Loading branch information
sallerli1 committed Aug 6, 2022
1 parent dc40ed9 commit c8774b6
Show file tree
Hide file tree
Showing 17 changed files with 320 additions and 145 deletions.
14 changes: 14 additions & 0 deletions packages/pro/search/demo/ConvertToKeyword.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
order: 21
title:
zh: 非法搜索项转换为关键字
en: Convert Illegal Search Value To Keyword
---

## zh

使用 `onItemConfirm` 事件将非法搜索项转换为关键字。

## en

convert illgale search value to keyword via `onItemConfirm` event.
117 changes: 117 additions & 0 deletions packages/pro/search/demo/ConvertToKeyword.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<template>
<IxProSearch
v-model:value="searchValues"
style="width: 100%"
:searchFields="searchFields"
:onChange="onChange"
:onSearch="onSearch"
:onItemConfirm="onItemConfirm"
></IxProSearch>
</template>

<script setup lang="ts">
import type { SearchField, SearchItemConfirmContext, SearchValue } from '@idux/pro/search'
import { ref } from 'vue'
const searchValues = ref<SearchValue[]>([])
const searchFields: SearchField[] = [
{
key: 'keyword',
type: 'input',
label: 'Keyword',
multiple: true,
fieldConfig: {
trim: true,
},
},
{
type: 'select',
label: 'Level',
key: 'level',
operators: ['=', '!='],
defaultOperator: '=',
fieldConfig: {
multiple: false,
searchable: true,
dataSource: [
{
key: 'level1',
label: 'Level 1',
},
{
key: 'level2',
label: 'Level 2',
},
{
key: 'level3',
label: 'Level 3',
},
],
},
},
{
type: 'select',
label: 'Security State',
key: 'security_state',
fieldConfig: {
multiple: true,
searchable: true,
dataSource: [
{
key: 'fatal',
label: 'fatal',
},
{
key: 'high',
label: 'high',
},
{
key: 'mediumn',
label: 'mediumn',
},
{
key: 'low',
label: 'low',
},
],
},
},
{
type: 'datePicker',
label: 'Date',
key: 'date',
fieldConfig: {
type: 'datetime',
},
},
{
type: 'dateRangePicker',
label: 'Date Range',
key: 'date_range',
fieldConfig: {
type: 'datetime',
},
},
]
const onChange = (value: SearchValue[] | undefined, oldValue: SearchValue[] | undefined) => {
console.log(value, oldValue)
}
const onSearch = () => {
console.log('onSearch')
}
const onItemConfirm = (context: SearchItemConfirmContext) => {
const { removed, nameInput, operatorInput, valueInput } = context
if (removed) {
return
}
searchValues.value.push({
key: 'keyword',
value: (nameInput ?? '') + (operatorInput ?? '') + (valueInput ?? ''),
})
}
</script>

<style scoped lang="less"></style>
4 changes: 2 additions & 2 deletions packages/pro/search/demo/Invalid.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ title:

## zh

使用 `onItemInvalid` 事件处理非法搜索项
通过 `searchField.validator` 校验搜索项

## en

Handle invalid search value via `onItemInvalid` event.
validate search value via `searchField.validator`.
90 changes: 23 additions & 67 deletions packages/pro/search/demo/Invalid.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
<template>
<IxProSearch
v-model:value="searchValues"
v-model:errors="errors"
style="width: 100%"
:searchFields="searchFields"
:onChange="onChange"
:onSearch="onSearch"
:onItemInvalid="onInvalid"
></IxProSearch>
</template>

<script setup lang="ts">
import type { InvalidSearchValue, SearchField, SearchValue } from '@idux/pro/search'
import type { SearchField, SearchItemError, SearchValue } from '@idux/pro/search'
import { ref } from 'vue'
const searchValues = ref<SearchValue[]>([])
const errors = ref<SearchItemError[]>([])
const searchFields: SearchField[] = [
{
key: 'keyword',
Expand All @@ -24,73 +25,34 @@ const searchFields: SearchField[] = [
fieldConfig: {
trim: true,
},
},
{
type: 'select',
label: 'Level',
key: 'level',
operators: ['=', '!='],
defaultOperator: '=',
fieldConfig: {
multiple: false,
searchable: true,
dataSource: [
{
key: 'level1',
label: 'Level 1',
},
{
key: 'level2',
label: 'Level 2',
},
{
key: 'level3',
label: 'Level 3',
},
],
},
},
{
type: 'select',
label: 'Security State',
key: 'security_state',
fieldConfig: {
multiple: true,
searchable: true,
dataSource: [
{
key: 'fatal',
label: 'fatal',
},
{
key: 'high',
label: 'high',
},
{
key: 'mediumn',
label: 'mediumn',
},
{
key: 'low',
label: 'low',
},
],
validator(searchValue) {
if (/[?^<>/+\-=]/.test(searchValue.value)) {
return { message: "keyword mustn't contain ?^<>+-=" }
}
return
},
},
{
type: 'datePicker',
label: 'Date',
label: 'Creation Time',
key: 'date',
operators: ['=', '>', '<'],
fieldConfig: {
type: 'datetime',
},
},
{
type: 'dateRangePicker',
label: 'Date Range',
key: 'date_range',
fieldConfig: {
type: 'datetime',
validator(searchValue) {
const { operator, value } = searchValue
const currentYear = new Date().getFullYear()
if ((operator === '>' || operator === '=') && value.getFullYear() > currentYear) {
return { message: `cannot select date after year ${currentYear}` }
}
if ((operator === '<' || operator === '=') && value.getFullYear() < 2000) {
return { message: `cannot select date before year 2000` }
}
return
},
},
]
Expand All @@ -101,12 +63,6 @@ const onChange = (value: SearchValue[] | undefined, oldValue: SearchValue[] | un
const onSearch = () => {
console.log('onSearch')
}
const onInvalid = (value: InvalidSearchValue) => {
searchValues.value.push({
key: 'keyword',
value: (value.nameInput ?? '') + (value.operatorInput ?? '') + (value.valueInput ?? ''),
})
}
</script>

<style scoped lang="less"></style>
9 changes: 6 additions & 3 deletions packages/pro/search/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ subtitle: 复合搜索

| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `v-model:value` | 复合选中的搜索值 | - | - || - |
| `v-model:value` | 选中的搜索值 | - | - | - | - |
| `v-model:errors` | 校验错误 | `{ index: number, message: string }` | - | - | - |
| `clearable` | 是否可清除 | `boolean` | `true` || - |
| `clearIcon` | 清除图标 | `string \| VNode \| #clearIcon` | `close-circle` || - |
| `disabled` | 是否禁用 | `boolean` | `false` | - | - |
Expand All @@ -24,7 +25,7 @@ subtitle: 复合搜索
| `onChange` | 搜索条件改变之后的回调 | `(value: searchValue[] \| undefined, oldValue: searchValue[] \| undefined) => void` | - | - | - |
| `onClear` | 清除搜索条件的回调 | `() => void` | - | - | - |
| `onItemRemove` | 搜索条件删除时的回调 | `(item: SearchValue) => void` | - | - | - |
| `onItemInvalid` | 搜索条件不合法时触发的回调 | `(item: InvalidSearchValue) => void` | - | - | - |
| `onItemConfirm` | 搜索条件不合法时触发的回调 | `(item: SearchItemConfirmContext) => void` | - | - | - |
| `onSearch` | 搜索按钮触发的回调 | `(value: searchValue[] \| undefined) => void` | - | - | - |

#### ProSearchSlots
Expand All @@ -40,10 +41,11 @@ interface SearchValue<V = unknown> {
value: V // 搜索值
operator?: string // 搜索操作符
}
interface InvalidSearchValue<V = unknown> extends Partial<SearchValue<V>> {
interface SearchItemConfirmContext<V = unknown> extends Partial<SearchValue<V>> {
nameInput?: string // 搜索字段名称输入
operatorInput?: string // 操作符输入
valueInput?: string // 值输入
removed: boolean // 是否被移除
}
```

Expand All @@ -64,6 +66,7 @@ interface InvalidSearchValue<V = unknown> extends Partial<SearchValue<V>> {
| `defaultOperator` | 默认的操作符 | `string` | - | - | 提供时,会自动填入默认的操作符 |
| `defaultValue` | 默认值 | - | - | - | 提供时,会自动填入默认值 |
| `inputClassName` | 输入框class | `string` | - | - | 用于自定义输入框样式 |
| `validator` | 搜索项校验函数 | `(value: SearchValue) => { message?: string } | undefined` | - | - | 返回错误信息 |

#### InputSearchField

Expand Down
3 changes: 2 additions & 1 deletion packages/pro/search/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export type {
ProSearchPublicProps as ProSearchProps,
SearchField,
SearchValue,
InvalidSearchValue,
SearchItemError,
SearchItemConfirmContext,
} from './src/types'
11 changes: 10 additions & 1 deletion packages/pro/search/src/ProSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useGlobalConfig } from '@idux/pro/config'
import { useActiveSegment } from './composables/useActiveSegment'
import { useCommonOverlayProps } from './composables/useCommonOverlayProps'
import { useSearchItems } from './composables/useSearchItem'
import { useSearchItemErrors } from './composables/useSearchItemErrors'
import { tempSearchStateKey, useSearchStates } from './composables/useSearchStates'
import { useSearchValues } from './composables/useSearchValues'
import SearchItemComp from './searchItem/SearchItem'
Expand All @@ -37,7 +38,15 @@ export default defineComponent({

const { searchValues, searchValueEmpty, setSearchValues } = useSearchValues(props)
const searchStateContext = useSearchStates(props, dateConfig, searchValues, setSearchValues)
const searchItems = useSearchItems(props, slots, mergedPrefixCls, searchStateContext.searchStates, dateConfig)
const errors = useSearchItemErrors(props, searchValues)
const searchItems = useSearchItems(
props,
slots,
mergedPrefixCls,
searchStateContext.searchStates,
errors,
dateConfig,
)
const activeSegmentContext = useActiveSegment(props, searchItems, searchStateContext.tempSearchStateAvailable)
const commonOverlayProps = useCommonOverlayProps(mergedPrefixCls, props, config)

Expand Down
4 changes: 3 additions & 1 deletion packages/pro/search/src/composables/useSearchItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { ProSearchProps, SearchField, SearchItem } from '../types'
import type { ProSearchProps, SearchField, SearchItem, SearchItemError } from '../types'
import type { SearchState } from './useSearchStates'
import type { DateConfig } from '@idux/components/config'

Expand All @@ -24,6 +24,7 @@ export function useSearchItems(
slots: Slots,
mergedPrefixCls: ComputedRef<string>,
searchStates: ComputedRef<SearchState[]>,
searchItemErrors: ComputedRef<SearchItemError[] | undefined>,
dateConfig: DateConfig,
): ComputedRef<SearchItem[]> {
const searchStatesKeys = computed(() => new Set(searchStates.value?.map(state => state.fieldKey)))
Expand All @@ -40,6 +41,7 @@ export function useSearchItems(
return {
key: searchState.key,
optionKey: searchState.fieldKey,
error: searchItemErrors.value?.find(error => error.index === searchState.index),
segments: searchState.segmentValues
.map(segmentValue => {
if (segmentValue.name === 'name') {
Expand Down
Loading

0 comments on commit c8774b6

Please sign in to comment.