@opentiny/vue-search-box 0.1.0 → 0.1.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.zh-CN.md +107 -0
- package/__tests__/search-box.spec.ts +0 -0
- package/package.json +10 -6
- package/scripts/pre-release.cjs +8 -0
- package/src/composables/use-checkbox.ts +90 -0
- package/src/composables/use-custom.ts +53 -0
- package/src/composables/use-datepicker.ts +90 -0
- package/src/composables/use-dropdown.ts +251 -0
- package/src/composables/use-edit.ts +119 -0
- package/src/composables/use-init.ts +69 -0
- package/src/composables/use-match.ts +193 -0
- package/src/composables/use-num-range.ts +86 -0
- package/src/composables/use-placeholder.ts +43 -0
- package/src/composables/use-tag.ts +54 -0
- package/src/index.less +376 -0
- package/src/index.ts +13 -0
- package/src/index.type.ts +192 -0
- package/src/index.vue +1138 -0
- package/src/smb-theme.ts +15 -0
- package/src/theme.json +135 -0
- package/src/utils/clone.ts +37 -0
- package/src/utils/date.ts +724 -0
- package/src/utils/dropdown.ts +27 -0
- package/src/utils/en_US.ts +41 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/tag.ts +80 -0
- package/src/utils/type.ts +6 -0
- package/src/utils/validate.ts +234 -0
- package/src/utils/zh_CN.ts +41 -0
- package/src/vars.less +56 -0
- package/vite.config.theme.ts +20 -0
- package/vite.config.ts +60 -0
- /package/{es → dist/es}/composables/use-checkbox.es.js +0 -0
- /package/{es → dist/es}/composables/use-custom.es.js +0 -0
- /package/{es → dist/es}/composables/use-datepicker.es.js +0 -0
- /package/{es → dist/es}/composables/use-dropdown.es.js +0 -0
- /package/{es → dist/es}/composables/use-edit.es.js +0 -0
- /package/{es → dist/es}/composables/use-init.es.js +0 -0
- /package/{es → dist/es}/composables/use-match.es.js +0 -0
- /package/{es → dist/es}/composables/use-num-range.es.js +0 -0
- /package/{es → dist/es}/composables/use-placeholder.es.js +0 -0
- /package/{es → dist/es}/composables/use-tag.es.js +0 -0
- /package/{es → dist/es}/index-VrLZbD8H.css +0 -0
- /package/{es → dist/es}/index.es.js +0 -0
- /package/{es → dist/es}/index.type.es.js +0 -0
- /package/{es → dist/es}/index.vue.es.js +0 -0
- /package/{es → dist/es}/index.vue.es2.js +0 -0
- /package/{es → dist/es}/smb-theme.es.js +0 -0
- /package/{es → dist/es}/utils/clone.es.js +0 -0
- /package/{es → dist/es}/utils/date.es.js +0 -0
- /package/{es → dist/es}/utils/dropdown.es.js +0 -0
- /package/{es → dist/es}/utils/en_US.es.js +0 -0
- /package/{es → dist/es}/utils/index.es.js +0 -0
- /package/{es → dist/es}/utils/tag.es.js +0 -0
- /package/{es → dist/es}/utils/type.es.js +0 -0
- /package/{es → dist/es}/utils/validate.es.js +0 -0
- /package/{es → dist/es}/utils/zh_CN.es.js +0 -0
- /package/{index.css → dist/index.css} +0 -0
- /package/{lib → dist/lib}/composables/use-checkbox.cjs.js +0 -0
- /package/{lib → dist/lib}/composables/use-custom.cjs.js +0 -0
- /package/{lib → dist/lib}/composables/use-datepicker.cjs.js +0 -0
- /package/{lib → dist/lib}/composables/use-dropdown.cjs.js +0 -0
- /package/{lib → dist/lib}/composables/use-edit.cjs.js +0 -0
- /package/{lib → dist/lib}/composables/use-init.cjs.js +0 -0
- /package/{lib → dist/lib}/composables/use-match.cjs.js +0 -0
- /package/{lib → dist/lib}/composables/use-num-range.cjs.js +0 -0
- /package/{lib → dist/lib}/composables/use-placeholder.cjs.js +0 -0
- /package/{lib → dist/lib}/composables/use-tag.cjs.js +0 -0
- /package/{lib → dist/lib}/index-VrLZbD8H.css +0 -0
- /package/{lib → dist/lib}/index.cjs.js +0 -0
- /package/{lib → dist/lib}/index.type.cjs.js +0 -0
- /package/{lib → dist/lib}/index.vue.cjs.js +0 -0
- /package/{lib → dist/lib}/index.vue.cjs2.js +0 -0
- /package/{lib → dist/lib}/smb-theme.cjs.js +0 -0
- /package/{lib → dist/lib}/utils/clone.cjs.js +0 -0
- /package/{lib → dist/lib}/utils/date.cjs.js +0 -0
- /package/{lib → dist/lib}/utils/dropdown.cjs.js +0 -0
- /package/{lib → dist/lib}/utils/en_US.cjs.js +0 -0
- /package/{lib → dist/lib}/utils/index.cjs.js +0 -0
- /package/{lib → dist/lib}/utils/tag.cjs.js +0 -0
- /package/{lib → dist/lib}/utils/type.cjs.js +0 -0
- /package/{lib → dist/lib}/utils/validate.cjs.js +0 -0
- /package/{lib → dist/lib}/utils/zh_CN.cjs.js +0 -0
- /package/{types → dist/types}/composables/use-checkbox.d.ts +0 -0
- /package/{types → dist/types}/composables/use-custom.d.ts +0 -0
- /package/{types → dist/types}/composables/use-datepicker.d.ts +0 -0
- /package/{types → dist/types}/composables/use-dropdown.d.ts +0 -0
- /package/{types → dist/types}/composables/use-edit.d.ts +0 -0
- /package/{types → dist/types}/composables/use-init.d.ts +0 -0
- /package/{types → dist/types}/composables/use-match.d.ts +0 -0
- /package/{types → dist/types}/composables/use-num-range.d.ts +0 -0
- /package/{types → dist/types}/composables/use-placeholder.d.ts +0 -0
- /package/{types → dist/types}/composables/use-tag.d.ts +0 -0
- /package/{types → dist/types}/index.type.d.ts +0 -0
- /package/{types → dist/types}/smb-theme.d.ts +0 -0
- /package/{types → dist/types}/utils/clone.d.ts +0 -0
- /package/{types → dist/types}/utils/date.d.ts +0 -0
- /package/{types → dist/types}/utils/dropdown.d.ts +0 -0
- /package/{types → dist/types}/utils/en_US.d.ts +0 -0
- /package/{types → dist/types}/utils/index.d.ts +0 -0
- /package/{types → dist/types}/utils/tag.d.ts +0 -0
- /package/{types → dist/types}/utils/type.d.ts +0 -0
- /package/{types → dist/types}/utils/validate.d.ts +0 -0
- /package/{types → dist/types}/utils/zh_CN.d.ts +0 -0
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# TinySearchBox 综合搜索组件
|
|
2
|
+
|
|
3
|
+
TinySearchBox 是一个基于 Vue3 的综合搜索组件,使用 TinyVue 组件库,遵循 OpenTiny 设计规范,简单易用、功能强大,支持单选、多选、时间、时间区间等多条件筛选。
|
|
4
|
+
|
|
5
|
+
[English](README.md) | 简体中文
|
|
6
|
+
|
|
7
|
+
## 项目优势
|
|
8
|
+
|
|
9
|
+
TinySearchBox 主要有以下特点和优势:
|
|
10
|
+
|
|
11
|
+
- 将筛选条件聚拢在一个输入框中,筛选效率更高、用户体验更好
|
|
12
|
+
- 支持单选、多选、时间、时间区间、数字大小、数字区间等多种类型条件筛选
|
|
13
|
+
- 强大的搜索功能,支持模糊搜索、自定义搜索等
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
## 快速上手
|
|
18
|
+
|
|
19
|
+
安装 TinySearchBox
|
|
20
|
+
|
|
21
|
+
```shell
|
|
22
|
+
npm i @opentiny/vue-search-box
|
|
23
|
+
```
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
导入 TinySearchBox 综合搜索:
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
import TinySearchBox from '@opentiny/vue-search-box';
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
引入样式:
|
|
33
|
+
|
|
34
|
+
```css
|
|
35
|
+
@import '@opentiny/vue-search-box/index.css';
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
在模板中使用:
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<script setup>
|
|
42
|
+
const tags = ref([]);
|
|
43
|
+
const items = ref([
|
|
44
|
+
{
|
|
45
|
+
label: '名称',
|
|
46
|
+
field: 'testName',
|
|
47
|
+
replace: true,
|
|
48
|
+
placeholder: '我是自定义名称的占位符',
|
|
49
|
+
options: [
|
|
50
|
+
{
|
|
51
|
+
label: 'test-1'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
label: 'test-2'
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: '可用地区',
|
|
60
|
+
field: 'testRegion',
|
|
61
|
+
type: 'checkbox',
|
|
62
|
+
mergeTag: true,
|
|
63
|
+
placeholder: '我是自定义可选地区的占位符',
|
|
64
|
+
editAttrDisabled: true, // 编辑状态此属性禁用,不可变更
|
|
65
|
+
options: [
|
|
66
|
+
{
|
|
67
|
+
label: '华南区',
|
|
68
|
+
id: '2-1'
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
label: '华北区',
|
|
72
|
+
id: '2-2'
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: '大小',
|
|
78
|
+
field: 'size',
|
|
79
|
+
type: 'numRange',
|
|
80
|
+
placeholder: '我是自定义大小的占位符',
|
|
81
|
+
unit: 'GB',
|
|
82
|
+
start: -1,
|
|
83
|
+
min: -1,
|
|
84
|
+
max: 20
|
|
85
|
+
}
|
|
86
|
+
]);
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<template>
|
|
90
|
+
<tiny-search-box v-model="tags" :items="items"></tiny-search-box>
|
|
91
|
+
</template>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 本地开发
|
|
95
|
+
|
|
96
|
+
```shell
|
|
97
|
+
git clone git@github.com:opentiny/tiny-search-box.git
|
|
98
|
+
cd tiny-search-box
|
|
99
|
+
pnpm i
|
|
100
|
+
pnpm dev
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
打开浏览器访问:[http://localhost:5173/tiny-search-box/](http://localhost:5173/tiny-search-box/)
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
[MIT](LICENSE)
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentiny/vue-search-box",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-alpha.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://github.com/opentiny/tiny-search-box#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -13,10 +13,13 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": "",
|
|
15
15
|
"type": "module",
|
|
16
|
-
"main": "lib/index.cjs.js",
|
|
17
|
-
"module": "es/index.es.js",
|
|
18
|
-
"types": "types/index.d.ts",
|
|
16
|
+
"main": "dist/lib/index.cjs.js",
|
|
17
|
+
"module": "dist/es/index.es.js",
|
|
18
|
+
"types": "dist/types/index.d.ts",
|
|
19
19
|
"style": "src/index.less",
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
20
23
|
"keywords": [
|
|
21
24
|
"search",
|
|
22
25
|
"comprehensive search",
|
|
@@ -57,7 +60,8 @@
|
|
|
57
60
|
"vite-plugin-dts": "^4.3.0"
|
|
58
61
|
},
|
|
59
62
|
"scripts": {
|
|
60
|
-
"build": "pnpm build:theme && vite build
|
|
61
|
-
"build:theme": "vite build --config vite.config.theme.ts"
|
|
63
|
+
"build": "pnpm build:theme && vite build",
|
|
64
|
+
"build:theme": "vite build --config vite.config.theme.ts",
|
|
65
|
+
"prepublish": "node scripts/pre-release.cjs"
|
|
62
66
|
}
|
|
63
67
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { computed } from 'vue'
|
|
2
|
+
import { hasTagItem, createNewTag, getTagId, emitChangeModelEvent } from '../utils/tag'
|
|
3
|
+
import { deepClone, omitObj } from '../utils/clone'
|
|
4
|
+
|
|
5
|
+
export function useCheckbox({ props, state, emits }) {
|
|
6
|
+
const selectCheckbox = (confirm: boolean) => {
|
|
7
|
+
const { checkboxGroup, prevItem, propItem } = state
|
|
8
|
+
const rest = omitObj(prevItem)
|
|
9
|
+
state.hiden = true
|
|
10
|
+
if (confirm) {
|
|
11
|
+
const tagList = []
|
|
12
|
+
const oldValue = deepClone(state.innerModelValue)
|
|
13
|
+
const { mergeTag, operators, label: prevLabel } = prevItem
|
|
14
|
+
if (mergeTag) {
|
|
15
|
+
let value = ''
|
|
16
|
+
const options = []
|
|
17
|
+
const { indexMap } = state
|
|
18
|
+
const hasTagIndex = indexMap.get(prevLabel)
|
|
19
|
+
hasTagIndex !== undefined && state.innerModelValue.splice(hasTagIndex, 1)
|
|
20
|
+
|
|
21
|
+
state.backupList.forEach((item) => {
|
|
22
|
+
const { label } = item
|
|
23
|
+
const checkboxLabel = `${prevLabel}${label}`
|
|
24
|
+
const hasItem = checkboxGroup.includes(checkboxLabel)
|
|
25
|
+
if (hasItem) {
|
|
26
|
+
delete item.isFilter
|
|
27
|
+
const id = getTagId(props, prevItem, item)
|
|
28
|
+
const newOptions = { label: item.label, ...id }
|
|
29
|
+
value += !value ? label : ` | ${label}`
|
|
30
|
+
options.push(newOptions)
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
if (options.length > 0) {
|
|
34
|
+
const newTag = { ...rest, value, options }
|
|
35
|
+
tagList.push(newTag)
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
const { valueMap } = state
|
|
39
|
+
const indexList = []
|
|
40
|
+
state.backupList.forEach((item) => {
|
|
41
|
+
const { label } = item
|
|
42
|
+
const value = `${prevLabel}${label}`
|
|
43
|
+
const hasItem = checkboxGroup.includes(value)
|
|
44
|
+
if (hasItem && !hasTagItem(state, label)) {
|
|
45
|
+
const id = getTagId(props, prevItem, item)
|
|
46
|
+
const operator = state.operatorValue && operators ? { operator: state.operatorValue } : null
|
|
47
|
+
const newTag = createNewTag({ ...rest, label: propItem.label, value: label, ...id, ...operator })
|
|
48
|
+
tagList.push(newTag)
|
|
49
|
+
item.isChecked = true
|
|
50
|
+
} else if (!hasItem && hasTagItem(state, label)) {
|
|
51
|
+
item.isChecked = false
|
|
52
|
+
const index = valueMap.get(value)
|
|
53
|
+
indexList.push(index)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
if (indexList.length) {
|
|
57
|
+
state.innerModelValue = state.innerModelValue.filter((item, index) => item && !indexList.includes(index))
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
emitChangeModelEvent({ emits, state, tagList, oldValue })
|
|
61
|
+
} else {
|
|
62
|
+
propItem.label = ''
|
|
63
|
+
state.inputValue = ''
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const isIndeterminate = computed(
|
|
68
|
+
() => state.checkboxGroup.length > 0 && state.checkboxGroup.length !== state.filterList.length
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const checkAll = computed({
|
|
72
|
+
get: () => state.checkboxGroup.length && state.checkboxGroup.length === state.filterList.length,
|
|
73
|
+
set: (val) => {
|
|
74
|
+
if (val) {
|
|
75
|
+
state.checkboxGroup = state.filterList.flatMap((item) => `${state.prevItem.label}${item.label}`)
|
|
76
|
+
} else {
|
|
77
|
+
state.checkboxGroup = []
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const isShowClose = computed(() => props.modelValue.length || state.propItem.label || state.inputValue)
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
selectCheckbox,
|
|
86
|
+
isIndeterminate,
|
|
87
|
+
checkAll,
|
|
88
|
+
isShowClose
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { resetInput, emitChangeModelEvent, hasTagItem } from '../utils/tag'
|
|
2
|
+
import { showDropdown, showPopover } from '../utils/dropdown'
|
|
3
|
+
|
|
4
|
+
export function useCustom({ state, emits }) {
|
|
5
|
+
const updateCustomValue = (customTag) => {
|
|
6
|
+
const { prevItem, indexMap } = state
|
|
7
|
+
const { replace, label } = prevItem
|
|
8
|
+
|
|
9
|
+
const tagList = []
|
|
10
|
+
if (replace && indexMap.has(label)) {
|
|
11
|
+
const index = indexMap.get(label)
|
|
12
|
+
const newTag = { ...prevItem, ...customTag }
|
|
13
|
+
emitChangeModelEvent({ emits, state, newTag, index })
|
|
14
|
+
|
|
15
|
+
return
|
|
16
|
+
} else if (!replace && Array.isArray(customTag)) {
|
|
17
|
+
customTag.forEach((tag) => {
|
|
18
|
+
if (!hasTagItem(state, tag.value)) {
|
|
19
|
+
tagList.push({ ...prevItem, ...tag })
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
} else {
|
|
23
|
+
if (!hasTagItem(state, customTag.value)) {
|
|
24
|
+
tagList.push({ ...prevItem, ...customTag })
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
emitChangeModelEvent({ emits, state, tagList })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const handleConfirm = (customTag) => {
|
|
31
|
+
if (!customTag) {
|
|
32
|
+
resetInput(state)
|
|
33
|
+
showDropdown(state, false)
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
updateCustomValue(customTag)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const handleEditConfirm = (customTag) => {
|
|
41
|
+
if (!customTag) {
|
|
42
|
+
showPopover(state, false)
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
updateCustomValue(customTag)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
handleConfirm,
|
|
51
|
+
handleEditConfirm
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { showDropdown } from '../utils/dropdown'
|
|
2
|
+
import { getVerifyDateTag } from '../utils/validate'
|
|
3
|
+
import { emitChangeModelEvent } from '../utils/tag'
|
|
4
|
+
|
|
5
|
+
export function useDatePicker({ props, state, emits }) {
|
|
6
|
+
const { instance } = state
|
|
7
|
+
const onConfirmDate = async (confirm: boolean, isDateTimeType = false) => {
|
|
8
|
+
if (!confirm) {
|
|
9
|
+
state.propItem.label = ''
|
|
10
|
+
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
const newTag = await getVerifyDateTag(instance, state, props, isDateTimeType)
|
|
14
|
+
if (newTag) {
|
|
15
|
+
const newValue = props.modelValue.filter((prev) => prev.type !== newTag.type || prev.field !== newTag.field)
|
|
16
|
+
newValue.push(newTag)
|
|
17
|
+
emitChangeModelEvent({ emits, state, newValue })
|
|
18
|
+
showDropdown(state, false)
|
|
19
|
+
} else {
|
|
20
|
+
showDropdown(state)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const handleDateShow = () => showDropdown(state)
|
|
25
|
+
|
|
26
|
+
const pickerOptions = (startDate, endName = '') => ({
|
|
27
|
+
disabledDate(time) {
|
|
28
|
+
const { maxTimeLength = 0, min, max } = state.prevItem
|
|
29
|
+
|
|
30
|
+
const endDate = state[endName]
|
|
31
|
+
const curTime = time.getTime()
|
|
32
|
+
// 有限制时间跨度timeLength时
|
|
33
|
+
if (maxTimeLength > 0) {
|
|
34
|
+
if (min || max) {
|
|
35
|
+
if (endName && endDate) {
|
|
36
|
+
const end = new Date(endDate).getTime()
|
|
37
|
+
const start = !min && max ? end - maxTimeLength : Math.max(min.getTime(), end - maxTimeLength)
|
|
38
|
+
return curTime < start || curTime > end
|
|
39
|
+
} else if (!endName && startDate) {
|
|
40
|
+
const start = new Date(startDate).getTime()
|
|
41
|
+
const end = min && !max ? start + maxTimeLength : Math.min(max.getTime(), start + maxTimeLength)
|
|
42
|
+
return curTime < start || curTime > end
|
|
43
|
+
} else {
|
|
44
|
+
return (min && curTime < min.getTime()) || (max && curTime > max.getTime())
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
if (endName && endDate) {
|
|
48
|
+
const end = new Date(endDate).getTime()
|
|
49
|
+
const start = end - maxTimeLength
|
|
50
|
+
return curTime < start || curTime > end
|
|
51
|
+
} else if (!endName && startDate) {
|
|
52
|
+
const start = new Date(startDate).getTime()
|
|
53
|
+
const end = start + maxTimeLength
|
|
54
|
+
return curTime < start || curTime > end
|
|
55
|
+
} else {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
if (min || max) {
|
|
61
|
+
if (endName && endDate) {
|
|
62
|
+
const end = new Date(endDate).getTime()
|
|
63
|
+
return (min && curTime < min.getTime()) || curTime > end
|
|
64
|
+
} else if (!endName && startDate) {
|
|
65
|
+
const start = new Date(startDate).getTime()
|
|
66
|
+
return curTime < start || (max && curTime > max.getTime())
|
|
67
|
+
} else {
|
|
68
|
+
return curTime < min || curTime > max
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
if (endName && endDate) {
|
|
72
|
+
const end = new Date(endDate).getTime()
|
|
73
|
+
return curTime > end
|
|
74
|
+
} else if (!endName && startDate) {
|
|
75
|
+
const start = new Date(startDate).getTime()
|
|
76
|
+
return curTime < start
|
|
77
|
+
} else {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
onConfirmDate,
|
|
87
|
+
handleDateShow,
|
|
88
|
+
pickerOptions
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { hasTagItem, resetInput, createNewTag, getTagId, emitChangeModelEvent } from '../utils/tag'
|
|
2
|
+
import { showDropdown } from '../utils/dropdown'
|
|
3
|
+
import { setStateNumRange } from '../utils/validate'
|
|
4
|
+
import { deepClone, omitObj } from '../utils/clone'
|
|
5
|
+
|
|
6
|
+
export function useDropdown({ props, emits, state, t, format }) {
|
|
7
|
+
const { instance } = state
|
|
8
|
+
const showValueItem = (item) => {
|
|
9
|
+
const { start, end, type } = item
|
|
10
|
+
state.backupList = item.options?.length ? item.options : []
|
|
11
|
+
if (type === 'numRange') {
|
|
12
|
+
setStateNumRange(state, item, t)
|
|
13
|
+
state.hiden = false
|
|
14
|
+
} else if (type === 'dateRange') {
|
|
15
|
+
const { dateRangeFormat } = state
|
|
16
|
+
if (!state.startDate && start) {
|
|
17
|
+
const newStart = format(start, dateRangeFormat)
|
|
18
|
+
state.startDate = state.endDate < newStart ? null : newStart
|
|
19
|
+
}
|
|
20
|
+
if (!state.endDate && end) {
|
|
21
|
+
const newEnd = format(end, dateRangeFormat)
|
|
22
|
+
state.endDate = newEnd < state.startDate ? null : newEnd
|
|
23
|
+
}
|
|
24
|
+
state.hiden = false
|
|
25
|
+
} else if (type === 'datetimeRange') {
|
|
26
|
+
const { datetimeRangeFormat } = state
|
|
27
|
+
if (!state.startDateTime && start) {
|
|
28
|
+
const newStart = format(start, datetimeRangeFormat)
|
|
29
|
+
state.startDateTime = state.endDateTime < newStart ? null : newStart
|
|
30
|
+
}
|
|
31
|
+
if (!state.endDateTime && end) {
|
|
32
|
+
const newEnd = format(end, datetimeRangeFormat)
|
|
33
|
+
state.endDateTime = newEnd < state.startDateTime ? null : newEnd
|
|
34
|
+
}
|
|
35
|
+
state.hiden = false
|
|
36
|
+
} else if (state.backupList && type === 'checkbox') {
|
|
37
|
+
state.filterList = state.backupList
|
|
38
|
+
state.checkboxGroup = []
|
|
39
|
+
state.backupList.forEach((subItem) => {
|
|
40
|
+
if (hasTagItem(state, subItem.label)) {
|
|
41
|
+
state.checkboxGroup.push(`${item.label}${subItem.label}`)
|
|
42
|
+
}
|
|
43
|
+
subItem.isFilter = false
|
|
44
|
+
})
|
|
45
|
+
state.hiden = false
|
|
46
|
+
} else if (type === 'custom') {
|
|
47
|
+
state.hiden = false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (state.backupList.length && type !== 'checkbox') {
|
|
51
|
+
state.backupList.forEach((option) => {
|
|
52
|
+
option.isFilter = false
|
|
53
|
+
option.isChecked = hasTagItem(state, option.label)
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
state.currentOperators = null
|
|
58
|
+
|
|
59
|
+
if (!((type === 'radio' || !type) && !state.backupList.length)) {
|
|
60
|
+
showDropdown(state)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const selectPropItem = (item) => {
|
|
65
|
+
const { field, label } = item
|
|
66
|
+
state.propItem.label = label
|
|
67
|
+
|
|
68
|
+
emits('first-level-select', field)
|
|
69
|
+
|
|
70
|
+
const inputRef = instance.refs.inputRef
|
|
71
|
+
state.prevItem = item
|
|
72
|
+
state.backupPrevItem = item
|
|
73
|
+
const { operators } = item
|
|
74
|
+
if (operators?.length) {
|
|
75
|
+
state.operatorValue = ''
|
|
76
|
+
state.currentOperators = operators
|
|
77
|
+
state.isResetFlag = true
|
|
78
|
+
showDropdown(state)
|
|
79
|
+
} else {
|
|
80
|
+
state.operatorValue = ':'
|
|
81
|
+
showValueItem(item)
|
|
82
|
+
}
|
|
83
|
+
state.inputValue = ''
|
|
84
|
+
inputRef.focus()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const setOperator = (operator) => {
|
|
88
|
+
state.operatorValue = operator
|
|
89
|
+
showValueItem(state.prevItem)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 更新modelValue值
|
|
94
|
+
* @param prevItem items数组的第一层元素信息,用来识别此类型标签的某些属性作相应处理
|
|
95
|
+
* @param item 当前选中的标签值信息
|
|
96
|
+
* @param label 标签左侧的label
|
|
97
|
+
* @param value 标签右侧值的value
|
|
98
|
+
*/
|
|
99
|
+
const updateModelValue = (prevItem, item, label, value) => {
|
|
100
|
+
const { replace, operators, mergeTag } = prevItem
|
|
101
|
+
const rest = omitObj(prevItem)
|
|
102
|
+
const { indexMap } = state
|
|
103
|
+
const index = indexMap.get(label)
|
|
104
|
+
const id = getTagId(props, prevItem, item)
|
|
105
|
+
const operator = state.operatorValue && operators ? { operator: state.operatorValue } : null
|
|
106
|
+
let newTag = null
|
|
107
|
+
|
|
108
|
+
if (mergeTag) {
|
|
109
|
+
const options = { label: value, ...id }
|
|
110
|
+
if (index >= 0) {
|
|
111
|
+
const newValue = `${state.innerModelValue[index].value} | ${value}`
|
|
112
|
+
const newOptions = [...state.innerModelValue[index].options, options]
|
|
113
|
+
newTag = createNewTag({ ...state.innerModelValue[index], value: newValue, options: newOptions })
|
|
114
|
+
} else {
|
|
115
|
+
newTag = createNewTag({ ...rest, label, value, options: [options] })
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
newTag = createNewTag({ ...rest, label, value, ...id, ...operator })
|
|
119
|
+
}
|
|
120
|
+
if (hasTagItem(state, value, label)) {
|
|
121
|
+
resetInput(state)
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const oldValue = deepClone(state.innerModelValue)
|
|
126
|
+
if ((replace || mergeTag) && index >= 0) {
|
|
127
|
+
state.innerModelValue.splice(index, 1)
|
|
128
|
+
}
|
|
129
|
+
state.innerModelValue.push(newTag)
|
|
130
|
+
const newValue = state.innerModelValue
|
|
131
|
+
emitChangeModelEvent({ emits, state, newValue, oldValue })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 选中单选标签
|
|
136
|
+
* @param item 选中的标签option项
|
|
137
|
+
* @param isPotential 是否为选择潜在匹配项,默认否。参数可选
|
|
138
|
+
*/
|
|
139
|
+
const selectRadioItem = (item, isPotential = false) => {
|
|
140
|
+
// 潜在匹配项没有prevItem
|
|
141
|
+
if (isPotential) {
|
|
142
|
+
state.prevItem = item
|
|
143
|
+
state.backupPrevItem = item
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const { prevItem } = state
|
|
147
|
+
const value = item.value || item.label
|
|
148
|
+
const inputRef = instance.refs.inputRef
|
|
149
|
+
if (!hasTagItem(state, value)) {
|
|
150
|
+
const tagLabel = state.propItem.label || item.label
|
|
151
|
+
updateModelValue(prevItem, item, tagLabel, value)
|
|
152
|
+
}
|
|
153
|
+
inputRef.focus()
|
|
154
|
+
}
|
|
155
|
+
const hasNotInputValueCreateTag = (propItem, prevItem) => {
|
|
156
|
+
// 有label
|
|
157
|
+
if (propItem.label) {
|
|
158
|
+
if (!prevItem.options) {
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const hasTag = props.modelValue.find((item) => item.value === prevItem.options[0].label)
|
|
163
|
+
|
|
164
|
+
if (!hasTag) {
|
|
165
|
+
const label = prevItem.label
|
|
166
|
+
const value = prevItem.options && prevItem.options[0].label
|
|
167
|
+
updateModelValue(prevItem, prevItem.options[0], label, value)
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
emits('search', state.innerModelValue)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const newTagUpdateModelValue = (prevItem, propItem, tag) => {
|
|
176
|
+
const item = state.backupList.find((subItem) => subItem.label === tag)
|
|
177
|
+
updateModelValue(prevItem, item, propItem.label, tag)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const hasInputValueCreateTag = (inputValue, propItem, prevItem) => {
|
|
181
|
+
// 有label的情况
|
|
182
|
+
if (propItem.label) {
|
|
183
|
+
const { regexp, replace, type, mergeTag } = prevItem
|
|
184
|
+
const tagList =
|
|
185
|
+
(type !== 'checkbox' && replace) || (type === 'checkbox' && mergeTag)
|
|
186
|
+
? [inputValue]
|
|
187
|
+
: inputValue.split(props.splitInputValue)
|
|
188
|
+
|
|
189
|
+
if (regexp) {
|
|
190
|
+
for (const tag of tagList) {
|
|
191
|
+
if (regexp.test(tag)) {
|
|
192
|
+
newTagUpdateModelValue(prevItem, propItem, tag)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// 有输入且无正则
|
|
196
|
+
} else {
|
|
197
|
+
for (const tag of tagList) {
|
|
198
|
+
newTagUpdateModelValue(prevItem, propItem, tag)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// 无label的情况
|
|
202
|
+
} else {
|
|
203
|
+
const { items, defaultField } = props
|
|
204
|
+
const currentItem = defaultField ? items.find((item) => item.field === defaultField) : state.allTypeAttri
|
|
205
|
+
const { replace, type, mergeTag } = currentItem
|
|
206
|
+
const tagList =
|
|
207
|
+
(type !== 'checkbox' && replace) || (type === 'checkbox' && mergeTag)
|
|
208
|
+
? [inputValue]
|
|
209
|
+
: inputValue.split(props.splitInputValue)
|
|
210
|
+
|
|
211
|
+
if (currentItem?.options?.length) {
|
|
212
|
+
state.backupList = [...currentItem.options]
|
|
213
|
+
state.backupList.forEach((item) => {
|
|
214
|
+
const label = item.value || item.label
|
|
215
|
+
if (tagList.includes(label)) {
|
|
216
|
+
item.isChecked = true
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
const label = currentItem.label
|
|
221
|
+
for (const tag of tagList) {
|
|
222
|
+
updateModelValue(currentItem, {}, label, tag)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const createTag = () => {
|
|
228
|
+
const { inputValue, propItem, prevItem } = state
|
|
229
|
+
showDropdown(state, false)
|
|
230
|
+
if (!inputValue) {
|
|
231
|
+
// 输入为空的情况
|
|
232
|
+
hasNotInputValueCreateTag(propItem, prevItem)
|
|
233
|
+
} else {
|
|
234
|
+
// 输入不为空的情况
|
|
235
|
+
const { maxlength } = props
|
|
236
|
+
if (maxlength && maxlength < inputValue.length) {
|
|
237
|
+
emits('exceed', maxlength)
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
hasInputValueCreateTag(inputValue, propItem, prevItem)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 帮助图标点击事件
|
|
246
|
+
const helpClick = () => {
|
|
247
|
+
emits('help')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return { selectPropItem, selectRadioItem, createTag, helpClick, setOperator }
|
|
251
|
+
}
|