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