@opendesign-plus-test/components 0.0.1-rc.42 → 0.0.1-rc.45
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 +132 -112
- package/dist/components/OHeaderSearch.vue.d.ts +824 -506
- package/dist/components/OSourceCode.vue.d.ts +4 -6
- package/dist/components/activity/OActivityApproval.vue.d.ts +6 -10
- package/dist/components/activity/OActivityForm.vue.d.ts +3 -5
- package/dist/components/activity/OMyActivityCalendar.vue.d.ts +100 -46
- package/dist/components/activity/index.d.ts +56 -33
- package/dist/components/activity/types.d.ts +8 -1
- package/dist/components/events/config.d.ts +5 -18
- package/dist/components/events/types.d.ts +4 -1
- package/dist/components/header/OHeaderMobile.vue.d.ts +18 -11
- package/dist/components/header/components/HeaderNavMobile.vue.d.ts +4 -1
- package/dist/components/header/index.d.ts +12 -5
- package/dist/components/header/types.d.ts +4 -0
- package/dist/components/meeting/OMeetingCalendar.vue.d.ts +8 -12
- package/dist/components/meeting/OMeetingForm.vue.d.ts +3 -5
- package/dist/components/meeting/OMeetingPlayback.vue.d.ts +45 -0
- package/dist/components/meeting/OMyMeetingCalendar.vue.d.ts +100 -46
- package/dist/components/meeting/components/OMeetingCalendarList.vue.d.ts +1 -1
- package/dist/components/meeting/index.d.ts +817 -0
- package/dist/components/meeting/types.d.ts +82 -17
- package/dist/components/meeting/utils.d.ts +1 -1
- 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 +43 -43
- package/dist/components.css +1 -1
- package/dist/components.es.js +11681 -10636
- package/dist/index.d.ts +1 -0
- package/package.json +3 -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/OBanner.vue +18 -18
- package/src/components/OCookieNotice.vue +21 -21
- package/src/components/OFooter.vue +18 -17
- package/src/components/OHeaderSearch.vue +402 -420
- package/src/components/OHeaderUser.vue +3 -2
- package/src/components/OSection.vue +4 -4
- package/src/components/OSourceCode.vue +8 -10
- package/src/components/activity/OActivityApproval.vue +32 -32
- package/src/components/activity/OActivityForm.vue +5 -5
- package/src/components/activity/OMyActivityCalendar.vue +66 -50
- package/src/components/activity/config.ts +1 -1
- package/src/components/activity/types.ts +8 -1
- package/src/components/common/ContentWrapper.vue +3 -3
- package/src/components/common/MoreText.vue +1 -1
- package/src/components/common/ThFilter.vue +7 -7
- package/src/components/element-plus/OElCookieNotice.vue +27 -27
- package/src/components/events/OEventsApply.vue +44 -44
- package/src/components/events/OEventsCalendar.vue +14 -14
- package/src/components/events/OEventsList.vue +53 -26
- package/src/components/events/config.ts +1 -1
- package/src/components/events/types.ts +4 -1
- package/src/components/header/OHeader.vue +2 -2
- package/src/components/header/OHeaderMobile.vue +8 -1
- package/src/components/header/components/HeaderContent.vue +67 -63
- package/src/components/header/components/HeaderNav.vue +4 -4
- package/src/components/header/components/HeaderNavMobile.vue +8 -5
- package/src/components/header/types.ts +4 -0
- package/src/components/meeting/OMeetingCalendar.vue +66 -54
- package/src/components/meeting/OMeetingForm.vue +70 -51
- package/src/components/meeting/OMeetingPlayback.vue +8 -8
- package/src/components/meeting/OMyMeetingCalendar.vue +56 -45
- package/src/components/meeting/OSigMeetingCalendar.vue +31 -28
- package/src/components/meeting/components/OMeetingCalendarList.vue +31 -26
- package/src/components/meeting/components/OMeetingCalendarSelector.vue +10 -6
- package/src/components/meeting/components/OMeetingDetail.vue +32 -18
- package/src/components/meeting/components/OMeetingPlaybackSubtitles.vue +1 -1
- package/src/components/meeting/components/OMeetingPlaybackVideo.vue +12 -12
- package/src/components/meeting/components/OSigMeetingAside.vue +8 -9
- package/src/components/meeting/types.ts +88 -17
- 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/draft/Banner.vue +6 -6
- package/src/draft/ButtonCards.vue +1 -1
- package/src/draft/Feature.vue +6 -6
- package/src/draft/Footer.vue +29 -22
- package/src/draft/HorizontalAnchor.vue +4 -4
- package/src/draft/ItemSwiper.vue +2 -2
- package/src/draft/Logo.vue +3 -3
- package/src/draft/LogoCard.vue +2 -2
- package/src/draft/MultiCard.vue +1 -1
- package/src/draft/MultiIconCard.vue +1 -1
- package/src/draft/OInfoCard.vue +4 -4
- package/src/draft/Section.vue +4 -4
- package/src/draft/SingleTabCard.vue +1 -1
- package/src/draft/SliderCard.vue +4 -3
- 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 -3
|
@@ -32,10 +32,12 @@ export interface CalendarOtherDataT {
|
|
|
32
32
|
export interface OptionItemT {
|
|
33
33
|
label: string;
|
|
34
34
|
value: string | number;
|
|
35
|
+
|
|
36
|
+
[key: string]: any;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
|
|
38
|
-
export type PlatformT = 'welink' | 'tencent';
|
|
40
|
+
export type PlatformT = 'welink' | 'tencent' | 'zoom' | 'WELINK' | 'TENCENT' | 'ZOOM';
|
|
39
41
|
|
|
40
42
|
export enum MeetingGroupType {
|
|
41
43
|
SIG = 'sig',
|
|
@@ -54,11 +56,12 @@ export interface MeetingCalendarPropsT {
|
|
|
54
56
|
groupType: MeetingGroupType;
|
|
55
57
|
groups: string[];
|
|
56
58
|
}
|
|
59
|
+
|
|
57
60
|
// 会议新增、修改
|
|
58
61
|
export interface MeetingPostT {
|
|
59
62
|
id?: number; // 会议id
|
|
60
63
|
topic: string; // 会议主题 128
|
|
61
|
-
sponsor
|
|
64
|
+
sponsor?: string; // 会议发起人 20
|
|
62
65
|
group_name: string; // 所属SIG 64
|
|
63
66
|
platform: PlatformT; // 会议平台
|
|
64
67
|
date: string; // 会议日期
|
|
@@ -71,14 +74,16 @@ export interface MeetingPostT {
|
|
|
71
74
|
email_list: string; // 通知邮件列表 1020
|
|
72
75
|
is_record: boolean; // 会议录制
|
|
73
76
|
is_cycle: boolean; // 是否重复
|
|
74
|
-
cycle_start_date
|
|
75
|
-
cycle_end_date
|
|
76
|
-
cycle_start
|
|
77
|
-
cycle_end
|
|
77
|
+
cycle_start_date?: string;
|
|
78
|
+
cycle_end_date?: string;
|
|
79
|
+
cycle_start?: string;
|
|
80
|
+
cycle_end?: string;
|
|
78
81
|
cycle_interval: number; // 重复周期, 当intervalType为month时固定为1
|
|
79
82
|
cycle_type: typeof INTERVAL_DAY | typeof INTERVAL_WEEK | typeof INTERVAL_MONTH; // 重复类型,默认为INTERVAL_DAY
|
|
80
83
|
cycle_point: number[]; // 重复节点,当intervalType为week|month时存在值
|
|
81
84
|
is_notify?: boolean;
|
|
85
|
+
mid?: string;
|
|
86
|
+
sub_id?: string;
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
export interface MeetingFormPropsT {
|
|
@@ -96,6 +101,29 @@ export interface MeetingFormPropsT {
|
|
|
96
101
|
bookText?: string
|
|
97
102
|
}
|
|
98
103
|
|
|
104
|
+
export interface ObsDataItemT {
|
|
105
|
+
id: number;
|
|
106
|
+
mid: string;
|
|
107
|
+
sub_id: string;
|
|
108
|
+
status: number;
|
|
109
|
+
text_vtt_url: string;
|
|
110
|
+
text_json_url: string;
|
|
111
|
+
text_video_url: string;
|
|
112
|
+
text_picture_url: string;
|
|
113
|
+
topic_url: string;
|
|
114
|
+
meeting_id: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface CycleSubItemT {
|
|
118
|
+
id: number;
|
|
119
|
+
mid: string;
|
|
120
|
+
sub_id: string;
|
|
121
|
+
date: string;
|
|
122
|
+
start: string;
|
|
123
|
+
end: string;
|
|
124
|
+
meeting_id: number;
|
|
125
|
+
}
|
|
126
|
+
|
|
99
127
|
// 会议详情
|
|
100
128
|
export interface MeetingItemT extends MeetingPostT {
|
|
101
129
|
id: number;
|
|
@@ -108,9 +136,55 @@ export interface MeetingItemT extends MeetingPostT {
|
|
|
108
136
|
is_delete: boolean; // 是否取消
|
|
109
137
|
update_time: string;
|
|
110
138
|
time: string; // 处理一下时间范围
|
|
111
|
-
cycle_sub:
|
|
139
|
+
cycle_sub: CycleSubItemT[];
|
|
112
140
|
live_url?: string;
|
|
113
141
|
type: CalendarDataType;
|
|
142
|
+
obs_data?: ObsDataItemT[]; // 回放数据
|
|
143
|
+
isExpired?: boolean; // 处理数据,是否过期
|
|
144
|
+
sub_id: string; // 处理数据,周期会议平铺之后的id
|
|
145
|
+
dateRange?: string; // 计算属性
|
|
146
|
+
hasObsData?: boolean; // 计算属性
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface MeetingEventsItemT {
|
|
150
|
+
id: number;
|
|
151
|
+
title: string;
|
|
152
|
+
start_date: string;
|
|
153
|
+
end_date: string;
|
|
154
|
+
activity_type: number;
|
|
155
|
+
synopsis: string;
|
|
156
|
+
create_time: string;
|
|
157
|
+
address: string;
|
|
158
|
+
status: number;
|
|
159
|
+
sponsor: string;
|
|
160
|
+
is_delete: number;
|
|
161
|
+
start: string;
|
|
162
|
+
end: string;
|
|
163
|
+
register_url: string;
|
|
164
|
+
content_url: string;
|
|
165
|
+
live_url: null;
|
|
166
|
+
register_end_date: string;
|
|
167
|
+
update_activity_id: null;
|
|
168
|
+
approver: string;
|
|
169
|
+
organizer: string;
|
|
170
|
+
email_list: string;
|
|
171
|
+
dates?: string[]; // 处理数据,如果时跨天活动,有多个元素
|
|
172
|
+
type: CalendarDataType;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface SummitItemT {
|
|
176
|
+
id: string;
|
|
177
|
+
name: string;
|
|
178
|
+
url: string;
|
|
179
|
+
dates: string[];
|
|
180
|
+
location: string;
|
|
181
|
+
address: string;
|
|
182
|
+
start_date: string;
|
|
183
|
+
end_date: string;
|
|
184
|
+
start: string;
|
|
185
|
+
end: string;
|
|
186
|
+
activity_type: number;
|
|
187
|
+
type: CalendarDataType;
|
|
114
188
|
}
|
|
115
189
|
|
|
116
190
|
export interface PageParamsT {
|
|
@@ -118,14 +192,9 @@ export interface PageParamsT {
|
|
|
118
192
|
size: number;
|
|
119
193
|
}
|
|
120
194
|
|
|
121
|
-
export interface OptionItemT {
|
|
122
|
-
label: string;
|
|
123
|
-
value: string | number;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
195
|
export interface meetingTabT {
|
|
127
196
|
label: string;
|
|
128
|
-
value:
|
|
197
|
+
value: CalendarDataType;
|
|
129
198
|
icon: any;
|
|
130
199
|
color?: string;
|
|
131
200
|
zIndex?: number;
|
|
@@ -133,7 +202,7 @@ export interface meetingTabT {
|
|
|
133
202
|
|
|
134
203
|
|
|
135
204
|
// -------------------- 会议回放 --------------------
|
|
136
|
-
interface MeetingPlaybackPropsDetailT {
|
|
205
|
+
export interface MeetingPlaybackPropsDetailT {
|
|
137
206
|
topic: string; // 会议主题
|
|
138
207
|
date: string; // 会议日期
|
|
139
208
|
start: string; // 会议开始时间
|
|
@@ -143,7 +212,7 @@ interface MeetingPlaybackPropsDetailT {
|
|
|
143
212
|
groupName: string; // 会议所属sig组
|
|
144
213
|
}
|
|
145
214
|
|
|
146
|
-
interface MeetingPlaybackPropsDataT {
|
|
215
|
+
export interface MeetingPlaybackPropsDataT {
|
|
147
216
|
mid: string; // 会议ID
|
|
148
217
|
subId?: string; //子会议ID
|
|
149
218
|
textJson: string; // 字幕json文件
|
|
@@ -161,8 +230,10 @@ export interface MeetingPlaybackPropsT {
|
|
|
161
230
|
|
|
162
231
|
export interface SubjectItemT {
|
|
163
232
|
title: string; // 议题
|
|
164
|
-
startTime: string
|
|
165
|
-
endTime: string
|
|
233
|
+
startTime: string; // 议题开始时间 【时:分:秒】格式
|
|
234
|
+
endTime: string; // 议题结束时间 【时:分:秒】格式
|
|
235
|
+
startTimeNumber: number;
|
|
236
|
+
endTimeNumber: number;
|
|
166
237
|
}
|
|
167
238
|
|
|
168
239
|
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, toRef, watch } from 'vue';
|
|
3
|
+
import { onClickOutside, useDebounceFn } from '@vueuse/core';
|
|
4
|
+
|
|
5
|
+
import SearchImageInput from './internal/SearchImageInput.vue';
|
|
6
|
+
import SearchPanel from './internal/SearchPanel.vue';
|
|
7
|
+
import { useSearchHistory } from './composables/useSearchHistory';
|
|
8
|
+
import { useI18n } from '@/i18n';
|
|
9
|
+
import type {
|
|
10
|
+
OSearchPayload,
|
|
11
|
+
OSearchRecommendItem,
|
|
12
|
+
OSearchUploadImageFn,
|
|
13
|
+
} from './types';
|
|
14
|
+
|
|
15
|
+
export interface OSearchInputPropsT {
|
|
16
|
+
modelValue?: string;
|
|
17
|
+
imageUrl?: string;
|
|
18
|
+
size?: 'small' | 'medium' | 'large';
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
imagePlaceholder?: string;
|
|
21
|
+
|
|
22
|
+
/** ---- Image search ---- */
|
|
23
|
+
enableImageSearch?: boolean;
|
|
24
|
+
uploadImage?: OSearchUploadImageFn;
|
|
25
|
+
maxImageSize?: number;
|
|
26
|
+
imageUploadTooltip?: string;
|
|
27
|
+
|
|
28
|
+
/** ---- Recommendation lists (controlled by parent) ---- */
|
|
29
|
+
suggestItems?: OSearchRecommendItem[];
|
|
30
|
+
onestepItems?: OSearchRecommendItem[];
|
|
31
|
+
suggestTitle?: string;
|
|
32
|
+
onestepTitle?: string;
|
|
33
|
+
noDataText?: string;
|
|
34
|
+
/** When true, show "no data" placeholder for empty suggest while typing */
|
|
35
|
+
showSuggestEmpty?: boolean;
|
|
36
|
+
highlightKeyword?: boolean;
|
|
37
|
+
debounce?: number;
|
|
38
|
+
|
|
39
|
+
/** ---- History ---- */
|
|
40
|
+
enableHistory?: boolean;
|
|
41
|
+
historyItems?: string[];
|
|
42
|
+
maxHistoryCount?: number;
|
|
43
|
+
storeHistory?: boolean;
|
|
44
|
+
storageKey?: string;
|
|
45
|
+
historyTitle?: string;
|
|
46
|
+
/** Auto-record history on search; default true */
|
|
47
|
+
autoSaveHistory?: boolean;
|
|
48
|
+
|
|
49
|
+
/** ---- "Did you mean" list (below input) ---- */
|
|
50
|
+
suggestList?: string[];
|
|
51
|
+
suggestListLabel?: string;
|
|
52
|
+
/** When true, render suggestList items as HTML (with v-html on inert string) */
|
|
53
|
+
allowHtmlInSuggest?: boolean;
|
|
54
|
+
|
|
55
|
+
/** ---- Misc ---- */
|
|
56
|
+
clearable?: boolean;
|
|
57
|
+
closeOnSearch?: boolean;
|
|
58
|
+
closeOnClickOutside?: boolean;
|
|
59
|
+
/** Show dropdown on focus even when empty (history) */
|
|
60
|
+
openOnFocus?: boolean;
|
|
61
|
+
/** Always show image as inline thumbnail inside the input; never expand preview below */
|
|
62
|
+
alwaysInlineThumbnail?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const props = withDefaults(defineProps<OSearchInputPropsT>(), {
|
|
66
|
+
modelValue: '',
|
|
67
|
+
imageUrl: '',
|
|
68
|
+
size: 'large',
|
|
69
|
+
enableImageSearch: false,
|
|
70
|
+
maxImageSize: 10 * 1024 * 1024,
|
|
71
|
+
suggestItems: () => [],
|
|
72
|
+
onestepItems: () => [],
|
|
73
|
+
showSuggestEmpty: true,
|
|
74
|
+
highlightKeyword: true,
|
|
75
|
+
debounce: 300,
|
|
76
|
+
enableHistory: true,
|
|
77
|
+
historyItems: () => [],
|
|
78
|
+
maxHistoryCount: 6,
|
|
79
|
+
storeHistory: false,
|
|
80
|
+
storageKey: 'search-history',
|
|
81
|
+
autoSaveHistory: true,
|
|
82
|
+
suggestList: () => [],
|
|
83
|
+
allowHtmlInSuggest: false,
|
|
84
|
+
clearable: true,
|
|
85
|
+
closeOnSearch: true,
|
|
86
|
+
closeOnClickOutside: true,
|
|
87
|
+
openOnFocus: true,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const emit = defineEmits<{
|
|
91
|
+
(e: 'update:modelValue', val: string): void;
|
|
92
|
+
(e: 'update:imageUrl', url: string): void;
|
|
93
|
+
(e: 'update:historyItems', items: string[]): void;
|
|
94
|
+
(e: 'focus'): void;
|
|
95
|
+
(e: 'blur'): void;
|
|
96
|
+
(e: 'input', val: string): void;
|
|
97
|
+
(e: 'clear'): void;
|
|
98
|
+
(e: 'search', payload: OSearchPayload): void;
|
|
99
|
+
(e: 'recommend-click', item: OSearchRecommendItem): void;
|
|
100
|
+
(e: 'onestep-click', item: OSearchRecommendItem): void;
|
|
101
|
+
(e: 'history-click', val: string): void;
|
|
102
|
+
(e: 'suggest-list-click', val: string): void;
|
|
103
|
+
(e: 'delete-history', items: string[]): void;
|
|
104
|
+
(e: 'delete-history-item', val: string): void;
|
|
105
|
+
(e: 'image-upload-start', file: File): void;
|
|
106
|
+
(e: 'image-upload-success', url: string, file: File): void;
|
|
107
|
+
(e: 'image-upload-error', error: unknown, file: File): void;
|
|
108
|
+
(e: 'image-validate-error', reason: 'size' | 'type', file: File): void;
|
|
109
|
+
}>();
|
|
110
|
+
|
|
111
|
+
const { t } = useI18n();
|
|
112
|
+
|
|
113
|
+
const inputRef = ref<InstanceType<typeof SearchImageInput>>();
|
|
114
|
+
const wrapperRef = ref<HTMLElement | null>(null);
|
|
115
|
+
|
|
116
|
+
const innerValue = computed({
|
|
117
|
+
get: () => props.modelValue,
|
|
118
|
+
set: (val: string) => emit('update:modelValue', val),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const isFocus = ref(false);
|
|
122
|
+
const isPreviewOpen = ref(false);
|
|
123
|
+
const justClosedPreview = ref(false);
|
|
124
|
+
const hasInternalImage = ref(false);
|
|
125
|
+
|
|
126
|
+
const showDropdown = computed(() => {
|
|
127
|
+
if (!isFocus.value) return false;
|
|
128
|
+
if (props.imageUrl || hasInternalImage.value) return false;
|
|
129
|
+
const hasSuggest =
|
|
130
|
+
!!innerValue.value &&
|
|
131
|
+
(props.suggestItems.length > 0 || props.onestepItems.length > 0 || props.showSuggestEmpty);
|
|
132
|
+
const hasHistory = props.enableHistory && history.items.value.length > 0 && !innerValue.value;
|
|
133
|
+
if (hasSuggest) return true;
|
|
134
|
+
if (props.openOnFocus && hasHistory) return true;
|
|
135
|
+
return false;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// history
|
|
139
|
+
const historyItemsRef = toRef(props, 'historyItems');
|
|
140
|
+
const storageKeyRef = toRef(props, 'storageKey');
|
|
141
|
+
const storeHistoryRef = toRef(props, 'storeHistory');
|
|
142
|
+
const maxHistoryRef = toRef(props, 'maxHistoryCount');
|
|
143
|
+
|
|
144
|
+
const history = useSearchHistory({
|
|
145
|
+
initial: historyItemsRef,
|
|
146
|
+
storageKey: storageKeyRef,
|
|
147
|
+
storeHistory: storeHistoryRef,
|
|
148
|
+
maxHistoryCount: maxHistoryRef,
|
|
149
|
+
onChange: (items) => emit('update:historyItems', items),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// debounced input event
|
|
153
|
+
const emitInputDebounced = useDebounceFn((val: string) => {
|
|
154
|
+
emit('input', val);
|
|
155
|
+
}, () => props.debounce);
|
|
156
|
+
|
|
157
|
+
watch(innerValue, (val) => {
|
|
158
|
+
emitInputDebounced(val);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// click outside
|
|
162
|
+
onClickOutside(wrapperRef, () => {
|
|
163
|
+
if (!props.closeOnClickOutside) return;
|
|
164
|
+
if (isPreviewOpen.value || justClosedPreview.value) return;
|
|
165
|
+
isFocus.value = false;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const handleFocus = () => {
|
|
169
|
+
isFocus.value = true;
|
|
170
|
+
emit('focus');
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const handleBlur = () => {
|
|
174
|
+
emit('blur');
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const runSearch = async () => {
|
|
178
|
+
if (inputRef.value?.getIsUploading?.()) {
|
|
179
|
+
await inputRef.value.awaitUpload?.();
|
|
180
|
+
}
|
|
181
|
+
const keyword = innerValue.value.trim();
|
|
182
|
+
const imageUrl = inputRef.value?.getUploadedUrl?.() || props.imageUrl;
|
|
183
|
+
|
|
184
|
+
if (!keyword && !imageUrl) return;
|
|
185
|
+
|
|
186
|
+
if (props.autoSaveHistory && keyword) {
|
|
187
|
+
history.push(keyword);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (props.closeOnSearch) {
|
|
191
|
+
isFocus.value = false;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
emit('search', { keyword, imageUrl: imageUrl || undefined });
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const handleClear = () => {
|
|
198
|
+
innerValue.value = '';
|
|
199
|
+
emit('clear');
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const handleSuggestClick = (item: OSearchRecommendItem) => {
|
|
203
|
+
emit('recommend-click', item);
|
|
204
|
+
innerValue.value = item.key;
|
|
205
|
+
runSearch();
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const handleOnestepClick = (item: OSearchRecommendItem) => {
|
|
209
|
+
emit('onestep-click', item);
|
|
210
|
+
if (item.path) {
|
|
211
|
+
const url = item.path;
|
|
212
|
+
if (typeof window !== 'undefined') window.open(url, '_blank', 'noopener,noreferrer');
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const handleHistoryClick = (val: string) => {
|
|
217
|
+
emit('history-click', val);
|
|
218
|
+
innerValue.value = val;
|
|
219
|
+
runSearch();
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const handleHistoryRemove = (val: string) => {
|
|
223
|
+
history.remove(val);
|
|
224
|
+
emit('delete-history-item', val);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const handleHistoryClear = () => {
|
|
228
|
+
const removed = [...history.items.value];
|
|
229
|
+
history.clearAll();
|
|
230
|
+
emit('delete-history', removed);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const handleSuggestListClick = (val: string) => {
|
|
234
|
+
// Strip HTML tags when raw HTML is allowed for display
|
|
235
|
+
const text = props.allowHtmlInSuggest ? val.replace(/<[^>]+>/g, '') : val;
|
|
236
|
+
emit('suggest-list-click', text);
|
|
237
|
+
innerValue.value = text;
|
|
238
|
+
runSearch();
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const onPreviewChange = (visible: boolean) => {
|
|
242
|
+
isPreviewOpen.value = visible;
|
|
243
|
+
if (!visible) {
|
|
244
|
+
justClosedPreview.value = true;
|
|
245
|
+
setTimeout(() => {
|
|
246
|
+
justClosedPreview.value = false;
|
|
247
|
+
}, 100);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const showInlineThumbnail = computed(() =>
|
|
252
|
+
!!props.imageUrl && (props.alwaysInlineThumbnail || !isFocus.value)
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const onImageUploadStart = (file: File) => {
|
|
256
|
+
hasInternalImage.value = true;
|
|
257
|
+
emit('image-upload-start', file);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const onImageUploadSuccess = (url: string, file: File) => {
|
|
261
|
+
hasInternalImage.value = false;
|
|
262
|
+
emit('image-upload-success', url, file);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const onImageClear = () => {
|
|
266
|
+
hasInternalImage.value = false;
|
|
267
|
+
emit('update:imageUrl', '');
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
defineExpose({
|
|
271
|
+
focus: () => inputRef.value?.focus?.(),
|
|
272
|
+
blur: () => inputRef.value?.blur?.(),
|
|
273
|
+
search: runSearch,
|
|
274
|
+
saveHistory: (val?: string) => history.push(val ?? innerValue.value),
|
|
275
|
+
});
|
|
276
|
+
</script>
|
|
277
|
+
|
|
278
|
+
<template>
|
|
279
|
+
<div ref="wrapperRef" class="o-search-input" :class="{ 'is-focus': isFocus }">
|
|
280
|
+
<div class="o-search-input-box">
|
|
281
|
+
<SearchImageInput
|
|
282
|
+
ref="inputRef"
|
|
283
|
+
v-model="innerValue"
|
|
284
|
+
:image-url="imageUrl"
|
|
285
|
+
:placeholder="placeholder"
|
|
286
|
+
:image-placeholder="imagePlaceholder"
|
|
287
|
+
:size="size"
|
|
288
|
+
:enable-image-search="enableImageSearch"
|
|
289
|
+
:upload-image="uploadImage"
|
|
290
|
+
:max-image-size="maxImageSize"
|
|
291
|
+
:image-upload-tooltip="imageUploadTooltip"
|
|
292
|
+
:expanded="isFocus && !props.alwaysInlineThumbnail"
|
|
293
|
+
:inline-thumbnail="showInlineThumbnail"
|
|
294
|
+
:clearable="clearable"
|
|
295
|
+
class="o-search-input-field"
|
|
296
|
+
@update:imageUrl="(url: string) => emit('update:imageUrl', url)"
|
|
297
|
+
@focus="handleFocus"
|
|
298
|
+
@blur="handleBlur"
|
|
299
|
+
@enter="runSearch"
|
|
300
|
+
@clear="handleClear"
|
|
301
|
+
@image-clear="onImageClear"
|
|
302
|
+
@image-upload-start="onImageUploadStart"
|
|
303
|
+
@image-upload-success="onImageUploadSuccess"
|
|
304
|
+
@image-upload-error="(error: unknown, file: File) => emit('image-upload-error', error, file)"
|
|
305
|
+
@image-validate-error="(reason: 'size' | 'type', file: File) => emit('image-validate-error', reason, file)"
|
|
306
|
+
@preview-change="onPreviewChange"
|
|
307
|
+
>
|
|
308
|
+
<template #prefix><slot name="input-prefix" /></template>
|
|
309
|
+
<template #suffix="slotProps"><slot name="input-suffix" v-bind="slotProps" /></template>
|
|
310
|
+
<template v-if="$slots['image-preview']" #preview="slotProps">
|
|
311
|
+
<slot name="image-preview" v-bind="slotProps" />
|
|
312
|
+
</template>
|
|
313
|
+
</SearchImageInput>
|
|
314
|
+
|
|
315
|
+
<Transition name="o-search-input-dropdown">
|
|
316
|
+
<div v-if="showDropdown" class="o-search-input-dropdown">
|
|
317
|
+
<slot name="dropdown" :keyword="modelValue">
|
|
318
|
+
<SearchPanel
|
|
319
|
+
:keyword="modelValue"
|
|
320
|
+
:onestep-items="onestepItems"
|
|
321
|
+
:onestep-title="onestepTitle"
|
|
322
|
+
:suggest-items="suggestItems"
|
|
323
|
+
:suggest-title="suggestTitle"
|
|
324
|
+
:history-items="enableHistory ? history.items.value : []"
|
|
325
|
+
:history-title="historyTitle"
|
|
326
|
+
:no-data-text="noDataText"
|
|
327
|
+
:highlight-keyword="highlightKeyword"
|
|
328
|
+
:hide-on-keyword="true"
|
|
329
|
+
:show-suggest-empty="showSuggestEmpty"
|
|
330
|
+
history-layout="list"
|
|
331
|
+
@onestep-click="handleOnestepClick"
|
|
332
|
+
@suggest-click="handleSuggestClick"
|
|
333
|
+
@history-click="handleHistoryClick"
|
|
334
|
+
@history-remove="handleHistoryRemove"
|
|
335
|
+
@history-clear="handleHistoryClear"
|
|
336
|
+
>
|
|
337
|
+
<template v-if="$slots['onestep-header']" #onestep-header="slotProps">
|
|
338
|
+
<slot name="onestep-header" v-bind="slotProps" />
|
|
339
|
+
</template>
|
|
340
|
+
<template v-if="$slots['onestep-content']" #onestep-content="slotProps">
|
|
341
|
+
<slot name="onestep-content" v-bind="slotProps" />
|
|
342
|
+
</template>
|
|
343
|
+
<template v-if="$slots['suggest-header']" #suggest-header="slotProps">
|
|
344
|
+
<slot name="suggest-header" v-bind="slotProps" />
|
|
345
|
+
</template>
|
|
346
|
+
<template v-if="$slots['suggest-content']" #suggest-content="slotProps">
|
|
347
|
+
<slot name="suggest-content" v-bind="slotProps" />
|
|
348
|
+
</template>
|
|
349
|
+
<template v-if="$slots['history-header']" #history-header="slotProps">
|
|
350
|
+
<slot name="history-header" v-bind="slotProps" />
|
|
351
|
+
</template>
|
|
352
|
+
<template v-if="$slots['history-content']" #history-content="slotProps">
|
|
353
|
+
<slot name="history-content" v-bind="slotProps" />
|
|
354
|
+
</template>
|
|
355
|
+
</SearchPanel>
|
|
356
|
+
</slot>
|
|
357
|
+
</div>
|
|
358
|
+
</Transition>
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
<div v-if="suggestList?.length" class="o-search-input-suggest-list-row">
|
|
362
|
+
<slot name="suggest-list" :items="suggestList">
|
|
363
|
+
<span class="o-search-input-suggest-list-label">{{ suggestListLabel ?? t('search.suggestListLabel') }}</span>
|
|
364
|
+
<ul class="o-search-input-suggest-list">
|
|
365
|
+
<li
|
|
366
|
+
v-for="(item, idx) in suggestList"
|
|
367
|
+
:key="item + idx"
|
|
368
|
+
class="o-search-input-suggest-list-item"
|
|
369
|
+
@click="handleSuggestListClick(item)"
|
|
370
|
+
>
|
|
371
|
+
<span v-if="allowHtmlInSuggest" v-html="item" />
|
|
372
|
+
<span v-else>{{ item }}</span>
|
|
373
|
+
</li>
|
|
374
|
+
</ul>
|
|
375
|
+
</slot>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
</template>
|
|
379
|
+
|
|
380
|
+
<style lang="scss" scoped>
|
|
381
|
+
.o-search-input {
|
|
382
|
+
position: relative;
|
|
383
|
+
width: 100%;
|
|
384
|
+
background-color: var(--o-color-fill2);
|
|
385
|
+
border-radius: var(--o-radius-xs);
|
|
386
|
+
|
|
387
|
+
// Make large size 48px tall
|
|
388
|
+
:deep(.o-input.o_box-large) {
|
|
389
|
+
--_box-height: 48px;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.o-search-input-box {
|
|
394
|
+
position: relative;
|
|
395
|
+
width: 100%;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.o-search-input-field {
|
|
399
|
+
width: 100%;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.o-search-input-dropdown {
|
|
403
|
+
position: absolute;
|
|
404
|
+
top: calc(100% + 4px);
|
|
405
|
+
left: 0;
|
|
406
|
+
right: 0;
|
|
407
|
+
z-index: 10;
|
|
408
|
+
padding: var(--o-gap-4);
|
|
409
|
+
background-color: var(--o-color-fill2);
|
|
410
|
+
border-radius: var(--o-radius-xs);
|
|
411
|
+
box-shadow: var(--o-shadow-2);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.o-search-input-dropdown-enter-active,
|
|
415
|
+
.o-search-input-dropdown-leave-active {
|
|
416
|
+
transition: opacity var(--o-duration-m1), transform var(--o-duration-m1);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.o-search-input-dropdown-enter-from,
|
|
420
|
+
.o-search-input-dropdown-leave-to {
|
|
421
|
+
opacity: 0;
|
|
422
|
+
transform: translateY(-4px);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.o-search-input-suggest-list-row {
|
|
426
|
+
display: flex;
|
|
427
|
+
margin-top: 8px;
|
|
428
|
+
align-items: center;
|
|
429
|
+
flex-wrap: wrap;
|
|
430
|
+
@include tip1;
|
|
431
|
+
color: var(--o-color-info1);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.o-search-input-suggest-list-label {
|
|
435
|
+
color: var(--o-color-info3);
|
|
436
|
+
margin-right: 4px;
|
|
437
|
+
@include tip1;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.o-search-input-suggest-list {
|
|
441
|
+
display: flex;
|
|
442
|
+
flex-wrap: wrap;
|
|
443
|
+
align-items: center;
|
|
444
|
+
padding: 0;
|
|
445
|
+
margin: 0;
|
|
446
|
+
list-style: none;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.o-search-input-suggest-list-item {
|
|
450
|
+
margin-right: 8px;
|
|
451
|
+
cursor: pointer;
|
|
452
|
+
color: var(--o-color-primary1);
|
|
453
|
+
|
|
454
|
+
:deep(em) {
|
|
455
|
+
color: var(--o-color-primary1);
|
|
456
|
+
font-style: normal;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
@include hover {
|
|
460
|
+
text-decoration: underline;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
</style>
|