| | |
| | | <div |
| | | class="absolute w-full flex-cb top-4.5 z-10 flex-c !justify-end max-[1180px]:!justify-between" |
| | | > |
| | | <div class="flex-cc !hidden max-[1180px]:!flex ml-2 max-sm:ml-6"> |
| | | <ArtLogo class="icon" size="46" /> |
| | | <h1 class="text-xl ont-mediumf ml-2">{{ AppConfig.systemInfo.name }}</h1> |
| | | <div class="brand flex-cc !hidden max-[1180px]:!flex ml-2 max-sm:ml-6"> |
| | | <ArtLogo class="icon" fill /> |
| | | </div> |
| | | |
| | | <div class="flex-cc gap-1.5 mr-2 max-sm:mr-5"> |
| | |
| | | /> |
| | | </div> |
| | | </div> |
| | | <ElDropdown |
| | | <div |
| | | v-if="shouldShowLanguage" |
| | | @command="changeLanguage" |
| | | popper-class="langDropDownStyle" |
| | | class="language-picker relative flex-c" |
| | | @mouseenter="showLanguageMenu = true" |
| | | @mouseleave="showLanguageMenu = false" |
| | | > |
| | | <div class="btn language-btn h-8 w-8 c-p flex-cc tad-300"> |
| | | <div class="btn language-btn h-8 w-8 c-p flex-cc tad-300" @click="toggleLanguageMenu"> |
| | | <ArtSvgIcon |
| | | icon="ri:translate-2" |
| | | class="text-[19px] text-g-800 transition-colors duration-300" |
| | | /> |
| | | </div> |
| | | <template #dropdown> |
| | | <ElDropdownMenu> |
| | | <div v-for="lang in languageOptions" :key="lang.value" class="lang-btn-item"> |
| | | <ElDropdownItem |
| | | :command="lang.value" |
| | | :class="{ 'is-selected': locale === lang.value }" |
| | | > |
| | | <span class="menu-txt">{{ lang.label }}</span> |
| | | <ArtSvgIcon icon="ri:check-fill" class="text-base" v-if="locale === lang.value" /> |
| | | </ElDropdownItem> |
| | | </div> |
| | | </ElDropdownMenu> |
| | | </template> |
| | | </ElDropdown> |
| | | <div class="language-menu absolute right-0 top-10" :class="{ 'is-open': showLanguageMenu }"> |
| | | <div |
| | | v-for="lang in languageOptions" |
| | | :key="lang.value" |
| | | class="language-menu-item flex-cb c-p" |
| | | :class="{ 'is-selected': locale === lang.value }" |
| | | @click="changeLanguage(lang.value)" |
| | | > |
| | | <span class="menu-txt">{{ lang.label }}</span> |
| | | <ArtSvgIcon icon="ri:check-fill" class="text-base" v-if="locale === lang.value" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div |
| | | v-if="shouldShowThemeToggle" |
| | | class="btn theme-btn h-8 w-8 c-p flex-cc tad-300" |
| | | @click="themeAnimation" |
| | | @click="handleThemeAnimation" |
| | | > |
| | | <ArtSvgIcon |
| | | :icon="isDark ? 'ri:sun-fill' : 'ri:moon-line'" |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import AppConfig from '@/config' |
| | | import { useI18n } from 'vue-i18n' |
| | | import { languageOptions } from '@/locales/language-options' |
| | | import { useSettingStore } from '@/store/modules/setting' |
| | | import { useUserStore } from '@/store/modules/user' |
| | | import { useHeaderBar } from '@/hooks/core/useHeaderBar' |
| | | import { themeAnimation } from '@/utils/ui/animation' |
| | | import { languageOptions } from '@/locales' |
| | | import AppConfig from '@/config' |
| | | defineOptions({ name: 'AuthTopBar' }) |
| | | const settingStore = useSettingStore() |
| | | const userStore = useUserStore() |
| | | const { isDark, systemThemeColor } = storeToRefs(settingStore) |
| | | const { shouldShowThemeToggle, shouldShowLanguage } = useHeaderBar() |
| | | const { locale } = useI18n() |
| | | const showLanguageMenu = ref(false) |
| | | const mainColors = AppConfig.systemMainColor |
| | | const color = systemThemeColor |
| | | const changeLanguage = (lang) => { |
| | | showLanguageMenu.value = false |
| | | if (locale.value === lang) return |
| | | locale.value = lang |
| | | userStore.setLanguage(lang) |
| | |
| | | settingStore.setElementTheme(color2) |
| | | settingStore.reload() |
| | | } |
| | | const handleThemeAnimation = async (event) => { |
| | | const { themeAnimation } = await import('@/utils/ui/animation') |
| | | themeAnimation(event) |
| | | } |
| | | const toggleLanguageMenu = () => { |
| | | showLanguageMenu.value = !showLanguageMenu.value |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .brand { |
| | | width: 132px; |
| | | height: 34px; |
| | | } |
| | | |
| | | .icon { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .color-dots { |
| | | pointer-events: none; |
| | | backdrop-filter: blur(10px); |
| | |
| | | box-shadow: none; |
| | | } |
| | | |
| | | .language-menu { |
| | | min-width: 130px; |
| | | padding: 6px; |
| | | pointer-events: none; |
| | | background-color: var(--default-box-color); |
| | | border-radius: 12px; |
| | | box-shadow: 0 2px 12px var(--art-gray-300); |
| | | opacity: 0; |
| | | transform: translateY(8px); |
| | | transition: |
| | | opacity 0.2s ease, |
| | | transform 0.2s ease; |
| | | } |
| | | |
| | | .language-menu.is-open { |
| | | pointer-events: auto; |
| | | opacity: 1; |
| | | transform: translateY(0); |
| | | } |
| | | |
| | | .language-menu-item { |
| | | height: 34px; |
| | | padding: 0 12px; |
| | | color: var(--art-text-gray-800); |
| | | border-radius: 10px; |
| | | transition: |
| | | background-color 0.2s ease, |
| | | color 0.2s ease; |
| | | } |
| | | |
| | | .language-menu-item:hover, |
| | | .language-menu-item.is-selected { |
| | | background-color: var(--art-gray-100); |
| | | } |
| | | |
| | | .menu-txt { |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .dark .language-menu { |
| | | background-color: var(--art-gray-200); |
| | | box-shadow: none; |
| | | } |
| | | |
| | | .color-picker-expandable:hover .palette-btn :deep(.art-svg-icon) { |
| | | color: v-bind(color); |
| | | } |