| <!-- |
| ~ Licensed to the Apache Software Foundation (ASF) under one or more |
| ~ contributor license agreements. See the NOTICE file distributed with |
| ~ this work for additional information regarding copyright ownership. |
| ~ The ASF licenses this file to You under the Apache License, Version 2.0 |
| ~ (the "License"); you may not use this file except in compliance with |
| ~ the License. You may obtain a copy of the License at |
| ~ |
| ~ http://www.apache.org/licenses/LICENSE-2.0 |
| ~ |
| ~ Unless required by applicable law or agreed to in writing, software |
| ~ distributed under the License is distributed on an "AS IS" BASIS, |
| ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ~ See the License for the specific language governing permissions and |
| ~ limitations under the License. |
| --> |
| <template> |
| <div class="__container_layout_header"> |
| <a-layout-header class="header"> |
| <a-row> |
| <a-col :span="2"> |
| <menu-unfold-outlined |
| v-if="collapsed" |
| class="trigger" |
| @click="() => (collapsed = !collapsed)" |
| /> |
| <menu-fold-outlined v-else class="trigger" @click="() => (collapsed = !collapsed)" /> |
| </a-col> |
| <a-col :span="3"></a-col> |
| <a-col :span="10" class="search-group"> |
| <a-input-group compact> |
| <a-select v-model:value="searchType" class="select-type"> |
| <a-select-option v-for="option in searchTypeOptions" :value="option.value">{{ |
| option.label |
| }}</a-select-option> |
| </a-select> |
| <a-auto-complete |
| v-model:value="keywords" |
| class="input-keywords" |
| :placeholder="$t('globalSearchTip')" |
| :options="candidates" |
| @select="onSelect" |
| @search="inputChange" |
| /> |
| <a-button :icon="h(SearchOutlined)" class="search-icon" @click="inputChange"></a-button> |
| </a-input-group> |
| </a-col> |
| <a-col :span="3"></a-col> |
| <a-col :span="2"> |
| <a-segmented v-model:value="locale" :options="i18nConfig.opts" /> |
| </a-col> |
| <a-col :span="2"> |
| <color-picker |
| :pureColor="PRIMARY_COLOR" |
| @pureColorChange="changeTheme" |
| format="hex6" |
| shape="circle" |
| useType="pure" |
| ></color-picker> |
| <a-popover> |
| <template #content>reset the theme</template> |
| <Icon |
| class="reset-icon" |
| icon="material-symbols:reset-tv-outline" |
| @click="resetTheme" |
| ></Icon> |
| </a-popover> |
| </a-col> |
| <a-col :span="2"> |
| <a-avatar @click="devTool.todo('avatar and user info')"> |
| <template #icon> |
| <UserOutlined /> |
| </template> |
| </a-avatar> |
| <span class="username">张三</span> |
| </a-col> |
| </a-row> |
| </a-layout-header> |
| </div> |
| </template> |
| |
| <script setup lang="ts"> |
| import { MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined } from '@ant-design/icons-vue' |
| import type { ComponentInternalInstance } from 'vue' |
| import { inject, ref, reactive, watch, h, getCurrentInstance, computed } from 'vue' |
| import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject' |
| import { changeLanguage, localeConfig } from '@/base/i18n' |
| import { |
| LOCAL_STORAGE_LOCALE, |
| LOCAL_STORAGE_THEME, |
| PRIMARY_COLOR, |
| PRIMARY_COLOR_DEFAULT |
| } from '@/base/constants' |
| import devTool from '@/utils/DevToolUtil' |
| import { Icon } from '@iconify/vue' |
| import { SearchOutlined } from '@ant-design/icons-vue' |
| import { globalSearch } from '@/api/service/globalSearch' |
| import { debounce } from 'lodash' |
| import type { SelectOption } from '@/types/common.ts' |
| |
| const { |
| appContext: { |
| config: { globalProperties } |
| } |
| } = <ComponentInternalInstance>getCurrentInstance() |
| |
| let __null = PRIMARY_COLOR |
| const collapsed = inject(PROVIDE_INJECT_KEY.COLLAPSED) |
| const i18nConfig = <typeof localeConfig>inject(PROVIDE_INJECT_KEY.LOCALE) |
| let locale = ref(localeConfig.locale) |
| |
| function changeTheme(val: string) { |
| localStorage.setItem(LOCAL_STORAGE_THEME, val) |
| PRIMARY_COLOR.value = val |
| } |
| |
| function resetTheme(val: string) { |
| localStorage.removeItem(LOCAL_STORAGE_THEME) |
| PRIMARY_COLOR.value = PRIMARY_COLOR_DEFAULT |
| } |
| |
| watch(locale, (value) => { |
| changeLanguage(value) |
| }) |
| |
| const searchTypeOptions = reactive([ |
| { |
| label: 'IP', |
| value: 'ip' |
| }, |
| { |
| label: computed(() => globalProperties.$t('application')), |
| value: 'appName' |
| }, |
| { |
| label: computed(() => globalProperties.$t('instance')), |
| value: 'instanceName' |
| }, |
| { |
| label: computed(() => globalProperties.$t('service')), |
| value: 'serviceName' |
| } |
| ]) |
| const searchType = ref(searchTypeOptions[0].value) |
| |
| const keywords = ref('') |
| |
| const onSearch = async () => { |
| let { data } = await globalSearch({ |
| searchType: searchType.value, |
| keywords: keywords.value |
| }) |
| if (data.find) { |
| for (let i = 0; i < data.candidates.length; i++) { |
| candidates.value[i] = { |
| label: data.candidates[i], |
| value: data.candidates[i] |
| } |
| } |
| } else { |
| candidates.value = [] |
| } |
| } |
| |
| const candidates = ref<Array<SelectOption>>([]) |
| |
| const inputChange = debounce(onSearch, 300) |
| |
| const onSelect = () => {} |
| </script> |
| <style lang="less" scoped> |
| .__container_layout_header { |
| .header { |
| background: v-bind('PRIMARY_COLOR'); |
| padding: 0; |
| |
| .search-group { |
| display: flex; |
| align-items: center; |
| |
| .select-type { |
| width: 120px; |
| } |
| |
| .input-keywords { |
| width: calc(100% - 152px); |
| } |
| |
| .search-icon { |
| width: 32px; |
| } |
| } |
| } |
| |
| .trigger { |
| font-size: 20px; |
| margin-left: 20px; |
| color: white; |
| } |
| |
| .username { |
| color: white; |
| padding: 5px; |
| } |
| |
| .reset-icon { |
| font-size: 25px; |
| color: white; |
| margin-bottom: -9px; |
| } |
| } |
| </style> |