@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.
Files changed (104) hide show
  1. package/README.zh-CN.md +107 -0
  2. package/__tests__/search-box.spec.ts +0 -0
  3. package/package.json +10 -6
  4. package/scripts/pre-release.cjs +8 -0
  5. package/src/composables/use-checkbox.ts +90 -0
  6. package/src/composables/use-custom.ts +53 -0
  7. package/src/composables/use-datepicker.ts +90 -0
  8. package/src/composables/use-dropdown.ts +251 -0
  9. package/src/composables/use-edit.ts +119 -0
  10. package/src/composables/use-init.ts +69 -0
  11. package/src/composables/use-match.ts +193 -0
  12. package/src/composables/use-num-range.ts +86 -0
  13. package/src/composables/use-placeholder.ts +43 -0
  14. package/src/composables/use-tag.ts +54 -0
  15. package/src/index.less +376 -0
  16. package/src/index.ts +13 -0
  17. package/src/index.type.ts +192 -0
  18. package/src/index.vue +1138 -0
  19. package/src/smb-theme.ts +15 -0
  20. package/src/theme.json +135 -0
  21. package/src/utils/clone.ts +37 -0
  22. package/src/utils/date.ts +724 -0
  23. package/src/utils/dropdown.ts +27 -0
  24. package/src/utils/en_US.ts +41 -0
  25. package/src/utils/index.ts +11 -0
  26. package/src/utils/tag.ts +80 -0
  27. package/src/utils/type.ts +6 -0
  28. package/src/utils/validate.ts +234 -0
  29. package/src/utils/zh_CN.ts +41 -0
  30. package/src/vars.less +56 -0
  31. package/vite.config.theme.ts +20 -0
  32. package/vite.config.ts +60 -0
  33. /package/{es → dist/es}/composables/use-checkbox.es.js +0 -0
  34. /package/{es → dist/es}/composables/use-custom.es.js +0 -0
  35. /package/{es → dist/es}/composables/use-datepicker.es.js +0 -0
  36. /package/{es → dist/es}/composables/use-dropdown.es.js +0 -0
  37. /package/{es → dist/es}/composables/use-edit.es.js +0 -0
  38. /package/{es → dist/es}/composables/use-init.es.js +0 -0
  39. /package/{es → dist/es}/composables/use-match.es.js +0 -0
  40. /package/{es → dist/es}/composables/use-num-range.es.js +0 -0
  41. /package/{es → dist/es}/composables/use-placeholder.es.js +0 -0
  42. /package/{es → dist/es}/composables/use-tag.es.js +0 -0
  43. /package/{es → dist/es}/index-VrLZbD8H.css +0 -0
  44. /package/{es → dist/es}/index.es.js +0 -0
  45. /package/{es → dist/es}/index.type.es.js +0 -0
  46. /package/{es → dist/es}/index.vue.es.js +0 -0
  47. /package/{es → dist/es}/index.vue.es2.js +0 -0
  48. /package/{es → dist/es}/smb-theme.es.js +0 -0
  49. /package/{es → dist/es}/utils/clone.es.js +0 -0
  50. /package/{es → dist/es}/utils/date.es.js +0 -0
  51. /package/{es → dist/es}/utils/dropdown.es.js +0 -0
  52. /package/{es → dist/es}/utils/en_US.es.js +0 -0
  53. /package/{es → dist/es}/utils/index.es.js +0 -0
  54. /package/{es → dist/es}/utils/tag.es.js +0 -0
  55. /package/{es → dist/es}/utils/type.es.js +0 -0
  56. /package/{es → dist/es}/utils/validate.es.js +0 -0
  57. /package/{es → dist/es}/utils/zh_CN.es.js +0 -0
  58. /package/{index.css → dist/index.css} +0 -0
  59. /package/{lib → dist/lib}/composables/use-checkbox.cjs.js +0 -0
  60. /package/{lib → dist/lib}/composables/use-custom.cjs.js +0 -0
  61. /package/{lib → dist/lib}/composables/use-datepicker.cjs.js +0 -0
  62. /package/{lib → dist/lib}/composables/use-dropdown.cjs.js +0 -0
  63. /package/{lib → dist/lib}/composables/use-edit.cjs.js +0 -0
  64. /package/{lib → dist/lib}/composables/use-init.cjs.js +0 -0
  65. /package/{lib → dist/lib}/composables/use-match.cjs.js +0 -0
  66. /package/{lib → dist/lib}/composables/use-num-range.cjs.js +0 -0
  67. /package/{lib → dist/lib}/composables/use-placeholder.cjs.js +0 -0
  68. /package/{lib → dist/lib}/composables/use-tag.cjs.js +0 -0
  69. /package/{lib → dist/lib}/index-VrLZbD8H.css +0 -0
  70. /package/{lib → dist/lib}/index.cjs.js +0 -0
  71. /package/{lib → dist/lib}/index.type.cjs.js +0 -0
  72. /package/{lib → dist/lib}/index.vue.cjs.js +0 -0
  73. /package/{lib → dist/lib}/index.vue.cjs2.js +0 -0
  74. /package/{lib → dist/lib}/smb-theme.cjs.js +0 -0
  75. /package/{lib → dist/lib}/utils/clone.cjs.js +0 -0
  76. /package/{lib → dist/lib}/utils/date.cjs.js +0 -0
  77. /package/{lib → dist/lib}/utils/dropdown.cjs.js +0 -0
  78. /package/{lib → dist/lib}/utils/en_US.cjs.js +0 -0
  79. /package/{lib → dist/lib}/utils/index.cjs.js +0 -0
  80. /package/{lib → dist/lib}/utils/tag.cjs.js +0 -0
  81. /package/{lib → dist/lib}/utils/type.cjs.js +0 -0
  82. /package/{lib → dist/lib}/utils/validate.cjs.js +0 -0
  83. /package/{lib → dist/lib}/utils/zh_CN.cjs.js +0 -0
  84. /package/{types → dist/types}/composables/use-checkbox.d.ts +0 -0
  85. /package/{types → dist/types}/composables/use-custom.d.ts +0 -0
  86. /package/{types → dist/types}/composables/use-datepicker.d.ts +0 -0
  87. /package/{types → dist/types}/composables/use-dropdown.d.ts +0 -0
  88. /package/{types → dist/types}/composables/use-edit.d.ts +0 -0
  89. /package/{types → dist/types}/composables/use-init.d.ts +0 -0
  90. /package/{types → dist/types}/composables/use-match.d.ts +0 -0
  91. /package/{types → dist/types}/composables/use-num-range.d.ts +0 -0
  92. /package/{types → dist/types}/composables/use-placeholder.d.ts +0 -0
  93. /package/{types → dist/types}/composables/use-tag.d.ts +0 -0
  94. /package/{types → dist/types}/index.type.d.ts +0 -0
  95. /package/{types → dist/types}/smb-theme.d.ts +0 -0
  96. /package/{types → dist/types}/utils/clone.d.ts +0 -0
  97. /package/{types → dist/types}/utils/date.d.ts +0 -0
  98. /package/{types → dist/types}/utils/dropdown.d.ts +0 -0
  99. /package/{types → dist/types}/utils/en_US.d.ts +0 -0
  100. /package/{types → dist/types}/utils/index.d.ts +0 -0
  101. /package/{types → dist/types}/utils/tag.d.ts +0 -0
  102. /package/{types → dist/types}/utils/type.d.ts +0 -0
  103. /package/{types → dist/types}/utils/validate.d.ts +0 -0
  104. /package/{types → dist/types}/utils/zh_CN.d.ts +0 -0
@@ -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
+ ![TinySearchBox](TinySearchBox.png)
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.0",
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 && node scripts/pre-release.cjs",
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,8 @@
1
+ const shelljs = require("shelljs");
2
+
3
+ function preRelease() {
4
+ shelljs.cp("-rf", "../../README.md", "./");
5
+ shelljs.cp("-rf", "../../README.zh-CN.md", "./");
6
+ }
7
+
8
+ preRelease();
@@ -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
+ }