Vben
2021-04-07 5b8eb4a49a097a47caf491c44df427522ab58daa
提交 | 用户 | age
dddda5 1 <template>
c774a6 2   <Teleport to="body">
V 3     <transition name="zoom-fade" mode="out-in">
4       <div :class="getClass" @click.stop v-if="visible">
ff2b12 5         <div :class="`${prefixCls}-content`" v-click-outside="handleClose">
V 6           <div :class="`${prefixCls}-input__wrapper`">
7             <a-input
8               :class="`${prefixCls}-input`"
9               :placeholder="t('common.searchText')"
10               allow-clear
11               @change="handleSearch"
12             >
13               <template #prefix>
970d40 14                 <!-- <Icon icon="ion:search"/> -->
ff2b12 15                 <SearchOutlined />
V 16               </template>
17             </a-input>
3ba828 18             <span :class="`${prefixCls}-cancel`" @click="handleClose">
V 19               {{ t('common.cancelText') }}
20             </span>
c774a6 21           </div>
ff2b12 22
V 23           <div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
24             {{ t('component.app.searchNotData') }}
25           </div>
970d40 26
ff2b12 27           <ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
V 28             <li
29               :ref="setRefs(index)"
30               v-for="(item, index) in searchResult"
31               :key="item.path"
32               :data-index="index"
33               @mouseenter="handleMouseenter"
34               @click="handleEnter"
35               :class="[
36                 `${prefixCls}-list__item`,
37                 {
38                   [`${prefixCls}-list__item--active`]: activeIndex === index,
39                 },
40               ]"
41             >
42               <div :class="`${prefixCls}-list__item-icon`">
3ba828 43                 <Icon :icon="item.icon || 'mdi:form-select'" :size="20" />
ff2b12 44               </div>
9edc28 45               <div :class="`${prefixCls}-list__item-text`">
V 46                 {{ item.name }}
47               </div>
ff2b12 48               <div :class="`${prefixCls}-list__item-enter`">
3ba828 49                 <Icon icon="ant-design:enter-outlined" :size="20" />
ff2b12 50               </div>
V 51             </li>
52           </ul>
53           <AppSearchFooter />
54         </div>
dddda5 55       </div>
c774a6 56     </transition>
V 57   </Teleport>
dddda5 58 </template>
V 59 <script lang="ts">
60   import { defineComponent, computed, unref, ref } from 'vue';
970d40 61
236575 62   import { SearchOutlined } from '@ant-design/icons-vue';
V 63   import { Input } from 'ant-design-vue';
64   import AppSearchFooter from './AppSearchFooter.vue';
65   import Icon from '/@/components/Icon';
66
67   import clickOutside from '/@/directives/clickOutside';
dddda5 68
V 69   import { useDesign } from '/@/hooks/web/useDesign';
70   import { useRefs } from '/@/hooks/core/useRefs';
71   import { useMenuSearch } from './useMenuSearch';
72   import { useI18n } from '/@/hooks/web/useI18n';
c774a6 73   import { useAppInject } from '/@/hooks/web/useAppInject';
e6db0d 74
970d40 75   import { propTypes } from '/@/utils/propTypes';
V 76
dddda5 77   export default defineComponent({
V 78     name: 'AppSearchModal',
3ba828 79     components: { Icon, SearchOutlined, AppSearchFooter, [Input.name]: Input },
9edc28 80     directives: {
V 81       clickOutside,
82     },
c774a6 83     props: {
970d40 84       visible: propTypes.bool,
c774a6 85     },
9edc28 86     emits: ['close'],
dddda5 87     setup(_, { emit }) {
V 88       const scrollWrap = ref<ElRef>(null);
89       const { prefixCls } = useDesign('app-search-modal');
90       const { t } = useI18n();
91       const [refs, setRefs] = useRefs();
c774a6 92       const { getIsMobile } = useAppInject();
dddda5 93
V 94       const {
95         handleSearch,
96         searchResult,
97         keyword,
98         activeIndex,
99         handleEnter,
100         handleMouseenter,
101       } = useMenuSearch(refs, scrollWrap, emit);
102
103       const getIsNotData = computed(() => {
104         return !keyword || unref(searchResult).length === 0;
105       });
106
c774a6 107       const getClass = computed(() => {
V 108         return [
109           prefixCls,
110           {
111             [`${prefixCls}--mobile`]: unref(getIsMobile),
112           },
113         ];
114       });
115
236575 116       function handleClose() {
V 117         searchResult.value = [];
118         emit('close');
119       }
120
dddda5 121       return {
V 122         t,
123         prefixCls,
c774a6 124         getClass,
dddda5 125         handleSearch,
V 126         searchResult,
127         activeIndex,
128         getIsNotData,
129         handleEnter,
130         setRefs,
131         scrollWrap,
132         handleMouseenter,
236575 133         handleClose,
dddda5 134       };
V 135     },
136   });
137 </script>
138 <style lang="less" scoped>
139   @prefix-cls: ~'@{namespace}-app-search-modal';
c774a6 140   @footer-prefix-cls: ~'@{namespace}-app-search-footer';
dddda5 141   .@{prefix-cls} {
V 142     position: fixed;
143     top: 0;
144     left: 0;
c774a6 145     z-index: 800;
dddda5 146     display: flex;
V 147     width: 100%;
148     height: 100%;
149     padding-top: 50px;
efbde0 150     background: rgba(0, 0, 0, 0.25);
dddda5 151     justify-content: center;
c774a6 152
V 153     &--mobile {
154       padding: 0;
155
156       > div {
157         width: 100%;
158       }
159
160       .@{prefix-cls}-input {
161         width: calc(100% - 38px);
162       }
163
164       .@{prefix-cls}-cancel {
165         display: inline-block;
166       }
167
168       .@{prefix-cls}-content {
169         width: 100%;
170         height: 100%;
171         border-radius: 0;
172       }
173
174       .@{footer-prefix-cls} {
175         display: none;
176       }
177
178       .@{prefix-cls}-list {
179         height: calc(100% - 80px);
180         max-height: unset;
181
182         &__item {
183           &-enter {
184             opacity: 0 !important;
185           }
186         }
187       }
188     }
dddda5 189
V 190     &-content {
191       position: relative;
efbde0 192       width: 632px;
dddda5 193       margin: 0 auto auto auto;
5b8eb4 194       background: @component-background;
efbde0 195       border-radius: 16px;
V 196       box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
dddda5 197       flex-direction: column;
V 198     }
199
c774a6 200     &-input__wrapper {
V 201       display: flex;
202       padding: 14px 14px 0 14px;
203       justify-content: space-between;
204       align-items: center;
205     }
206
dddda5 207     &-input {
c774a6 208       width: 100%;
efbde0 209       height: 48px;
dddda5 210       font-size: 1.5em;
V 211       color: #1c1e21;
efbde0 212       border-radius: 6px;
dddda5 213
V 214       span[role='img'] {
215         color: #999;
216       }
217     }
218
c774a6 219     &-cancel {
V 220       display: none;
221       font-size: 1em;
222       color: #666;
223     }
224
dddda5 225     &-not-data {
V 226       display: flex;
227       width: 100%;
228       height: 100px;
229       font-size: 0.9;
230       color: rgb(150 159 175);
231       align-items: center;
232       justify-content: center;
233     }
234
235     &-list {
236       max-height: 472px;
237       padding: 0 14px;
238       padding-bottom: 20px;
239       margin: 0 auto;
240       margin-top: 14px;
241       overflow: auto;
242
243       &__item {
244         position: relative;
245         display: flex;
246         width: 100%;
247         height: 56px;
248         padding-bottom: 4px;
249         padding-left: 14px;
250         margin-top: 8px;
251         font-size: 14px;
252         color: @text-color-base;
253         cursor: pointer;
5b8eb4 254         background: @component-background;
dddda5 255         border-radius: 4px;
V 256         box-shadow: 0 1px 3px 0 #d4d9e1;
257         align-items: center;
258
e49072 259         > div:first-child,
V 260         > div:last-child {
261           display: flex;
262           align-items: center;
263         }
264
dddda5 265         &--active {
V 266           color: #fff;
267           background: @primary-color;
268
269           .@{prefix-cls}-list__item-enter {
270             opacity: 1;
271           }
272         }
273
274         &-icon {
275           width: 30px;
276         }
277
278         &-text {
279           flex: 1;
280         }
281
282         &-enter {
283           width: 30px;
284           opacity: 0;
285         }
286       }
287     }
288   }
289 </style>