add language change

This commit is contained in:
yuanshan 2025-10-14 15:47:40 +08:00
parent f6b2956ac3
commit 7466bcdcf7
12 changed files with 798 additions and 46 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -19,17 +19,46 @@
/>
</NConfigProvider>
</div>
<div class="header-right">
<div class="lang-switch-container">
<div class="lang-switch" @click.stop="toggleLanguagePicker">
<span class="lang-label">{{ currentLanguageLabel }}</span>
<svg
:class="{ rotated: showLanguagePicker }"
xmlns="http://www.w3.org/2000/svg"
width="7"
height="4"
viewBox="0 0 7 4"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.5 4L7 0L0 0L3.5 4Z"
fill="black"
/>
</svg>
</div>
<LanguagePicker
v-if="showLanguagePicker"
v-model="selectedLanguage"
:options="languageOptions"
@select="handleSelectLanguage"
/>
</div>
</div>
</div>
</NLayoutHeader>
</template>
<script setup>
import FiEELogo from "@/assets/image/header/logo.png";
import { ref, onMounted, onUnmounted } from "vue";
import { ref, onMounted, onUnmounted, computed } from "vue";
import { NMenu, NLayoutHeader, NImage, NConfigProvider } from "naive-ui";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { useHeaderMenuConfig } from "@/config/headerMenuConfig";
import LanguagePicker from "@/components/languagePicker/size1440/index.vue";
const themeOverrides = {
Menu: {
@ -51,7 +80,7 @@ const themeOverrides = {
},
};
const { t } = useI18n();
const { t, locale } = useI18n();
const router = useRouter();
// 使
@ -60,6 +89,42 @@ const selectedKey = ref(null);
const isScrolled = ref(false);
// language picker
const showLanguagePicker = ref(false);
const selectedLanguage = ref(
localStorage.getItem("language") || locale.value || "en"
);
const languageOptions = computed(() => [
{ label: t("language.ja"), value: "ja", key: "ja" },
{ label: t("language.en"), value: "en", key: "en" },
{ label: t("language.zh"), value: "zh", key: "zh" },
{ label: t("language.zhTW"), value: "zh-TW", key: "zh-TW" },
]);
const currentLanguageLabel = computed(() => {
const found = languageOptions.value.find(
(opt) => opt.value === (locale.value || "en")
);
return found ? found.label : "English";
});
const toggleLanguagePicker = () => {
showLanguagePicker.value = !showLanguagePicker.value;
};
const closeLanguagePicker = () => {
showLanguagePicker.value = false;
};
const handleSelectLanguage = (lang) => {
locale.value = lang;
localStorage.setItem("language", lang);
closeLanguagePicker();
selectedLanguage.value = locale.value;
};
//
function findMenuOptionByKey(options, key) {
for (const option of options) {
@ -88,10 +153,12 @@ const handleScroll = () => {
onMounted(() => {
window.addEventListener("scroll", handleScroll);
window.addEventListener("click", closeLanguagePicker);
});
onUnmounted(() => {
window.removeEventListener("scroll", handleScroll);
window.removeEventListener("click", closeLanguagePicker);
});
//
@ -247,6 +314,24 @@ const handleToHome = () => {
transform: translateY(0) scale(1);
}
}
.lang-switch-container {
position: relative;
}
.lang-switch {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
svg {
transition: transform 0.2s;
&.rotated {
transform: rotate(180deg);
}
}
}
</style>
<style>
.header-menu .n-menu .n-menu-item-content .n-menu-item-content-header {

View File

@ -19,17 +19,46 @@
/>
</NConfigProvider>
</div>
<div class="header-right">
<div class="lang-switch-container">
<div class="lang-switch" @click.stop="toggleLanguagePicker">
<span class="lang-label">{{ currentLanguageLabel }}</span>
<svg
:class="{ rotated: showLanguagePicker }"
xmlns="http://www.w3.org/2000/svg"
width="7"
height="4"
viewBox="0 0 7 4"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.5 4L7 0L0 0L3.5 4Z"
fill="black"
/>
</svg>
</div>
<LanguagePicker
v-if="showLanguagePicker"
v-model="selectedLanguage"
:options="languageOptions"
@select="handleSelectLanguage"
/>
</div>
</div>
</div>
</NLayoutHeader>
</template>
<script setup>
import FiEELogo from "@/assets/image/header/logo.png";
import { ref, onMounted, onUnmounted } from "vue";
import { ref, onMounted, onUnmounted, computed } from "vue";
import { NMenu, NLayoutHeader, NImage, NConfigProvider } from "naive-ui";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { useHeaderMenuConfig } from "@/config/headerMenuConfig";
import LanguagePicker from "@/components/languagePicker/size1920/index.vue";
const themeOverrides = {
Menu: {
@ -50,7 +79,7 @@ const themeOverrides = {
optionColorHoverInverted: "#fddfea", //
},
};
const { t } = useI18n();
const { t, locale } = useI18n();
const router = useRouter();
// 使
@ -59,6 +88,40 @@ const selectedKey = ref(null);
const isScrolled = ref(false);
const showLanguagePicker = ref(false);
const selectedLanguage = ref(
localStorage.getItem("language") || locale.value || "en"
);
const languageOptions = computed(() => [
{ label: t("language.ja"), value: "ja", key: "ja" },
{ label: t("language.en"), value: "en", key: "en" },
{ label: t("language.zh"), value: "zh", key: "zh" },
{ label: t("language.zhTW"), value: "zh-TW", key: "zh-TW" },
]);
const currentLanguageLabel = computed(() => {
const found = languageOptions.value.find(
(opt) => opt.value === (locale.value || "en")
);
return found ? found.label : "English";
});
const toggleLanguagePicker = () => {
showLanguagePicker.value = !showLanguagePicker.value;
};
const closeLanguagePicker = () => {
showLanguagePicker.value = false;
};
const handleSelectLanguage = (lang) => {
locale.value = lang;
localStorage.setItem("language", lang);
closeLanguagePicker();
selectedLanguage.value = locale.value;
};
//
function findMenuOptionByKey(options, key) {
for (const option of options) {
@ -87,10 +150,12 @@ const handleScroll = () => {
onMounted(() => {
window.addEventListener("scroll", handleScroll);
window.addEventListener("click", closeLanguagePicker);
});
onUnmounted(() => {
window.removeEventListener("scroll", handleScroll);
window.removeEventListener("click", closeLanguagePicker);
});
//
@ -240,4 +305,27 @@ const handleToHome = () => {
transform: translateY(0) scale(1);
}
}
.header-right {
margin-left: auto;
padding-left: 20px;
}
.lang-switch-container {
position: relative;
}
.lang-switch {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
svg {
transition: transform 0.2s;
&.rotated {
transform: rotate(180deg);
}
}
}
</style>

View File

@ -12,23 +12,42 @@
preview-disabled
/>
</div>
<div
class="menu-btn"
:class="{ 'menu-open': showMenu }"
@click="toggleMenu"
>
<img
v-if="showMenu"
src="@/assets/image/375/menu-close.png"
alt="menu"
class="menu-icon"
/>
<img
v-else
src="@/assets/image/375/menu-open.png"
alt="menu"
class="menu-icon"
/>
<div class="header-right">
<div class="lang-switch" @click="openLanguagePicker">
<span class="lang-label">{{ currentLanguageLabel }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="7"
height="4"
viewBox="0 0 7 4"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.5 4L7 0L0 0L3.5 4Z"
fill="black"
/>
</svg>
</div>
<div
class="menu-btn"
:class="{ 'menu-open': showMenu }"
@click="toggleMenu"
>
<img
v-if="showMenu"
src="@/assets/image/375/menu-close.png"
alt="menu"
class="menu-icon"
/>
<img
v-else
src="@/assets/image/375/menu-open.png"
alt="menu"
class="menu-icon"
/>
</div>
</div>
</div>
</NLayoutHeader>
@ -47,16 +66,24 @@
</NConfigProvider>
</div>
</transition>
<LanguagePicker
v-if="showLanguagePicker"
v-model="selectedLanguage"
:options="languageOptions"
@close="showLanguagePicker = false"
@confirm="handleConfirmLanguage"
/>
</template>
<script setup>
import FiEELogo from "@/assets/image/header/logo.png";
import { ref, onMounted, onUnmounted } from "vue";
import { ref, onMounted, onUnmounted, computed } from "vue";
import { NMenu, NLayoutHeader, NImage, NIcon, NConfigProvider } from "naive-ui";
import { MenuSharp, CloseSharp } from "@vicons/ionicons5";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { useHeaderMenuConfig } from "@/config/headerMenuConfig";
import LanguagePicker from "@/components/languagePicker/index.vue";
const themeOverrides = {
Menu: {
itemTextColorHover: "#000",
@ -67,13 +94,42 @@ const themeOverrides = {
itemColorActiveHover: "#fff8fb",
},
};
const { t } = useI18n();
const { t, locale } = useI18n();
const router = useRouter();
const isScrolled = ref(false);
const showMenu = ref(false);
const selectedKey = ref(null);
// language picker
const showLanguagePicker = ref(false);
const selectedLanguage = ref(
localStorage.getItem("language") || locale.value || "en"
);
const languageOptions = computed(() => [
{ label: t("language.ja"), value: "ja", key: "ja" },
{ label: t("language.en"), value: "en", key: "en" },
{ label: t("language.zh"), value: "zh", key: "zh" },
{ label: t("language.zhTW"), value: "zh-TW", key: "zh-TW" },
]);
const currentLanguageLabel = computed(() => {
const found = languageOptions.value.find(
(opt) => opt.value === (locale.value || "en")
);
return found ? found.label : "English";
});
const openLanguagePicker = () => {
showLanguagePicker.value = true;
selectedLanguage.value = locale.value;
};
const handleConfirmLanguage = (lang) => {
locale.value = lang;
localStorage.setItem("language", lang);
showLanguagePicker.value = false;
};
const toggleMenu = () => {
showMenu.value = !showMenu.value;
};
@ -150,6 +206,19 @@ const handleToHome = () => {
justify-content: space-between;
}
.lang-switch {
display: flex;
align-items: center;
gap: 11 * 5.12px;
font-weight: 600;
font-size: 14 * 5.12px;
cursor: pointer;
user-select: none;
}
.lang-caret {
font-size: 10 * 5.12px;
}
.logo {
flex-shrink: 0;
margin-right: 12px;
@ -249,4 +318,9 @@ const handleToHome = () => {
opacity: 0;
transform: translateY(-50px);
}
.header-right {
display: flex;
align-items: center;
gap: 24 * 5.12px;
}
</style>

View File

@ -8,23 +8,42 @@
<div class="logo" @click="handleToHome">
<NImage class="logo-image" :src="FiEELogo" preview-disabled />
</div>
<div
class="menu-btn"
:class="{ 'menu-open': showMenu }"
@click="toggleMenu"
>
<img
v-if="showMenu"
src="@/assets/image/768/menu-close.png"
alt="menu"
class="menu-icon"
/>
<img
v-else
src="@/assets/image/768/menu-open.png"
alt="menu"
class="menu-icon"
/>
<div class="header-right">
<div class="lang-switch" @click="openLanguagePicker">
<span class="lang-label">{{ currentLanguageLabel }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="7"
height="4"
viewBox="0 0 7 4"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.5 4L7 0L0 0L3.5 4Z"
fill="black"
/>
</svg>
</div>
<div
class="menu-btn"
:class="{ 'menu-open': showMenu }"
@click="toggleMenu"
>
<img
v-if="showMenu"
src="@/assets/image/768/menu-close.png"
alt="menu"
class="menu-icon"
/>
<img
v-else
src="@/assets/image/768/menu-open.png"
alt="menu"
class="menu-icon"
/>
</div>
</div>
</div>
</NLayoutHeader>
@ -43,16 +62,24 @@
</NConfigProvider>
</div>
</transition>
<LanguagePicker
v-if="showLanguagePicker"
v-model="selectedLanguage"
:options="languageOptions"
@close="showLanguagePicker = false"
@confirm="handleConfirmLanguage"
/>
</template>
<script setup>
import FiEELogo from "@/assets/image/header/logo.png";
import { ref, onMounted, onUnmounted } from "vue";
import { ref, onMounted, onUnmounted, computed } from "vue";
import { NMenu, NLayoutHeader, NImage, NIcon, NConfigProvider } from "naive-ui";
import { MenuSharp, CloseSharp } from "@vicons/ionicons5";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { useHeaderMenuConfig } from "@/config/headerMenuConfig";
import LanguagePicker from "@/components/languagePicker/index.vue";
const themeOverrides = {
Menu: {
itemTextColorHover: "#000",
@ -63,13 +90,42 @@ const themeOverrides = {
itemColorActiveHover: "#fff8fb",
},
};
const { t } = useI18n();
const { t, locale } = useI18n();
const router = useRouter();
const isScrolled = ref(false);
const showMenu = ref(false);
const selectedKey = ref(null);
// language picker
const showLanguagePicker = ref(false);
const selectedLanguage = ref(
localStorage.getItem("language") || locale.value || "en"
);
const languageOptions = computed(() => [
{ label: t("language.ja"), value: "ja", key: "ja" },
{ label: t("language.en"), value: "en", key: "en" },
{ label: t("language.zh"), value: "zh", key: "zh" },
{ label: t("language.zhTW"), value: "zh-TW", key: "zh-TW" },
]);
const currentLanguageLabel = computed(() => {
const found = languageOptions.value.find(
(opt) => opt.value === (locale.value || "en")
);
return found ? found.label : "English";
});
const openLanguagePicker = () => {
showLanguagePicker.value = true;
selectedLanguage.value = locale.value;
};
const handleConfirmLanguage = (lang) => {
locale.value = lang;
localStorage.setItem("language", lang);
showLanguagePicker.value = false;
};
const toggleMenu = () => {
showMenu.value = !showMenu.value;
};
@ -146,6 +202,22 @@ const handleToHome = () => {
justify-content: space-between;
}
.header-right {
display: flex;
align-items: center;
gap: 24 * 2.5px;
}
.lang-switch {
display: flex;
align-items: center;
gap: 11 * 2.5px;
font-weight: 600;
font-size: 14 * 2.5px;
cursor: pointer;
user-select: none;
}
.logo {
flex-shrink: 0;
margin-left: 11 * 2.5px;

View File

@ -0,0 +1,34 @@
<script setup>
import { computed } from "vue";
import { useWindowSize } from "@vueuse/core";
import size375 from "./size375/index.vue";
import size768 from "./size768/index.vue";
import size1440 from "./size1440/index.vue";
import size1920 from "./size1920/index.vue";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
const router = useRouter();
const { width } = useWindowSize();
const { t } = useI18n();
const viewComponent = computed(() => {
const viewWidth = width.value;
if (viewWidth <= 450) {
return size375;
} else if (viewWidth <= 835) {
return size768;
} else if (viewWidth <= 1640) {
return size1440;
} else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920;
}
});
</script>
<template>
<component :is="viewComponent" />
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,59 @@
<template>
<div class="language-popover">
<div
v-for="opt in options"
:key="opt.value"
class="picker-item"
:class="{ active: opt.value === modelValue }"
@click="selectLanguage(opt.value)"
>
{{ opt.label }}
</div>
</div>
</template>
<script setup>
defineProps({
modelValue: { type: String, required: true },
options: { type: Array, required: true },
});
const emit = defineEmits(["update:modelValue", "select"]);
function selectLanguage(lang) {
emit("update:modelValue", lang);
emit("select", lang);
}
</script>
<style scoped lang="scss">
.language-popover {
position: absolute;
top: calc(100% + 10px);
left: 50%;
transform: translateX(-50%);
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 8px;
z-index: 1000;
width: max-content;
min-width: 120px;
}
.picker-item {
padding: 8px 12px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
border-radius: 6px;
text-align: center;
transition: background-color 0.2s;
&:hover {
background: #f5f5f5;
}
&.active {
background: #fddfea;
color: #000;
}
}
</style>

View File

@ -0,0 +1,59 @@
<template>
<div class="language-popover">
<div
v-for="opt in options"
:key="opt.value"
class="picker-item"
:class="{ active: opt.value === modelValue }"
@click="selectLanguage(opt.value)"
>
{{ opt.label }}
</div>
</div>
</template>
<script setup>
defineProps({
modelValue: { type: String, required: true },
options: { type: Array, required: true },
});
const emit = defineEmits(["update:modelValue", "select"]);
function selectLanguage(lang) {
emit("update:modelValue", lang);
emit("select", lang);
}
</script>
<style scoped lang="scss">
.language-popover {
position: absolute;
top: calc(100% + 10px);
left: 50%;
transform: translateX(-50%);
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 8px;
z-index: 1000;
width: max-content;
min-width: 120px;
}
.picker-item {
padding: 8px 12px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
border-radius: 6px;
text-align: center;
transition: background-color 0.2s;
&:hover {
background: #f5f5f5;
}
&.active {
background: #fddfea;
color: #000;
}
}
</style>

View File

@ -0,0 +1,141 @@
<template>
<Teleport to="body">
<div class="picker-mask" @click.self="emit('close')">
<div class="picker-panel">
<div class="picker-title">
{{ t("home.nav.please_select") }}
<svg
@click="emit('close')"
style="cursor: pointer"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.666016 9.74935C0.666016 4.59469 4.84469 0.416016 9.99935 0.416016C15.154 0.416016 19.3327 4.59469 19.3327 9.74935C19.3327 14.904 15.154 19.0827 9.99935 19.0827C4.84469 19.0827 0.666016 14.904 0.666016 9.74935Z"
fill="#CCCCCC"
/>
<path
d="M12.833 5.84961C13.1273 5.55596 13.6042 5.55565 13.8965 5.84863C14.1907 6.14223 14.1893 6.61848 13.8965 6.91211L11.0615 9.74609L13.9043 12.5898C14.1973 12.8848 14.1986 13.3607 13.9043 13.6543C13.6114 13.947 13.1344 13.9471 12.8408 13.6543L9.99707 10.8105L7.1582 13.6504C6.86386 13.9444 6.38729 13.9446 6.09375 13.6504C5.8002 13.3574 5.80045 12.8809 6.09473 12.5859L8.93359 9.74707L6.10254 6.91602C5.80956 6.62236 5.80889 6.1452 6.10254 5.85156C6.39486 5.55817 6.87209 5.55802 7.16699 5.85156L9.99805 8.68262L12.833 5.84961Z"
fill="white"
/>
</svg>
</div>
<div class="language-list">
<div
v-for="opt in options"
:key="opt.value"
class="picker-item"
:class="{ active: opt.value === localValue }"
@click="localValue = opt.value"
>
{{ opt.label }}
</div>
</div>
<div class="picker-actions">
<button class="picker-confirm" @click="confirm">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
</Teleport>
</template>
<script setup>
import { ref, watch, Teleport } from "vue";
import { useI18n } from "vue-i18n";
const props = defineProps({
modelValue: { type: String, required: true },
options: { type: Array, required: true },
});
const emit = defineEmits(["update:modelValue", "close", "confirm"]);
const { t } = useI18n();
const localValue = ref(props.modelValue);
watch(
() => props.modelValue,
(v) => {
localValue.value = v;
}
);
function confirm() {
emit("update:modelValue", localValue.value);
emit("confirm", localValue.value);
}
</script>
<style scoped lang="scss">
.picker-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.picker-panel {
width: 100%;
background: #fff;
box-shadow: 0 3 * 5.12px 14 * 5.12px rgba(0, 0, 0, 0.16);
padding: 16 * 5.12px;
position: fixed;
bottom: 0;
}
.picker-title {
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 5.12px;
color: #455363;
margin-bottom: 16 * 5.12px;
display: flex;
justify-content: space-between;
align-items: center;
}
.language-list {
border-top: 1px solid #ededed;
border-bottom: 1px solid #ededed;
}
.picker-item {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
color: #9da3ad;
display: flex;
justify-content: center;
align-items: center;
height: 44 * 5.12px;
border-top: 1px solid #f2f2f2;
}
.picker-item:first-child {
border-top: none;
}
.picker-item.active {
color: #000;
font-weight: 500;
}
.picker-actions {
margin-top: 16 * 5.12px;
}
.picker-confirm {
width: 100%;
height: 44 * 5.12px;
background: #ff7bac;
color: #fff;
border: none;
border-radius: 8 * 5.12px;
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 5.12px;
}
</style>

View File

@ -0,0 +1,141 @@
<template>
<Teleport to="body">
<div class="picker-mask" @click.self="emit('close')">
<div class="picker-panel">
<div class="picker-title">
{{ t("home.nav.please_select") }}
<svg
@click="emit('close')"
style="cursor: pointer"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.666016 9.74935C0.666016 4.59469 4.84469 0.416016 9.99935 0.416016C15.154 0.416016 19.3327 4.59469 19.3327 9.74935C19.3327 14.904 15.154 19.0827 9.99935 19.0827C4.84469 19.0827 0.666016 14.904 0.666016 9.74935Z"
fill="#CCCCCC"
/>
<path
d="M12.833 5.84961C13.1273 5.55596 13.6042 5.55565 13.8965 5.84863C14.1907 6.14223 14.1893 6.61848 13.8965 6.91211L11.0615 9.74609L13.9043 12.5898C14.1973 12.8848 14.1986 13.3607 13.9043 13.6543C13.6114 13.947 13.1344 13.9471 12.8408 13.6543L9.99707 10.8105L7.1582 13.6504C6.86386 13.9444 6.38729 13.9446 6.09375 13.6504C5.8002 13.3574 5.80045 12.8809 6.09473 12.5859L8.93359 9.74707L6.10254 6.91602C5.80956 6.62236 5.80889 6.1452 6.10254 5.85156C6.39486 5.55817 6.87209 5.55802 7.16699 5.85156L9.99805 8.68262L12.833 5.84961Z"
fill="white"
/>
</svg>
</div>
<div class="language-list">
<div
v-for="opt in options"
:key="opt.value"
class="picker-item"
:class="{ active: opt.value === localValue }"
@click="localValue = opt.value"
>
{{ opt.label }}
</div>
</div>
<div class="picker-actions">
<button class="picker-confirm" @click="confirm">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
</Teleport>
</template>
<script setup>
import { ref, watch, Teleport } from "vue";
import { useI18n } from "vue-i18n";
const props = defineProps({
modelValue: { type: String, required: true },
options: { type: Array, required: true },
});
const emit = defineEmits(["update:modelValue", "close", "confirm"]);
const { t } = useI18n();
const localValue = ref(props.modelValue);
watch(
() => props.modelValue,
(v) => {
localValue.value = v;
}
);
function confirm() {
emit("update:modelValue", localValue.value);
emit("confirm", localValue.value);
}
</script>
<style scoped lang="scss">
.picker-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.picker-panel {
width: 100%;
background: #fff;
box-shadow: 0 3 * 2.5px 14 * 2.5px rgba(0, 0, 0, 0.16);
padding: 16 * 2.5px;
position: fixed;
bottom: 0;
}
.picker-title {
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 2.5px;
color: #455363;
margin-bottom: 16 * 2.5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.language-list {
border-top: 1px solid #ededed;
border-bottom: 1px solid #ededed;
}
.picker-item {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 2.5px;
color: #9da3ad;
display: flex;
justify-content: center;
align-items: center;
height: 44 * 2.5px;
border-top: 1px solid #f2f2f2;
}
.picker-item:first-child {
border-top: none;
}
.picker-item.active {
color: #000;
font-weight: 500;
}
.picker-actions {
margin-top: 16 * 2.5px;
}
.picker-confirm {
width: 100%;
height: 44 * 2.5px;
background: #ff7bac;
color: #fff;
border: none;
border-radius: 8 * 2.5px;
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 2.5px;
}
</style>

View File

@ -26,8 +26,8 @@ function getBrowserLanguage() {
return 'en' // 默认英语
}
// 直接设为英文
const savedLanguage = 'en'
// 读取本地或浏览器语言,默认英文
const savedLanguage = (typeof localStorage !== 'undefined' && localStorage.getItem('language')) || getBrowserLanguage()
const i18n = createI18n({
legacy: false, // 使用 Composition API
locale: savedLanguage,
@ -37,7 +37,6 @@ const i18n = createI18n({
zh,
ja,
'zh-TW': zhTW,
de
}
})

View File

@ -16,7 +16,7 @@ export default {
home: "Home",
company: "Company Overview",
businessintroduction: "Business Introduction",
please_select: "Please Select",
please_select: "Select Language",
confirm_select: "Confirm Selection",
investor: "Investor Guide",
},