@opendesign-plus-test/components 0.0.1-rc.47 → 0.0.1-rc.48
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/dist/chunk-OElCookieNotice.cjs.js +1 -1
- package/dist/chunk-OElCookieNotice.es.js +66 -46
- package/dist/components/OHeaderSearch.vue.d.ts +812 -534
- package/dist/components/search/OSearchInput.vue.d.ts +1003 -0
- package/dist/components/search/composables/useImageSearch.d.ts +48 -0
- package/dist/components/search/composables/useKeywordHighlight.d.ts +2 -0
- package/dist/components/search/composables/useSearchHistory.d.ts +14 -0
- package/dist/components/search/index.d.ts +590 -0
- package/dist/components/search/internal/HighlightText.vue.d.ts +9 -0
- package/dist/components/search/internal/SearchImageInput.vue.d.ts +716 -0
- package/dist/components/search/internal/SearchPanel.vue.d.ts +100 -0
- package/dist/components/search/types.d.ts +20 -0
- package/dist/components.cjs.js +41 -41
- package/dist/components.css +1 -1
- package/dist/components.es.js +11296 -10253
- package/dist/index.d.ts +1 -0
- package/package.json +5 -3
- package/src/assets/svg-icons/icon-delete-hover.svg +4 -0
- package/src/assets/svg-icons/icon-delete.svg +5 -1
- package/src/assets/svg-icons/icon-image-close.svg +4 -0
- package/src/assets/svg-icons/icon-image-upload.svg +3 -0
- package/src/assets/svg-icons/icon-image-zoomin.svg +3 -0
- package/src/assets/svg-icons/icon-refresh.svg +3 -0
- package/src/components/OHeaderSearch.vue +407 -425
- package/src/components/search/OSearchInput.vue +463 -0
- package/src/components/search/composables/useImageSearch.ts +157 -0
- package/src/components/search/composables/useKeywordHighlight.ts +30 -0
- package/src/components/search/composables/useSearchHistory.ts +75 -0
- package/src/components/search/index.ts +23 -0
- package/src/components/search/internal/HighlightText.vue +37 -0
- package/src/components/search/internal/SearchImageInput.vue +488 -0
- package/src/components/search/internal/SearchPanel.vue +430 -0
- package/src/components/search/types.ts +25 -0
- package/src/i18n/en.ts +10 -0
- package/src/i18n/zh.ts +10 -0
- package/src/index.ts +1 -0
- package/vite.config.ts +4 -0
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed,
|
|
3
|
-
import { OIcon
|
|
4
|
-
import { onClickOutside } from '@vueuse/core';
|
|
2
|
+
import { computed, ref, toRef, watch } from 'vue';
|
|
3
|
+
import { OIcon } from '@opensig/opendesign';
|
|
4
|
+
import { onClickOutside, useDebounceFn } from '@vueuse/core';
|
|
5
5
|
import { useScreen } from '@opendesign-plus/composables';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import SearchImageInput from './search/internal/SearchImageInput.vue';
|
|
8
|
+
import SearchPanel from './search/internal/SearchPanel.vue';
|
|
9
|
+
import { useSearchHistory } from './search/composables/useSearchHistory';
|
|
10
|
+
import { useI18n } from '@/i18n';
|
|
11
|
+
import type {
|
|
12
|
+
OSearchPayload,
|
|
13
|
+
OSearchRecommendItem,
|
|
14
|
+
OSearchUploadImageFn,
|
|
15
|
+
} from './search/types';
|
|
16
|
+
|
|
8
17
|
import IconSearch from '~icons/components/icon-header-search.svg';
|
|
9
|
-
import IconDelete from '~icons/components/icon-header-delete.svg';
|
|
10
|
-
import IconDeleteAll from '~icons/components/icon-delete.svg';
|
|
11
18
|
import IconBack from '~icons/components/icon-header-back.svg';
|
|
12
19
|
|
|
13
|
-
import { useI18n } from '@/i18n';
|
|
14
|
-
|
|
15
20
|
export interface OHeaderSearchPropsT {
|
|
21
|
+
/** ---- Backward-compatible props ---- */
|
|
16
22
|
modelValue?: string;
|
|
17
23
|
placeholder?: string; // 搜索框默认提示
|
|
18
24
|
expandedPlaceholder?: string; // 搜索框展开后提示
|
|
@@ -20,28 +26,37 @@ export interface OHeaderSearchPropsT {
|
|
|
20
26
|
clearable?: boolean; // 是否显示清除按钮,默认显示
|
|
21
27
|
historyItems?: string[]; // 搜索历史记录
|
|
22
28
|
maxHistoryCount?: number; // 最多保存的搜索历史记录数,默认 6 条
|
|
23
|
-
storeHistory?: boolean; // 是否使用 localStorage 存储搜索历史记录,存储之后初始化时会自动加载搜索历史记录,默认为 false
|
|
29
|
+
storeHistory?: boolean; // 是否使用 localStorage 存储搜索历史记录,存储之后初始化时会自动加载搜索历史记录,默认为 false
|
|
24
30
|
historyTitle?: string; // 历史记录标题
|
|
25
31
|
storageKey?: string; // localStorage 存储搜索历史记录的 key,默认为 search-history
|
|
26
32
|
hotItems?: string[]; // 热门搜索
|
|
27
33
|
hotTitle?: string; // 推荐搜索标题
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
/** Legacy plain-string recommend list. Shown when input is empty. */
|
|
35
|
+
recommendItems?: string[];
|
|
36
|
+
searchUrl?: string;
|
|
37
|
+
searchUrlOpenBlank?: boolean;
|
|
38
|
+
searchTextMobile?: string;
|
|
39
|
+
imagePlaceholder?: string;
|
|
40
|
+
enableImageSearch?: boolean;
|
|
41
|
+
imageUrl?: string;
|
|
42
|
+
uploadImage?: OSearchUploadImageFn;
|
|
43
|
+
maxImageSize?: number;
|
|
44
|
+
imageUploadTooltip?: string;
|
|
45
|
+
suggestItems?: OSearchRecommendItem[];
|
|
46
|
+
onestepItems?: OSearchRecommendItem[];
|
|
47
|
+
suggestTitle?: string;
|
|
48
|
+
onestepTitle?: string;
|
|
49
|
+
noDataText?: string;
|
|
50
|
+
highlightKeyword?: boolean;
|
|
51
|
+
/** Debounce ms for the `input` event */
|
|
52
|
+
debounce?: number;
|
|
53
|
+
/** Auto-record history on search; default true */
|
|
54
|
+
autoSaveHistory?: boolean;
|
|
55
|
+
/** Show "no data" empty state in suggest section while typing */
|
|
56
|
+
showSuggestEmpty?: boolean;
|
|
57
|
+
allowedImageTypes?: string[];
|
|
32
58
|
}
|
|
33
59
|
|
|
34
|
-
export interface OHeaderSearchEmitsT {
|
|
35
|
-
(e: 'update:modelValue', value: string): void;
|
|
36
|
-
(e: 'update:historyItems', value: string[]): void;
|
|
37
|
-
(e: 'clear'): void;
|
|
38
|
-
(e: 'search', value: string): void;
|
|
39
|
-
(e: 'delete-history', value: string[]): void;
|
|
40
|
-
(e: 'delete-history-item', value: string): void;
|
|
41
|
-
}
|
|
42
|
-
const { lePadV } = useScreen();
|
|
43
|
-
const { t } = useI18n();
|
|
44
|
-
|
|
45
60
|
const props = withDefaults(defineProps<OHeaderSearchPropsT>(), {
|
|
46
61
|
modelValue: '',
|
|
47
62
|
expandDirection: 'left',
|
|
@@ -53,235 +68,349 @@ const props = withDefaults(defineProps<OHeaderSearchPropsT>(), {
|
|
|
53
68
|
hotItems: () => [],
|
|
54
69
|
recommendItems: () => [],
|
|
55
70
|
searchUrlOpenBlank: true,
|
|
71
|
+
enableImageSearch: false,
|
|
72
|
+
imageUrl: '',
|
|
73
|
+
maxImageSize: 10 * 1024 * 1024,
|
|
74
|
+
suggestItems: () => [],
|
|
75
|
+
onestepItems: () => [],
|
|
76
|
+
highlightKeyword: true,
|
|
77
|
+
debounce: 300,
|
|
78
|
+
autoSaveHistory: true,
|
|
79
|
+
showSuggestEmpty: true,
|
|
56
80
|
});
|
|
57
81
|
|
|
58
|
-
const emit = defineEmits<
|
|
82
|
+
const emit = defineEmits<{
|
|
83
|
+
(e: 'update:modelValue', value: string): void;
|
|
84
|
+
(e: 'update:historyItems', value: string[]): void;
|
|
85
|
+
(e: 'update:imageUrl', url: string): void;
|
|
86
|
+
(e: 'focus'): void;
|
|
87
|
+
(e: 'blur'): void;
|
|
88
|
+
(e: 'input', val: string): void;
|
|
89
|
+
(e: 'clear'): void;
|
|
90
|
+
/** Backward compatible: previously `(val: string)`. Now emits payload. */
|
|
91
|
+
(e: 'search', payload: OSearchPayload): void;
|
|
92
|
+
(e: 'recommend-click', item: OSearchRecommendItem | string): void;
|
|
93
|
+
(e: 'onestep-click', item: OSearchRecommendItem): void;
|
|
94
|
+
(e: 'history-click', val: string): void;
|
|
95
|
+
(e: 'hot-click', val: string): void;
|
|
96
|
+
(e: 'hot-refresh'): void;
|
|
97
|
+
(e: 'delete-history', value: string[]): void;
|
|
98
|
+
(e: 'delete-history-item', value: string): void;
|
|
99
|
+
(e: 'image-upload-start', file: File): void;
|
|
100
|
+
(e: 'image-upload-success', url: string, file: File): void;
|
|
101
|
+
(e: 'image-upload-error', error: unknown, file: File): void;
|
|
102
|
+
(e: 'image-validate-error', reason: 'size' | 'type', file: File): void;
|
|
103
|
+
}>();
|
|
104
|
+
|
|
105
|
+
const { lePadV } = useScreen();
|
|
106
|
+
const { t } = useI18n();
|
|
59
107
|
|
|
60
|
-
const
|
|
61
|
-
const
|
|
108
|
+
const wrapperRef = ref<HTMLElement | null>(null);
|
|
109
|
+
const inputRef = ref<InstanceType<typeof SearchImageInput>>();
|
|
62
110
|
const isShowDrawer = ref(false);
|
|
63
|
-
const
|
|
111
|
+
const internalImageStaged = ref(false);
|
|
112
|
+
const internalImageUrl = ref('');
|
|
113
|
+
const hasImage = computed(() => !!props.imageUrl || internalImageStaged.value);
|
|
64
114
|
|
|
65
|
-
const
|
|
66
|
-
|
|
115
|
+
const innerValue = computed({
|
|
116
|
+
get: () => props.modelValue,
|
|
117
|
+
set: (val: string) => emit('update:modelValue', val),
|
|
67
118
|
});
|
|
68
119
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
() =>
|
|
80
|
-
|
|
81
|
-
emit('update:modelValue', val);
|
|
82
|
-
}
|
|
83
|
-
);
|
|
120
|
+
const historyItemsRef = toRef(props, 'historyItems');
|
|
121
|
+
const storageKeyRef = toRef(props, 'storageKey');
|
|
122
|
+
const storeHistoryRef = toRef(props, 'storeHistory');
|
|
123
|
+
const maxHistoryRef = toRef(props, 'maxHistoryCount');
|
|
124
|
+
|
|
125
|
+
const history = useSearchHistory({
|
|
126
|
+
initial: historyItemsRef,
|
|
127
|
+
storageKey: storageKeyRef,
|
|
128
|
+
storeHistory: storeHistoryRef,
|
|
129
|
+
maxHistoryCount: maxHistoryRef,
|
|
130
|
+
onChange: (items) => emit('update:historyItems', items),
|
|
131
|
+
});
|
|
84
132
|
|
|
85
|
-
|
|
86
|
-
()
|
|
87
|
-
|
|
88
|
-
if (searchHistoryItems.value !== val) {
|
|
89
|
-
searchHistoryItems.value = val;
|
|
90
|
-
}
|
|
133
|
+
const placeholder = computed(() => {
|
|
134
|
+
if (props.imageUrl) {
|
|
135
|
+
return props.imagePlaceholder ?? t('search.extendedPlaceholder');
|
|
91
136
|
}
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
watch(
|
|
95
|
-
() => searchHistoryItems.value,
|
|
96
|
-
(val) => {
|
|
97
|
-
emit('update:historyItems', val);
|
|
137
|
+
if (isShowDrawer.value && props.enableImageSearch) {
|
|
138
|
+
return props.imagePlaceholder ?? t('search.imagePlaceholder');
|
|
98
139
|
}
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
onMounted(() => {
|
|
102
|
-
if (props.storeHistory && props.storageKey) {
|
|
103
|
-
try {
|
|
104
|
-
const history = JSON.parse(localStorage.getItem(props.storageKey) || '[]');
|
|
105
|
-
if (Array.isArray(history) && history.length) {
|
|
106
|
-
searchHistoryItems.value = Array.from(new Set([...searchHistoryItems.value, ...history]));
|
|
107
|
-
}
|
|
108
|
-
} catch {
|
|
109
|
-
// nothing
|
|
110
|
-
}
|
|
140
|
+
if (isShowDrawer.value) {
|
|
141
|
+
return props.expandedPlaceholder ?? t('search.expandedPlaceholder');
|
|
111
142
|
}
|
|
143
|
+
return props.placeholder ?? t('search.placeholder');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// debounce input event
|
|
147
|
+
const emitInputDebounced = useDebounceFn((val: string) => emit('input', val), () => props.debounce);
|
|
148
|
+
watch(innerValue, (val) => emitInputDebounced(val));
|
|
149
|
+
|
|
150
|
+
// click outside (desktop only)
|
|
151
|
+
onClickOutside(wrapperRef, () => {
|
|
152
|
+
if (lePadV.value) return;
|
|
153
|
+
closeDrawer();
|
|
112
154
|
});
|
|
113
155
|
|
|
114
|
-
const
|
|
156
|
+
const openDrawer = () => {
|
|
115
157
|
isShowDrawer.value = true;
|
|
116
158
|
};
|
|
117
159
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
if (!input) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
160
|
+
const closeDrawer = () => {
|
|
161
|
+
if (lePadV.value) return;
|
|
124
162
|
isShowDrawer.value = false;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const handleFocus = () => {
|
|
166
|
+
openDrawer();
|
|
167
|
+
emit('focus');
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const handleBlur = () => {
|
|
171
|
+
emit('blur');
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const runSearch = async () => {
|
|
175
|
+
if (inputRef.value?.getIsUploading?.()) {
|
|
176
|
+
await inputRef.value.awaitUpload?.();
|
|
129
177
|
}
|
|
178
|
+
const keyword = innerValue.value.trim();
|
|
179
|
+
const imageUrl = internalImageUrl.value || inputRef.value?.getUploadedUrl?.() || props.imageUrl;
|
|
180
|
+
|
|
181
|
+
if (!keyword && !imageUrl) return;
|
|
130
182
|
|
|
131
|
-
if (props.
|
|
132
|
-
|
|
183
|
+
if (props.autoSaveHistory && keyword) {
|
|
184
|
+
history.push(keyword);
|
|
133
185
|
}
|
|
134
|
-
|
|
186
|
+
|
|
187
|
+
isShowDrawer.value = false;
|
|
188
|
+
emit('search', { keyword, imageUrl: imageUrl || undefined });
|
|
135
189
|
|
|
136
190
|
if (props.searchUrl) {
|
|
137
|
-
|
|
191
|
+
const params = new URLSearchParams();
|
|
192
|
+
if (keyword) params.set('q', keyword);
|
|
193
|
+
if (imageUrl) params.set('imageUrl', imageUrl);
|
|
194
|
+
const sep = props.searchUrl.includes('?') ? '&' : '?';
|
|
195
|
+
const url = `${props.searchUrl}${sep}${params.toString()}`;
|
|
196
|
+
if (typeof window !== 'undefined') {
|
|
197
|
+
window.open(url, props.searchUrlOpenBlank ? '_blank' : '_self', 'noopener,noreferrer');
|
|
198
|
+
}
|
|
138
199
|
}
|
|
139
200
|
};
|
|
140
201
|
|
|
141
|
-
const
|
|
142
|
-
|
|
202
|
+
const handleClear = () => {
|
|
203
|
+
innerValue.value = '';
|
|
143
204
|
emit('clear');
|
|
144
|
-
|
|
145
|
-
isShowDrawer.value = false;
|
|
146
|
-
}
|
|
205
|
+
inputRef.value?.focus?.();
|
|
147
206
|
};
|
|
148
207
|
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
208
|
+
const handleSuggestClick = (item: OSearchRecommendItem) => {
|
|
209
|
+
emit('recommend-click', item);
|
|
210
|
+
innerValue.value = item.key;
|
|
211
|
+
runSearch();
|
|
212
|
+
};
|
|
155
213
|
|
|
156
|
-
|
|
214
|
+
const handleRecommendClick = (val: string) => {
|
|
215
|
+
emit('recommend-click', val);
|
|
216
|
+
innerValue.value = val;
|
|
217
|
+
runSearch();
|
|
157
218
|
};
|
|
158
219
|
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
localStorage.setItem(props.storageKey, JSON.stringify(searchHistoryItems.value));
|
|
164
|
-
} else {
|
|
165
|
-
localStorage.removeItem(props.storageKey);
|
|
166
|
-
}
|
|
220
|
+
const handleOnestepClick = (item: OSearchRecommendItem) => {
|
|
221
|
+
emit('onestep-click', item);
|
|
222
|
+
if (item.path && typeof window !== 'undefined') {
|
|
223
|
+
window.open(item.path, '_blank', 'noopener,noreferrer');
|
|
167
224
|
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const handleHistoryClick = (val: string) => {
|
|
228
|
+
emit('history-click', val);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const handleHotClick = (val: string) => {
|
|
232
|
+
emit('hot-click', val);
|
|
233
|
+
};
|
|
168
234
|
|
|
235
|
+
const handleHistoryRemove = (val: string) => {
|
|
236
|
+
history.remove(val);
|
|
169
237
|
emit('delete-history-item', val);
|
|
170
238
|
};
|
|
171
239
|
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
240
|
+
const handleHistoryClear = () => {
|
|
241
|
+
const removed = [...history.items.value];
|
|
242
|
+
history.clearAll();
|
|
243
|
+
emit('delete-history', removed);
|
|
175
244
|
};
|
|
176
245
|
|
|
177
|
-
const
|
|
178
|
-
|
|
246
|
+
const handleBack = () => {
|
|
247
|
+
innerValue.value = '';
|
|
248
|
+
internalImageStaged.value = false;
|
|
249
|
+
emit('update:imageUrl', '');
|
|
179
250
|
isShowDrawer.value = false;
|
|
180
251
|
};
|
|
181
252
|
|
|
182
|
-
const
|
|
183
|
-
|
|
253
|
+
const handleImageUploadStart = (file: File) => {
|
|
254
|
+
internalImageStaged.value = true;
|
|
255
|
+
internalImageUrl.value = '';
|
|
256
|
+
emit('image-upload-start', file);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const handleImageUploadSuccess = (url: string, file: File) => {
|
|
260
|
+
internalImageUrl.value = url;
|
|
261
|
+
emit('update:imageUrl', url);
|
|
262
|
+
emit('image-upload-success', url, file);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const handleImageClear = () => {
|
|
266
|
+
internalImageStaged.value = false;
|
|
267
|
+
internalImageUrl.value = '';
|
|
268
|
+
emit('update:imageUrl', '');
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const showPanel = computed(() => {
|
|
272
|
+
if (!isShowDrawer.value) return false;
|
|
273
|
+
if (hasImage.value) return false;
|
|
274
|
+
return true;
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
defineExpose({
|
|
278
|
+
focus: () => inputRef.value?.focus?.(),
|
|
279
|
+
blur: () => inputRef.value?.blur?.(),
|
|
280
|
+
open: openDrawer,
|
|
281
|
+
close: closeDrawer,
|
|
282
|
+
search: runSearch,
|
|
283
|
+
});
|
|
184
284
|
</script>
|
|
185
285
|
|
|
186
286
|
<template>
|
|
187
287
|
<div class="o-header-search">
|
|
188
288
|
<div
|
|
189
|
-
ref="
|
|
289
|
+
ref="wrapperRef"
|
|
290
|
+
class="o-header-search-pos"
|
|
190
291
|
:class="{
|
|
191
|
-
'
|
|
192
|
-
'
|
|
193
|
-
'
|
|
194
|
-
'
|
|
195
|
-
focus: isShowDrawer,
|
|
292
|
+
'is-pc': !lePadV,
|
|
293
|
+
'is-mobile': lePadV,
|
|
294
|
+
'is-left': expandDirection === 'left',
|
|
295
|
+
'is-right': expandDirection === 'right',
|
|
296
|
+
'is-focus': isShowDrawer,
|
|
196
297
|
}"
|
|
197
298
|
>
|
|
198
|
-
<div class="o-header-search-
|
|
199
|
-
<OIcon v-if="lePadV && isShowDrawer" class="o-header-search-icon" @click="
|
|
299
|
+
<div class="o-header-search-row" :class="{ 'is-focus': isShowDrawer }">
|
|
300
|
+
<OIcon v-if="lePadV && isShowDrawer" class="o-header-search-back-icon" @click="handleBack">
|
|
200
301
|
<IconBack />
|
|
201
302
|
</OIcon>
|
|
202
303
|
|
|
203
|
-
<
|
|
304
|
+
<SearchImageInput
|
|
204
305
|
ref="inputRef"
|
|
205
|
-
v-model="
|
|
306
|
+
v-model="innerValue"
|
|
307
|
+
:image-url="imageUrl"
|
|
308
|
+
:placeholder="placeholder"
|
|
309
|
+
:enable-image-search="enableImageSearch"
|
|
310
|
+
:upload-image="uploadImage"
|
|
311
|
+
:max-image-size="maxImageSize"
|
|
312
|
+
:image-upload-tooltip="imageUploadTooltip"
|
|
313
|
+
:clearable="clearable && isShowDrawer"
|
|
314
|
+
:expanded="isShowDrawer && hasImage"
|
|
315
|
+
:allowed-image-types="allowedImageTypes"
|
|
316
|
+
size="medium"
|
|
206
317
|
class="o-header-search-input"
|
|
207
|
-
:
|
|
208
|
-
@
|
|
209
|
-
@
|
|
318
|
+
:class="{ 'is-collapsed': !isShowDrawer }"
|
|
319
|
+
@update:imageUrl="(url: string) => emit('update:imageUrl', url)"
|
|
320
|
+
@focus="handleFocus"
|
|
321
|
+
@blur="handleBlur"
|
|
322
|
+
@enter="runSearch"
|
|
323
|
+
@clear="handleClear"
|
|
324
|
+
@image-clear="handleImageClear"
|
|
325
|
+
@image-upload-start="handleImageUploadStart"
|
|
326
|
+
@image-upload-success="handleImageUploadSuccess"
|
|
327
|
+
@image-upload-error="(error: unknown, file: File) => emit('image-upload-error', error, file)"
|
|
328
|
+
@image-validate-error="(reason: 'size' | 'type', file: File) => emit('image-validate-error', reason, file)"
|
|
210
329
|
>
|
|
211
|
-
<template #prefix>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
</OIcon>
|
|
216
|
-
</slot>
|
|
330
|
+
<template #prefix><slot name="input-prefix" /></template>
|
|
331
|
+
<template #suffix="slotProps"><slot name="input-suffix" v-bind="slotProps" /></template>
|
|
332
|
+
<template v-if="$slots['image-preview']" #preview="slotProps">
|
|
333
|
+
<slot name="image-preview" v-bind="slotProps" />
|
|
217
334
|
</template>
|
|
335
|
+
</SearchImageInput>
|
|
218
336
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
<IconClose />
|
|
223
|
-
</OIcon>
|
|
224
|
-
</slot>
|
|
225
|
-
</template>
|
|
226
|
-
</OInput>
|
|
227
|
-
|
|
228
|
-
<span v-if="lePadV && isShowDrawer" class="o-header-search-text" @click="onSearch">{{ searchTextMobile ?? t('search') }}</span>
|
|
337
|
+
<span v-if="lePadV && isShowDrawer" class="o-header-search-text" @click="runSearch">
|
|
338
|
+
{{ searchTextMobile ?? t('search') }}
|
|
339
|
+
</span>
|
|
229
340
|
</div>
|
|
230
341
|
|
|
231
|
-
<
|
|
232
|
-
<
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
<
|
|
276
|
-
<
|
|
277
|
-
</
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
342
|
+
<Transition name="o-header-search-drawer">
|
|
343
|
+
<div v-if="showPanel" class="o-header-search-drawer">
|
|
344
|
+
<slot
|
|
345
|
+
name="drawer"
|
|
346
|
+
:recommend-items="recommendItems"
|
|
347
|
+
:history-items="history.items.value"
|
|
348
|
+
:hot-items="hotItems"
|
|
349
|
+
:suggest-items="suggestItems"
|
|
350
|
+
:onestep-items="onestepItems"
|
|
351
|
+
:keyword="modelValue"
|
|
352
|
+
>
|
|
353
|
+
<SearchPanel
|
|
354
|
+
:keyword="modelValue"
|
|
355
|
+
:onestep-items="onestepItems"
|
|
356
|
+
:onestep-title="onestepTitle"
|
|
357
|
+
:suggest-items="suggestItems"
|
|
358
|
+
:suggest-title="suggestTitle"
|
|
359
|
+
:recommend-items="recommendItems"
|
|
360
|
+
:history-items="history.items.value"
|
|
361
|
+
:history-title="historyTitle"
|
|
362
|
+
:hot-items="hotItems"
|
|
363
|
+
:hot-title="hotTitle"
|
|
364
|
+
:no-data-text="noDataText"
|
|
365
|
+
:highlight-keyword="highlightKeyword"
|
|
366
|
+
:hide-on-keyword="true"
|
|
367
|
+
:show-suggest-empty="showSuggestEmpty"
|
|
368
|
+
@onestep-click="handleOnestepClick"
|
|
369
|
+
@suggest-click="handleSuggestClick"
|
|
370
|
+
@recommend-click="handleRecommendClick"
|
|
371
|
+
@history-click="handleHistoryClick"
|
|
372
|
+
@history-remove="handleHistoryRemove"
|
|
373
|
+
@history-clear="handleHistoryClear"
|
|
374
|
+
@hot-click="handleHotClick"
|
|
375
|
+
@hot-refresh="emit('hot-refresh')"
|
|
376
|
+
>
|
|
377
|
+
<template v-if="$slots['recommend-header']" #recommend-header="slotProps">
|
|
378
|
+
<slot name="recommend-header" v-bind="slotProps" />
|
|
379
|
+
</template>
|
|
380
|
+
<template v-if="$slots['recommend-content']" #recommend-content="slotProps">
|
|
381
|
+
<slot name="recommend-content" v-bind="slotProps" />
|
|
382
|
+
</template>
|
|
383
|
+
<template v-if="$slots['onestep-header']" #onestep-header="slotProps">
|
|
384
|
+
<slot name="onestep-header" v-bind="slotProps" />
|
|
385
|
+
</template>
|
|
386
|
+
<template v-if="$slots['onestep-content']" #onestep-content="slotProps">
|
|
387
|
+
<slot name="onestep-content" v-bind="slotProps" />
|
|
388
|
+
</template>
|
|
389
|
+
<template v-if="$slots['suggest-header']" #suggest-header="slotProps">
|
|
390
|
+
<slot name="suggest-header" v-bind="slotProps" />
|
|
391
|
+
</template>
|
|
392
|
+
<template v-if="$slots['suggest-content']" #suggest-content="slotProps">
|
|
393
|
+
<slot name="suggest-content" v-bind="slotProps" />
|
|
394
|
+
</template>
|
|
395
|
+
<template v-if="$slots['history-header']" #history-header="slotProps">
|
|
396
|
+
<slot name="history-header" v-bind="slotProps" />
|
|
397
|
+
</template>
|
|
398
|
+
<template v-if="$slots['history-content']" #history-content="slotProps">
|
|
399
|
+
<slot name="history-content" v-bind="slotProps" />
|
|
400
|
+
</template>
|
|
401
|
+
<template v-if="$slots['hot-header']" #hot-header="slotProps">
|
|
402
|
+
<slot name="hot-header" v-bind="slotProps" />
|
|
403
|
+
</template>
|
|
404
|
+
<template v-if="$slots['hot-content']" #hot-content="slotProps">
|
|
405
|
+
<slot name="hot-content" v-bind="slotProps" />
|
|
406
|
+
</template>
|
|
407
|
+
</SearchPanel>
|
|
408
|
+
</slot>
|
|
409
|
+
</div>
|
|
410
|
+
</Transition>
|
|
282
411
|
</div>
|
|
283
412
|
|
|
284
|
-
<OIcon v-if="lePadV" class="o-header-search-icon
|
|
413
|
+
<OIcon v-if="lePadV" class="o-header-search-mobile-icon" @click="openDrawer">
|
|
285
414
|
<IconSearch />
|
|
286
415
|
</OIcon>
|
|
287
416
|
</div>
|
|
@@ -293,117 +422,110 @@ onClickOutside(posWrapper, onClear);
|
|
|
293
422
|
width: 160px;
|
|
294
423
|
height: 32px;
|
|
295
424
|
|
|
296
|
-
@include respond('<=laptop') {
|
|
425
|
+
@include respond-to('<=laptop') {
|
|
297
426
|
width: 120px;
|
|
298
427
|
}
|
|
299
428
|
|
|
300
|
-
@
|
|
429
|
+
@include respond-to('<=pad_v') {
|
|
301
430
|
width: 24px;
|
|
302
431
|
height: 24px;
|
|
432
|
+
margin-left: auto;
|
|
303
433
|
}
|
|
304
434
|
}
|
|
305
435
|
|
|
306
|
-
.o-header-search-
|
|
307
|
-
cursor: pointer;
|
|
308
|
-
color: var(--o-color-info1);
|
|
309
|
-
@include h4;
|
|
310
|
-
|
|
311
|
-
@include respond('<=pad_v') {
|
|
312
|
-
font-size: 20px;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
.close {
|
|
316
|
-
@include x-svg-hover;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
.o-header-search-icon-mobile {
|
|
321
|
-
font-size: 24px;
|
|
322
|
-
line-height: 28px;
|
|
323
|
-
color: var(--o-color-info1);
|
|
324
|
-
cursor: pointer;
|
|
325
|
-
display: none;
|
|
326
|
-
|
|
327
|
-
@include respond('<=pad_v') {
|
|
328
|
-
display: block;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
.o-header-search-input-pc-wrapper {
|
|
436
|
+
.o-header-search-pos {
|
|
333
437
|
position: absolute;
|
|
334
|
-
right: 0;
|
|
335
438
|
top: 0;
|
|
336
439
|
width: fit-content;
|
|
337
440
|
background-color: var(--o-color-fill2);
|
|
338
441
|
z-index: 100;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
.o-header-search-input-pc-wrapper-left {
|
|
342
|
-
right: 0;
|
|
343
|
-
}
|
|
344
442
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
443
|
+
&.is-left {
|
|
444
|
+
right: 0;
|
|
445
|
+
}
|
|
446
|
+
&.is-right {
|
|
447
|
+
left: 0;
|
|
448
|
+
}
|
|
449
|
+
&.is-mobile {
|
|
450
|
+
display: none;
|
|
451
|
+
}
|
|
452
|
+
&.is-pc.is-focus {
|
|
453
|
+
box-shadow: var(--o-shadow-2);
|
|
454
|
+
top: calc(-1 * var(--o-gap-4));
|
|
455
|
+
border-radius: var(--o-radius-xs);
|
|
456
|
+
}
|
|
353
457
|
|
|
354
|
-
|
|
355
|
-
|
|
458
|
+
&.is-mobile.is-focus {
|
|
459
|
+
position: fixed;
|
|
460
|
+
top: 0;
|
|
461
|
+
right: 0;
|
|
462
|
+
bottom: 0;
|
|
463
|
+
left: 0;
|
|
464
|
+
width: 100%;
|
|
465
|
+
display: block;
|
|
466
|
+
height: 100vh;
|
|
467
|
+
background-color: var(--o-color-fill2);
|
|
468
|
+
z-index: 100;
|
|
469
|
+
overflow-y: auto;
|
|
470
|
+
}
|
|
356
471
|
}
|
|
357
472
|
|
|
358
|
-
.o-header-search-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
right: 0;
|
|
362
|
-
bottom: 0;
|
|
363
|
-
left: 0;
|
|
364
|
-
display: block;
|
|
365
|
-
height: 100vh;
|
|
366
|
-
background-color: var(--o-color-fill2);
|
|
367
|
-
z-index: 100;
|
|
368
|
-
overflow: hidden;
|
|
369
|
-
}
|
|
473
|
+
.o-header-search-row {
|
|
474
|
+
display: flex;
|
|
475
|
+
align-items: start;
|
|
370
476
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
transition: width var(--o-easing-standard-in) var(--o-duration-m2);
|
|
477
|
+
&.is-focus {
|
|
478
|
+
padding: var(--o-gap-4);
|
|
479
|
+
border-radius: var(--o-radius-xs);
|
|
375
480
|
|
|
376
|
-
@include respond('<=
|
|
377
|
-
|
|
481
|
+
@include respond-to('<=pad_v') {
|
|
482
|
+
gap: var(--o-gap-4);
|
|
483
|
+
padding: 10px var(--o-gap-4) var(--o-gap-4) var(--o-gap-4);
|
|
484
|
+
border-radius: unset;
|
|
378
485
|
}
|
|
379
486
|
}
|
|
380
487
|
}
|
|
381
488
|
|
|
382
|
-
.o-header-search-input
|
|
383
|
-
|
|
489
|
+
.o-header-search-input {
|
|
490
|
+
width: 160px;
|
|
491
|
+
transition: width var(--o-duration-m2) var(--o-easing-standard-in);
|
|
384
492
|
|
|
385
|
-
@include respond('<=
|
|
386
|
-
|
|
387
|
-
align-items: center;
|
|
388
|
-
gap: var(--o-gap-4);
|
|
389
|
-
padding: 10px var(--o-gap-4) var(--o-gap-4) var(--o-gap-4);
|
|
493
|
+
@include respond-to('<=laptop') {
|
|
494
|
+
width: 120px;
|
|
390
495
|
}
|
|
496
|
+
}
|
|
391
497
|
|
|
392
|
-
|
|
393
|
-
|
|
498
|
+
.o-header-search-row.is-focus .o-header-search-input {
|
|
499
|
+
width: 480px;
|
|
394
500
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
501
|
+
@include respond-to('<=laptop') {
|
|
502
|
+
width: 240px;
|
|
503
|
+
}
|
|
398
504
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
505
|
+
@include respond-to('<=pad_v') {
|
|
506
|
+
flex: 1;
|
|
507
|
+
width: auto;
|
|
402
508
|
}
|
|
403
509
|
}
|
|
404
510
|
|
|
405
|
-
.o-header-search-icon
|
|
406
|
-
|
|
511
|
+
.o-header-search-back-icon {
|
|
512
|
+
cursor: pointer;
|
|
513
|
+
color: var(--o-color-info1);
|
|
514
|
+
font-size: 20px;
|
|
515
|
+
height: 30px;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.o-header-search-text {
|
|
519
|
+
white-space: nowrap;
|
|
520
|
+
font-size: 16px;
|
|
521
|
+
line-height: 24px;
|
|
522
|
+
cursor: pointer;
|
|
523
|
+
color: var(--o-color-info1);
|
|
524
|
+
height: 30px;
|
|
525
|
+
|
|
526
|
+
@include hover {
|
|
527
|
+
color: var(--o-color-primary1);
|
|
528
|
+
}
|
|
407
529
|
}
|
|
408
530
|
|
|
409
531
|
.o-header-search-drawer {
|
|
@@ -413,13 +535,15 @@ onClickOutside(posWrapper, onClear);
|
|
|
413
535
|
padding-top: var(--o-gap-2);
|
|
414
536
|
background-color: var(--o-color-fill2);
|
|
415
537
|
box-shadow: var(--o-shadow-2);
|
|
538
|
+
border-radius: 0 0 var(--o-radius-xs) var(--o-radius-xs);
|
|
416
539
|
|
|
417
|
-
@include respond('<=pad_v') {
|
|
540
|
+
@include respond-to('<=pad_v') {
|
|
418
541
|
position: static;
|
|
419
542
|
height: calc(100vh - 50px);
|
|
420
543
|
padding-top: 0;
|
|
421
544
|
overflow-y: auto;
|
|
422
545
|
box-shadow: unset;
|
|
546
|
+
border-radius: unset;
|
|
423
547
|
}
|
|
424
548
|
}
|
|
425
549
|
|
|
@@ -431,171 +555,29 @@ onClickOutside(posWrapper, onClear);
|
|
|
431
555
|
top: -14px;
|
|
432
556
|
height: 14px;
|
|
433
557
|
background-color: var(--o-color-fill2);
|
|
434
|
-
box-shadow: unset;
|
|
435
558
|
|
|
436
|
-
@include respond('<=
|
|
437
|
-
top: -10px;
|
|
438
|
-
height: 10px;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
@include respond('<=pad') {
|
|
442
|
-
top: -8px;
|
|
443
|
-
height: 8px;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
@include respond('<=pad_v') {
|
|
559
|
+
@include respond-to('<=pad_v') {
|
|
447
560
|
display: none;
|
|
448
561
|
}
|
|
449
562
|
}
|
|
450
563
|
|
|
451
|
-
.o-header-search-
|
|
564
|
+
.o-header-search-mobile-icon {
|
|
565
|
+
font-size: 24px;
|
|
566
|
+
line-height: 28px;
|
|
452
567
|
color: var(--o-color-info1);
|
|
453
|
-
margin-bottom: var(--o-gap-3);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
.o-header-search-recommend-item {
|
|
457
568
|
cursor: pointer;
|
|
458
|
-
@include tip2;
|
|
459
|
-
|
|
460
|
-
@include hover {
|
|
461
|
-
color: var(--o-color-primary1);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
@include respond('<=pad_v') {
|
|
465
|
-
font-size: 12px;
|
|
466
|
-
line-height: 18px;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
& + & {
|
|
470
|
-
margin-top: var(--o-gap-3);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
.o-header-search-history-container {
|
|
475
|
-
@include respond('<=pad_v') {
|
|
476
|
-
margin-bottom: var(--o-gap-5);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
.o-header-search-history-header {
|
|
481
|
-
display: flex;
|
|
482
|
-
align-items: center;
|
|
483
|
-
justify-content: space-between;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
.o-header-search-history-header-title {
|
|
487
|
-
@include tip2;
|
|
488
|
-
color: var(--o-color-info3);
|
|
489
|
-
|
|
490
|
-
@include respond('<=pad_v') {
|
|
491
|
-
@include text2;
|
|
492
|
-
color: var(--o-color-info1);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
.o-header-search-history-item-container {
|
|
497
|
-
display: flex;
|
|
498
|
-
gap: 8px;
|
|
499
|
-
flex-wrap: wrap;
|
|
500
|
-
margin-top: var(--o-gap-2);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
.o-header-search-history-item-icon {
|
|
504
|
-
position: absolute;
|
|
505
|
-
right: -8px;
|
|
506
|
-
top: -8px;
|
|
507
569
|
display: none;
|
|
508
|
-
align-items: center;
|
|
509
|
-
justify-content: center;
|
|
510
|
-
width: 16px;
|
|
511
|
-
height: 16px;
|
|
512
|
-
border-radius: 50%;
|
|
513
|
-
background-color: rgb(var(--o-grey-9));
|
|
514
|
-
|
|
515
|
-
.icon-delete {
|
|
516
|
-
height: 16px;
|
|
517
|
-
width: 16px;
|
|
518
|
-
color: var(--o-color-white);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
570
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
display: flex;
|
|
525
|
-
align-items: center;
|
|
526
|
-
max-width: 224px;
|
|
527
|
-
height: 24px;
|
|
528
|
-
padding: 0 var(--o-gap-3);
|
|
529
|
-
background-color: var(--o-color-fill3);
|
|
530
|
-
border-radius: var(--o-radius-xs);
|
|
531
|
-
cursor: pointer;
|
|
532
|
-
|
|
533
|
-
@include hover {
|
|
534
|
-
background-color: var(--o-color-control2-light);
|
|
535
|
-
color: var(--o-color-primary1);
|
|
536
|
-
|
|
537
|
-
.o-header-search-history-item-icon {
|
|
538
|
-
display: flex;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
.o-header-search-history-item-text {
|
|
544
|
-
max-width: 200px;
|
|
545
|
-
overflow: hidden;
|
|
546
|
-
text-overflow: ellipsis;
|
|
547
|
-
white-space: nowrap;
|
|
548
|
-
@include tip2;
|
|
549
|
-
|
|
550
|
-
@include respond('<=pad_v') {
|
|
551
|
-
@include text1;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
.o-header-search-drawer-divider {
|
|
556
|
-
--o-divider-gap: var(--o-gap-4);
|
|
557
|
-
|
|
558
|
-
@include respond('<=pad_v') {
|
|
559
|
-
display: none;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
.o-header-search-hot-header {
|
|
564
|
-
color: var(--o-color-info3);
|
|
565
|
-
@include tip2;
|
|
566
|
-
|
|
567
|
-
@include respond('<=pad_v') {
|
|
568
|
-
margin-bottom: var(--o-gap-3);
|
|
569
|
-
@include text2;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
.o-header-search-hot-item-container {
|
|
574
|
-
display: flex;
|
|
575
|
-
flex-wrap: wrap;
|
|
576
|
-
gap: var(--o-gap-4);
|
|
577
|
-
margin-top: var(--o-gap-3);
|
|
578
|
-
@include tip2;
|
|
579
|
-
|
|
580
|
-
@include respond('<=pad_v') {
|
|
581
|
-
flex-direction: column;
|
|
582
|
-
gap: 12px;
|
|
583
|
-
font-size: 12px;
|
|
584
|
-
line-height: 18px;
|
|
571
|
+
@include respond-to('<=pad_v') {
|
|
572
|
+
display: block;
|
|
585
573
|
}
|
|
586
574
|
}
|
|
587
575
|
|
|
588
|
-
.o-header-search-
|
|
589
|
-
|
|
590
|
-
cursor: pointer;
|
|
591
|
-
|
|
592
|
-
@include hover {
|
|
593
|
-
color: var(--o-color-primary1);
|
|
594
|
-
}
|
|
576
|
+
.o-header-search-drawer-enter-active {
|
|
577
|
+
transition: opacity var(--o-duration-m1);
|
|
595
578
|
}
|
|
596
579
|
|
|
597
|
-
.o-header-search-
|
|
598
|
-
|
|
599
|
-
line-height: 24px;
|
|
580
|
+
.o-header-search-drawer-enter-from {
|
|
581
|
+
opacity: 0;
|
|
600
582
|
}
|
|
601
583
|
</style>
|