plain-design 1.0.0-beta.78 → 1.0.0-beta.79
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/plain-design.commonjs.min.js +2 -2
- package/dist/plain-design.min.css +1 -0
- package/dist/plain-design.min.js +2 -2
- package/dist/report.html +2 -2
- package/package.json +1 -1
- package/src/packages/components/$search/SearchFooter.tsx +32 -0
- package/src/packages/components/$search/SearchList.tsx +206 -0
- package/src/packages/components/$search/SearchServicePanel.tsx +233 -0
- package/src/packages/components/$search/createSearchService.tsx +43 -0
- package/src/packages/components/$search/index.tsx +6 -0
- package/src/packages/components/$search/search-service.scss +220 -0
- package/src/packages/components/$search/search.utils.tsx +112 -0
- package/src/packages/components/Input/index.tsx +9 -1
- package/src/packages/components/Input/input.utils.ts +2 -1
- package/src/packages/components/Input/uses/useInputSuffixIcon.tsx +1 -1
- package/src/packages/components/Scroll/index.tsx +34 -0
- package/src/packages/components/Table/standard/PlcTree/PlcTree.renderNode.tsx +3 -0
- package/src/packages/entry.tsx +8 -0
- package/src/packages/i18n/lang/en-us.ts +9 -1
- package/src/packages/i18n/lang/zh-cn.ts +11 -3
- package/src/packages/utils/createListUtils.ts +38 -0
package/package.json
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
import {designPage} from "plain-design-composition";
|
2
|
+
import {i18n} from "../i18n";
|
3
|
+
|
4
|
+
export const SearchFooter = designPage(() => {
|
5
|
+
return () => (
|
6
|
+
<>
|
7
|
+
<svg width="15" height="15" aria-label="Enter key" role="img">
|
8
|
+
<g fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.2">
|
9
|
+
<path d="M12 3.53088v3c0 1-1 2-2 2H4M7 11.53088l-3-3 3-3"></path>
|
10
|
+
</g>
|
11
|
+
</svg>
|
12
|
+
<span>{i18n.$it('search.select').d('选择')}</span>
|
13
|
+
<svg width="15" height="15" aria-label="Arrow down" role="img">
|
14
|
+
<g fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.2">
|
15
|
+
<path d="M7.5 3.5v8M10.5 8.5l-3 3-3-3"></path>
|
16
|
+
</g>
|
17
|
+
</svg>
|
18
|
+
<svg width="15" height="15" aria-label="Arrow up" role="img">
|
19
|
+
<g fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.2">
|
20
|
+
<path d="M7.5 11.5v-8M10.5 6.5l-3-3-3 3"></path>
|
21
|
+
</g>
|
22
|
+
</svg>
|
23
|
+
<span>{i18n.$it('search.switch').d('切换')}</span>
|
24
|
+
<svg width="15" height="15" aria-label="Escape key" role="img">
|
25
|
+
<g fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.2">
|
26
|
+
<path d="M13.6167 8.936c-.1065.3583-.6883.962-1.4875.962-.7993 0-1.653-.9165-1.653-2.1258v-.5678c0-1.2548.7896-2.1016 1.653-2.1016.8634 0 1.3601.4778 1.4875 1.0724M9 6c-.1352-.4735-.7506-.9219-1.46-.8972-.7092.0246-1.344.57-1.344 1.2166s.4198.8812 1.3445.9805C8.465 7.3992 8.968 7.9337 9 8.5c.032.5663-.454 1.398-1.4595 1.398C6.6593 9.898 6 9 5.963 8.4851m-1.4748.5368c-.2635.5941-.8099.876-1.5443.876s-1.7073-.6248-1.7073-2.204v-.4603c0-1.0416.721-2.131 1.7073-2.131.9864 0 1.6425 1.031 1.5443 2.2492h-2.956"></path>
|
27
|
+
</g>
|
28
|
+
</svg>
|
29
|
+
<span>{i18n.$it('base.close').d('关闭')}</span>
|
30
|
+
</>
|
31
|
+
);
|
32
|
+
});
|
@@ -0,0 +1,206 @@
|
|
1
|
+
import {computed, designComponent, iMouseEvent, PropType, reactive, useRefs} from "plain-design-composition";
|
2
|
+
import {iSearchDataMeta, iSearchServiceConfig, SearchOptionButton, SearchTreeIcon, SearchType2Icon} from "./search.utils";
|
3
|
+
import {createListUtils} from "../../utils/createListUtils";
|
4
|
+
import {delay} from "plain-utils/utils/delay";
|
5
|
+
import VirtualList from "../VirtualList";
|
6
|
+
|
7
|
+
export const SearchList = designComponent({
|
8
|
+
props: {
|
9
|
+
data: { type: Array as PropType<iSearchDataMeta[]>, required: true },
|
10
|
+
config: { type: Object as PropType<iSearchServiceConfig>, required: true }
|
11
|
+
},
|
12
|
+
slots: ['default'],
|
13
|
+
emits: {
|
14
|
+
/*选中选项事件*/
|
15
|
+
onSelect: (val: iSearchDataMeta) => true,
|
16
|
+
/*删除选项事件,选项可能是历史选项,也可能是收藏选项*/
|
17
|
+
onRemoveItem: (val: iSearchDataMeta) => true,
|
18
|
+
/*添加选项为收藏选项*/
|
19
|
+
onAddFavorite: (val: iSearchDataMeta) => true,
|
20
|
+
},
|
21
|
+
setup({ props, slots, event: { emit } }) {
|
22
|
+
|
23
|
+
const { refs, onRef } = useRefs({ virtual: VirtualList });
|
24
|
+
|
25
|
+
const state = reactive({
|
26
|
+
/*当前选中的选项位置索引*/
|
27
|
+
selectIndex: null as null | number
|
28
|
+
});
|
29
|
+
|
30
|
+
/*有效可选中的选项数组*/
|
31
|
+
const availableList = computed(() => props.data.filter(i => i.type !== 'group'));
|
32
|
+
/*选中选项在有效选项数组中的位置索引*/
|
33
|
+
const availableIndex = computed(() => state.selectIndex == null ? null : availableList.value.indexOf(props.data[state.selectIndex]));
|
34
|
+
|
35
|
+
const listUtils = createListUtils({ getList: () => availableList.value, current: () => availableIndex.value, });
|
36
|
+
|
37
|
+
const methods = {
|
38
|
+
/**
|
39
|
+
* 选中上一个节点
|
40
|
+
* @author 韦胜健
|
41
|
+
* @date 2024.6.23 21:27
|
42
|
+
*/
|
43
|
+
selectPrev: async () => {
|
44
|
+
const newAvailableIndex = listUtils.prevIndex();
|
45
|
+
if (newAvailableIndex == -1) {return;}
|
46
|
+
const newSelectIndex = props.data.indexOf(availableList.value[newAvailableIndex]);
|
47
|
+
if (!!refs.virtual && availableIndex.value == 0 && newAvailableIndex == availableList.value.length - 1) {
|
48
|
+
/*滚动到底部再选中选项*/
|
49
|
+
refs.virtual.refs.scroll?.methods.scrollEnd();
|
50
|
+
await delay(78);
|
51
|
+
} else {
|
52
|
+
refs.virtual?.refs.scroll?.methods.showElement(`[data-index="_${newSelectIndex}"]`);
|
53
|
+
}
|
54
|
+
state.selectIndex = newSelectIndex;
|
55
|
+
},
|
56
|
+
/**
|
57
|
+
* 选中下一个节点
|
58
|
+
* @author 韦胜健
|
59
|
+
* @date 2024.6.23 21:28
|
60
|
+
*/
|
61
|
+
selectNext: async () => {
|
62
|
+
const newAvailableIndex = listUtils.nextIndex();
|
63
|
+
if (newAvailableIndex == -1) {return;}
|
64
|
+
const newSelectIndex = props.data.indexOf(availableList.value[newAvailableIndex]);
|
65
|
+
if (!!refs.virtual && availableIndex.value == availableList.value.length - 1 && newAvailableIndex == 0) {
|
66
|
+
/*滚动到顶部再选中选项*/
|
67
|
+
refs.virtual.refs.scroll?.methods.scrollTop(0);
|
68
|
+
await delay(78);
|
69
|
+
} else {
|
70
|
+
refs.virtual?.refs.scroll?.methods.showElement(`[data-index="_${newSelectIndex}"]`);
|
71
|
+
}
|
72
|
+
state.selectIndex = newSelectIndex;
|
73
|
+
},
|
74
|
+
/**
|
75
|
+
* 获取当前选中的节点
|
76
|
+
* @author 韦胜健
|
77
|
+
* @date 2024.6.23 21:28
|
78
|
+
*/
|
79
|
+
getSelected: (): iSearchDataMeta | null => {
|
80
|
+
return state.selectIndex == null ? null : props.data[state.selectIndex];
|
81
|
+
},
|
82
|
+
/**
|
83
|
+
* 选中节点
|
84
|
+
* @author 韦胜健
|
85
|
+
* @date 2024.6.23 21:28
|
86
|
+
*/
|
87
|
+
selectItem: (dataMeta: iSearchDataMeta) => {
|
88
|
+
emit.onSelect(dataMeta);
|
89
|
+
},
|
90
|
+
/**
|
91
|
+
* 删除收藏或者历史
|
92
|
+
* @author 韦胜健
|
93
|
+
* @date 2024.6.23 21:58
|
94
|
+
*/
|
95
|
+
removeItem: (e: iMouseEvent, dataMeta: iSearchDataMeta) => {
|
96
|
+
e.stopPropagation();
|
97
|
+
emit.onRemoveItem(dataMeta);
|
98
|
+
},
|
99
|
+
/**
|
100
|
+
* 添加收藏
|
101
|
+
* @author 韦胜健
|
102
|
+
* @date 2024.6.23 21:58
|
103
|
+
*/
|
104
|
+
addFavorite: (e: iMouseEvent, dataMeta: iSearchDataMeta) => {
|
105
|
+
e.stopPropagation();
|
106
|
+
emit.onAddFavorite(dataMeta);
|
107
|
+
},
|
108
|
+
/**
|
109
|
+
* 重置选中节点
|
110
|
+
* @author 韦胜健
|
111
|
+
* @date 2024.6.23 23:42
|
112
|
+
*/
|
113
|
+
resetSelectIndex: async () => {
|
114
|
+
await delay();
|
115
|
+
state.selectIndex = !availableList.value.length ? null : props.data.indexOf(availableList.value[0]);
|
116
|
+
},
|
117
|
+
};
|
118
|
+
|
119
|
+
const renderItem = (
|
120
|
+
{ item, vid, index }: {
|
121
|
+
item: iSearchDataMeta, vid: string, index: number
|
122
|
+
}
|
123
|
+
) => {
|
124
|
+
|
125
|
+
/**
|
126
|
+
* 是否为最后一个subHeader,如果返回值为null,表明不是sub_header,否则true为最后一个sub_header,false为普通sub_header
|
127
|
+
* @author 韦胜健
|
128
|
+
* @date 2024.6.23 23:49
|
129
|
+
*/
|
130
|
+
const isLastSubHeader = (() => {
|
131
|
+
if (item.type !== 'sub_header') {return null;}
|
132
|
+
const nextItem = index + 1 > props.data.length - 1 ? null : props.data[index + 1];
|
133
|
+
return !(!!nextItem && nextItem.type === 'sub_header');
|
134
|
+
})();
|
135
|
+
|
136
|
+
return (
|
137
|
+
<div
|
138
|
+
className={`search-service-option-item`}
|
139
|
+
data-active={String(state.selectIndex === index)}
|
140
|
+
key={vid}
|
141
|
+
data-vid={vid}
|
142
|
+
data-index={`_${index}`}
|
143
|
+
onMouseEnter={() => item.type != 'group' && (state.selectIndex = index)}
|
144
|
+
>
|
145
|
+
{(props.config.render || ((item: iSearchDataMeta) => {
|
146
|
+
return (
|
147
|
+
<div className="search-service-option-item-default" data-service-item-type={item.type}>
|
148
|
+
{item.type == 'group' ? (
|
149
|
+
<div className="search-service-option-item-default-title">{item.title}</div>
|
150
|
+
) : (
|
151
|
+
<div className="search-service-option-item-default-box" onClick={() => methods.selectItem(item)}>
|
152
|
+
{isLastSubHeader != null && SearchTreeIcon[isLastSubHeader ? 'last' : 'normal']()}
|
153
|
+
{SearchType2Icon[item.type]()}
|
154
|
+
<div className="search-service-option-item-default-label">
|
155
|
+
{item.title && <span>{item.title}</span>}
|
156
|
+
{item.desc && <span>{item.desc}</span>}
|
157
|
+
</div>
|
158
|
+
{item.type === 'favorite' ?
|
159
|
+
SearchOptionButton.remove((e) => methods.removeItem(e, item)) :
|
160
|
+
item.type === 'history' ? <>
|
161
|
+
{SearchOptionButton.favorite((e) => methods.addFavorite(e, item))}
|
162
|
+
{SearchOptionButton.remove((e) => methods.removeItem(e, item))}
|
163
|
+
</> : SearchOptionButton.normal()}
|
164
|
+
</div>
|
165
|
+
)}
|
166
|
+
</div>
|
167
|
+
);
|
168
|
+
}))(item)}
|
169
|
+
</div>
|
170
|
+
);
|
171
|
+
};
|
172
|
+
|
173
|
+
return {
|
174
|
+
refer: {
|
175
|
+
refs,
|
176
|
+
methods,
|
177
|
+
},
|
178
|
+
render: () => (
|
179
|
+
!props.data.length ? (
|
180
|
+
<div className="search-service-empty" key="empty">
|
181
|
+
{slots.default()}
|
182
|
+
</div>
|
183
|
+
) : (
|
184
|
+
<div className="search-service-list" key="list">
|
185
|
+
{availableList.value.length <= 6 ? (
|
186
|
+
/*不超过6个元素,就直接渲染选项数组*/
|
187
|
+
props.data.map((item, index) => (renderItem({ item, vid: `_${(item.title || String(index)) + (item.desc || String(index))}`, index })))
|
188
|
+
) : (
|
189
|
+
/*超过6个就渲染虚拟列表*/
|
190
|
+
<div className="search-service-option-virtual-list">
|
191
|
+
<VirtualList
|
192
|
+
ref={onRef.virtual}
|
193
|
+
size={56}
|
194
|
+
dynamicSize
|
195
|
+
data={props.data}
|
196
|
+
alwaysShowScrollbar
|
197
|
+
v-slots={{ default: ({ item, vid, index }) => renderItem({ item, vid, index }) }}
|
198
|
+
/>
|
199
|
+
</div>
|
200
|
+
)}
|
201
|
+
</div>
|
202
|
+
)
|
203
|
+
)
|
204
|
+
};
|
205
|
+
},
|
206
|
+
});
|
@@ -0,0 +1,233 @@
|
|
1
|
+
import {computed, createStore, designComponent, getComponentCls, onMounted, PropType, reactive, useClassCache, useRefs} from "plain-design-composition";
|
2
|
+
import {iSearchDataMeta, iSearchServiceConfig} from "./search.utils";
|
3
|
+
import {debounce} from "plain-utils/utils/debounce";
|
4
|
+
import {handleKeyboard} from "../KeyboardService";
|
5
|
+
import {Box} from "../Box";
|
6
|
+
import Input from "../Input";
|
7
|
+
import {SearchList} from "./SearchList";
|
8
|
+
import {SearchFooter} from "./SearchFooter";
|
9
|
+
import ApplicationConfigurationProvider from "../ApplicationConfigurationProvider";
|
10
|
+
import {i18n} from "../../i18n";
|
11
|
+
|
12
|
+
export const SearchServicePanel = designComponent({
|
13
|
+
props: {
|
14
|
+
config: { type: Object as PropType<iSearchServiceConfig>, required: true }
|
15
|
+
},
|
16
|
+
emits: {
|
17
|
+
onClose: () => true,
|
18
|
+
},
|
19
|
+
setup({ props, event: { emit } }) {
|
20
|
+
|
21
|
+
const configuration = ApplicationConfigurationProvider.inject();
|
22
|
+
|
23
|
+
/*微调输入框以及footer在黑白主题下的颜色*/
|
24
|
+
const inputBackgroundColor = computed(() => ({ backgroundColor: configuration.value.theme.vars[configuration.value.theme.dark ? "bg-1" : 'bg-2'] }));
|
25
|
+
|
26
|
+
/**
|
27
|
+
* 搜索缓存
|
28
|
+
* @author 韦胜健
|
29
|
+
* @date 2024.6.23 21:21
|
30
|
+
*/
|
31
|
+
const searchCache = createStore({
|
32
|
+
initialState: {
|
33
|
+
history: [] as iSearchDataMeta[],
|
34
|
+
favorite: [] as iSearchDataMeta[],
|
35
|
+
},
|
36
|
+
getCacheConfig: () => ({ envName: getComponentCls(''), cacheName: props.config.cacheName || `@@search_service}` })
|
37
|
+
});
|
38
|
+
|
39
|
+
const { refs, onRef } = useRefs({ list: SearchList });
|
40
|
+
|
41
|
+
const state = reactive({
|
42
|
+
/*当前输入的搜索关键词*/
|
43
|
+
searchText: '',
|
44
|
+
/*展示的选项数据*/
|
45
|
+
data: [] as iSearchDataMeta[],
|
46
|
+
/*当前是否处于加载状态*/
|
47
|
+
loading: false,
|
48
|
+
/*当前是否处于输入状态*/
|
49
|
+
editing: false,
|
50
|
+
});
|
51
|
+
|
52
|
+
const historyData = computed(() => {
|
53
|
+
const data: iSearchDataMeta[] = [];
|
54
|
+
if (!!searchCache.value.history.length) {
|
55
|
+
data.push({ title: i18n.$it('search.searchHistory').d('搜索历史'), type: 'group' });
|
56
|
+
data.push(...searchCache.value.history);
|
57
|
+
}
|
58
|
+
if (!!searchCache.value.favorite.length) {
|
59
|
+
data.push({ title: i18n.$it('search.favorite').d('收藏'), type: 'group' });
|
60
|
+
data.push(...searchCache.value.favorite);
|
61
|
+
}
|
62
|
+
return data;
|
63
|
+
});
|
64
|
+
|
65
|
+
const classes = useClassCache(() => [
|
66
|
+
getComponentCls('search-service-panel')
|
67
|
+
]);
|
68
|
+
|
69
|
+
const handler = {
|
70
|
+
/**
|
71
|
+
* 每次输入搜索关键词的时候都重置数据,防抖执行
|
72
|
+
* @author 韦胜健
|
73
|
+
* @date 2024.6.23 21:23
|
74
|
+
*/
|
75
|
+
resetData: debounce(async () => {
|
76
|
+
if (!state.searchText.trim().length) {
|
77
|
+
state.data = [];
|
78
|
+
state.editing = false;
|
79
|
+
refs.list?.methods.resetSelectIndex();
|
80
|
+
return;
|
81
|
+
}
|
82
|
+
state.loading = true;
|
83
|
+
try {
|
84
|
+
state.data = await props.config.getData(state.searchText);
|
85
|
+
} catch (e) {
|
86
|
+
state.data = [];
|
87
|
+
throw e;
|
88
|
+
} finally {
|
89
|
+
state.loading = false;
|
90
|
+
state.editing = false;
|
91
|
+
refs.list?.methods.resetSelectIndex();
|
92
|
+
}
|
93
|
+
}, 300),
|
94
|
+
/**
|
95
|
+
* 搜索关键词变化的话立即标识处于输入状态
|
96
|
+
* @author 韦胜健
|
97
|
+
* @date 2024.6.23 21:24
|
98
|
+
*/
|
99
|
+
onInputChange: () => {
|
100
|
+
state.editing = true;
|
101
|
+
handler.resetData();
|
102
|
+
},
|
103
|
+
/**
|
104
|
+
* 处理上下以及enter,esc快捷键
|
105
|
+
* @author 韦胜健
|
106
|
+
* @date 2024.6.23 21:24
|
107
|
+
*/
|
108
|
+
onKeydown: handleKeyboard({
|
109
|
+
up: async (e) => {
|
110
|
+
e.preventDefault();
|
111
|
+
e.stopPropagation();
|
112
|
+
refs.list?.methods.selectPrev();
|
113
|
+
},
|
114
|
+
down: async (e) => {
|
115
|
+
e.preventDefault();
|
116
|
+
e.stopPropagation();
|
117
|
+
refs.list?.methods.selectNext();
|
118
|
+
},
|
119
|
+
enter: (e) => {
|
120
|
+
e.preventDefault();
|
121
|
+
e.stopPropagation();
|
122
|
+
const target = refs.list?.methods.getSelected();
|
123
|
+
if (!target) {return;}
|
124
|
+
handler.onSelectItem(target);
|
125
|
+
},
|
126
|
+
esc: (e) => {
|
127
|
+
e.preventDefault();
|
128
|
+
e.stopPropagation();
|
129
|
+
emit.onClose();
|
130
|
+
},
|
131
|
+
}),
|
132
|
+
/**
|
133
|
+
* 处理选中某个选项的动作
|
134
|
+
* @author 韦胜健
|
135
|
+
* @date 2024.6.23 21:24
|
136
|
+
*/
|
137
|
+
onSelectItem: (dataMeta: iSearchDataMeta) => {
|
138
|
+
if (!historyData.value.find(i => i.title === dataMeta.title && i.desc === dataMeta.desc)) {
|
139
|
+
dataMeta = { ...dataMeta, type: 'history' };
|
140
|
+
searchCache.value.history.unshift(dataMeta);
|
141
|
+
if (searchCache.value.history.length > 5) {searchCache.value.history.pop();}
|
142
|
+
searchCache.value = { ...searchCache.value };
|
143
|
+
}
|
144
|
+
props.config.onSelect(dataMeta);
|
145
|
+
emit.onClose();
|
146
|
+
},
|
147
|
+
onRemoveItem: (dataMeta: iSearchDataMeta) => {
|
148
|
+
const historyIndex = searchCache.value.history.findIndex(i => i.title === dataMeta.title && i.desc === dataMeta.desc && i.type === 'history');
|
149
|
+
if (historyIndex > -1) {
|
150
|
+
searchCache.value.history.splice(historyIndex, 1);
|
151
|
+
searchCache.value = { ...searchCache.value };
|
152
|
+
return;
|
153
|
+
}
|
154
|
+
const favoriteIndex = searchCache.value.favorite.findIndex(i => i.title === dataMeta.title && i.desc === dataMeta.desc && i.type === 'favorite');
|
155
|
+
if (favoriteIndex > -1) {
|
156
|
+
searchCache.value.favorite.splice(favoriteIndex, 1);
|
157
|
+
searchCache.value = { ...searchCache.value };
|
158
|
+
return;
|
159
|
+
}
|
160
|
+
},
|
161
|
+
onAddFavorite: (dataMeta: iSearchDataMeta) => {
|
162
|
+
handler.onRemoveItem(dataMeta);
|
163
|
+
dataMeta = { ...dataMeta, type: 'favorite' };
|
164
|
+
searchCache.value.favorite.unshift(dataMeta);
|
165
|
+
searchCache.value = { ...searchCache.value };
|
166
|
+
},
|
167
|
+
};
|
168
|
+
|
169
|
+
const renderList = (data: iSearchDataMeta[], empty: () => any) => {
|
170
|
+
return (
|
171
|
+
<SearchList
|
172
|
+
ref={onRef.list}
|
173
|
+
data={data}
|
174
|
+
config={props.config}
|
175
|
+
onSelect={handler.onSelectItem}
|
176
|
+
onRemoveItem={handler.onRemoveItem}
|
177
|
+
onAddFavorite={handler.onAddFavorite}
|
178
|
+
>
|
179
|
+
{empty()}
|
180
|
+
</SearchList>
|
181
|
+
);
|
182
|
+
};
|
183
|
+
|
184
|
+
onMounted(() => {
|
185
|
+
refs.list?.methods.resetSelectIndex();
|
186
|
+
});
|
187
|
+
|
188
|
+
return () => (
|
189
|
+
<Box className={classes.value}>
|
190
|
+
<div className="search-service-input-box">
|
191
|
+
<Input
|
192
|
+
style={inputBackgroundColor.value}
|
193
|
+
autoFocus
|
194
|
+
prefixIcon="pi-search"
|
195
|
+
v-model={state.searchText}
|
196
|
+
placeholder={props.config.placeholder}
|
197
|
+
inputMode="stroke"
|
198
|
+
onChange={handler.onInputChange}
|
199
|
+
loading={state.loading}
|
200
|
+
loadingType="kappa"
|
201
|
+
onKeyDown={handler.onKeydown}
|
202
|
+
/>
|
203
|
+
</div>
|
204
|
+
<div className="search-service-body">
|
205
|
+
{!state.searchText.trim().length || (state.editing && !state.data.length) ? (
|
206
|
+
/*如果没有搜索关键词,或者处于输入关键词状态并且没有数据的话,显示搜索历史*/
|
207
|
+
renderList(historyData.value, () => (
|
208
|
+
<div className="search-service-panel-history" key="history">
|
209
|
+
{i18n.$it('search.noHistory').d('没有搜索历史')}
|
210
|
+
</div>))
|
211
|
+
) : (
|
212
|
+
/*否则显示具体的数据*/
|
213
|
+
renderList(state.data, () => {
|
214
|
+
return (
|
215
|
+
<div key="no_match">
|
216
|
+
<svg width="40" height="40" viewBox="0 0 20 20" fill="none" fillRule="evenodd" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round">
|
217
|
+
<path d="M15.5 4.8c2 3 1.7 7-1 9.7h0l4.3 4.3-4.3-4.3a7.8 7.8 0 01-9.8 1m-2.2-2.2A7.8 7.8 0 0113.2 2.4M2 18L18 2"></path>
|
218
|
+
</svg>
|
219
|
+
<div>
|
220
|
+
{i18n.$it('search.noMatch', { val: state.searchText }).d(`无法找到搜索结果 "${state.searchText}"`)}
|
221
|
+
</div>
|
222
|
+
</div>
|
223
|
+
);
|
224
|
+
})
|
225
|
+
)}
|
226
|
+
</div>
|
227
|
+
<div className="search-service-foot" style={inputBackgroundColor.value}>
|
228
|
+
{props.config.footer ? props.config.footer() : (<SearchFooter/>)}
|
229
|
+
</div>
|
230
|
+
</Box>
|
231
|
+
);
|
232
|
+
},
|
233
|
+
});
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import {iSearchServiceConfig, iSearchServiceCustomConfig, iSearchServiceDefaultConfig} from "./search.utils";
|
2
|
+
import $dialog from "../$dialog";
|
3
|
+
import {getComponentCls} from "plain-design-composition";
|
4
|
+
import './search-service.scss';
|
5
|
+
import {i18n} from "../i18n";
|
6
|
+
|
7
|
+
import {SearchServicePanel} from "./SearchServicePanel";
|
8
|
+
|
9
|
+
export function createSearchService(defaultConfig?: Partial<iSearchServiceDefaultConfig>) {
|
10
|
+
|
11
|
+
const _defaultConfig: iSearchServiceDefaultConfig = {
|
12
|
+
width: defaultConfig?.width || 560,
|
13
|
+
footer: defaultConfig?.footer,
|
14
|
+
render: defaultConfig?.render,
|
15
|
+
placeholder: defaultConfig?.placeholder || i18n.$it('table.pleaseEnterSearchKey').d('请输入搜索关键词')
|
16
|
+
};
|
17
|
+
|
18
|
+
return (customConfig: iSearchServiceCustomConfig & Partial<iSearchServiceDefaultConfig>) => {
|
19
|
+
|
20
|
+
const config: iSearchServiceConfig = {
|
21
|
+
..._defaultConfig,
|
22
|
+
...customConfig,
|
23
|
+
};
|
24
|
+
|
25
|
+
const closeDialog = $dialog({
|
26
|
+
width: config.width,
|
27
|
+
noHead: true,
|
28
|
+
noFoot: true,
|
29
|
+
externalClass: getComponentCls('search-service'),
|
30
|
+
noContentPadding: true,
|
31
|
+
render: () => {
|
32
|
+
return (
|
33
|
+
<SearchServicePanel config={config} onClose={closeDialog}/>
|
34
|
+
);
|
35
|
+
},
|
36
|
+
});
|
37
|
+
|
38
|
+
return {
|
39
|
+
close: closeDialog,
|
40
|
+
};
|
41
|
+
};
|
42
|
+
}
|
43
|
+
|