Merge branch 'zhangyuanshan-20250226-mobile'

This commit is contained in:
张 元山 2025-02-26 17:19:19 +08:00
commit 1e3fc6215b
43 changed files with 1629 additions and 108 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

View File

@ -15,7 +15,9 @@ export default {
nav: { nav: {
home: 'Startseite', home: 'Startseite',
company: 'Unternehmensprofil', company: 'Unternehmensprofil',
businessintroduction: 'Geschäftsvorstellung' businessintroduction: 'Geschäftsvorstellung',
please_select: 'Bitte auswählen',
confirm_select: 'Bestätigen Sie die Auswahl'
}, },
scroll: { scroll: {
tip: 'Nach unten scrollen' tip: 'Nach unten scrollen'

View File

@ -15,7 +15,9 @@ export default {
nav: { nav: {
home: 'Home', home: 'Home',
company: 'Company Overview', company: 'Company Overview',
businessintroduction: 'Business Introduction' businessintroduction: 'Business Introduction',
please_select: 'Please Select',
confirm_select: 'Confirm Selection'
}, },
scroll: { scroll: {
tip: 'Scroll Down' tip: 'Scroll Down'

View File

@ -15,7 +15,9 @@ export default {
"nav": { "nav": {
"home": "ホーム", "home": "ホーム",
"company": "会社概要", "company": "会社概要",
"businessintroduction": "業務紹介" "businessintroduction": "業務紹介",
"please_select": "選択してください",
"confirm_select": "確認選択"
}, },
"scroll": { "scroll": {
"tip": "下にスクロール" "tip": "下にスクロール"

View File

@ -15,7 +15,9 @@ export default {
"nav": { "nav": {
"home": "首頁", "home": "首頁",
"company": "公司概況", "company": "公司概況",
"businessintroduction": "業務介紹" "businessintroduction": "業務介紹",
"please_select": "請選擇",
"confirm_select": "確認選擇"
}, },
"scroll": { "scroll": {
"tip": "向下滑動" "tip": "向下滑動"

View File

@ -15,7 +15,10 @@ export default {
nav: { nav: {
home: '首页', home: '首页',
company: '公司概况', company: '公司概况',
businessintroduction: '业务介绍' businessintroduction: '业务介绍',
language_change: '语言切换',
please_select: '请选择',
confirm_select: '确认选择'
}, },
scroll: { scroll: {
tip: '向下滑动' tip: '向下滑动'

View File

@ -8,28 +8,69 @@
</div> </div>
<div class="tabs"> <div class="tabs">
<div <div
class="tab-item" class="tab-item ellipsis""
:class="{ active: currentTab === 'home' }" :class="{ active: currentTab === 'home' }"
@click="handleTabClick('home')" @click="handleTabClick('home')"
> >
{{ $t("home.nav.home") }} {{ t("home.nav.home") }}
</div> </div>
<div <div
class="tab-item" class="tab-item ellipsis""
:class="{ active: currentTab === 'companyprofil' }" :class="{ active: currentTab === 'companyprofil' }"
@click="handleTabClick('companyprofil')" @click="handleTabClick('companyprofil')"
> >
{{ $t("home.nav.company") }} {{ t("home.nav.company") }}
</div> </div>
<div <div
class="tab-item" class="tab-item ellipsis""
:class="{ active: currentTab === 'businessintroduction' }" :class="{ active: currentTab === 'businessintroduction' }"
@click="handleTabClick('businessintroduction')" @click="handleTabClick('businessintroduction')"
> >
{{ $t("home.nav.businessintroduction") }} {{ t("home.nav.businessintroduction") }}
</div>
<div class="tab-item ellipsis"" @click="handleLanguageChange">
{{ currentLanguageLabel }}
<img
src="@/assets/image/home/375/icon-language.png"
alt="language"
class="w-[36px] ml-[26px]"
/>
</div> </div>
</div> </div>
</header> </header>
<!-- 语言选择模态框 - 使用纯div实现 -->
<div v-if="showLanguageModal" class="language-modal">
<div class="language-modal-backdrop" @click="closeLanguageModal"></div>
<div class="language-modal-content">
<div class="modal-header font-semibold flex items-center">
{{ t("home.nav.please_select") }}
<img
src="@/assets/image/home/375/icon-close.png"
alt="close"
class="close-btn w-[102px]"
@click="closeLanguageModal"
/>
</div>
<div class="language-modal-body">
<div
v-for="option in languageOptions"
:key="option.value"
class="language-option"
:class="{ active: selectedLanguage === option.value }"
@click="selectedLanguage = option.value"
>
{{ option.label }}
</div>
</div>
<div class="language-modal-footer">
<button class="confirm-btn" @click="confirmLanguageChange">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
<main> <main>
<section class="relative pb-[1020px]"> <section class="relative pb-[1020px]">
<div class="absolute top-[0px] right-[0px]"> <div class="absolute top-[0px] right-[0px]">
@ -541,20 +582,27 @@
<script setup> <script setup>
import { NDivider } from "naive-ui"; import { NDivider } from "naive-ui";
import { onUnmounted, ref, onMounted, reactive, nextTick, computed } from "vue"; import {
onUnmounted,
ref,
onMounted,
reactive,
nextTick,
watch,
computed,
} from "vue";
import gsap from "gsap"; import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger"; import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useHome } from "@/store/home/index.js"; import { useHome } from "@/store/home/index.js";
import { useTransitionComposable } from "@/composables/transition-composable"; import { useTransitionComposable } from "@/composables/transition-composable";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n();
// Define component custom events // Define component custom events
defineEmits(["sendCode"]); defineEmits(["sendCode"]);
// Register ScrollTrigger plugin // Register ScrollTrigger plugin
gsap.registerPlugin(ScrollTrigger); gsap.registerPlugin(ScrollTrigger);
const { t } = useI18n();
const { transitionState } = useTransitionComposable(); const { transitionState } = useTransitionComposable();
const moveRefs = ref( const moveRefs = ref(
@ -564,7 +612,14 @@ const moveRefs = ref(
); );
const { currentTab } = useHome(); const { currentTab } = useHome();
let ctx; let ctx;
import { useLanguage } from "@/utils/changeLanguage.js";
const {
languageOptions,
currentLanguageLabel,
changeLanguage,
initLanguage,
currentLang,
} = useLanguage();
// Import images // Import images
import imageshow3 from "@/assets/image/businessintroduction/375/imageshow-3.png"; import imageshow3 from "@/assets/image/businessintroduction/375/imageshow-3.png";
import imageshow4 from "@/assets/image/businessintroduction/375/imageshow-4.png"; import imageshow4 from "@/assets/image/businessintroduction/375/imageshow-4.png";
@ -594,6 +649,7 @@ let carouselAnimation = null; // Store GSAP animation instance
// Listen for window size changes // Listen for window size changes
onMounted(() => { onMounted(() => {
initLanguage();
ctx = gsap.context(() => { ctx = gsap.context(() => {
moveRefs.value.forEach((moveRef, index) => { moveRefs.value.forEach((moveRef, index) => {
if (moveRef.value) { if (moveRef.value) {
@ -678,6 +734,48 @@ const challengeItems = computed(() => [
desc: t("businessintroduction.industry.challenges.items.3.desc"), desc: t("businessintroduction.industry.challenges.items.3.desc"),
}, },
]); ]);
//
const showLanguageModal = ref(false);
// localStorage
const selectedLanguage = ref(localStorage.getItem("language") || "zh");
//
const handleLanguageChange = () => {
showLanguageModal.value = true;
//
selectedLanguage.value = localStorage.getItem("language") || "zh";
//
document.body.style.overflow = "hidden";
};
const currentLanguage = ref(localStorage.getItem("language") || "zh");
//
watch(
() => localStorage.getItem("language"),
(newLang) => {
if (newLang) {
currentLanguage.value = newLang;
}
},
{ immediate: true }
);
//
const confirmLanguageChange = () => {
changeLanguage(selectedLanguage.value);
//
currentLanguage.value = selectedLanguage.value;
//
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
//
const closeLanguageModal = () => {
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -694,7 +792,7 @@ const challengeItems = computed(() => [
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
padding: 0 138px; padding: 0 102px;
background-color: #fff; background-color: #fff;
} }
.logo { .logo {
@ -706,9 +804,8 @@ const challengeItems = computed(() => [
.tabs { .tabs {
display: flex; display: flex;
gap: 128px; gap: 77px;
margin-right: 32px; margin-left: 67px;
margin-left: 60px;
} }
.tab-item { .tab-item {
@ -716,6 +813,7 @@ const challengeItems = computed(() => [
align-items: center; align-items: center;
font-size: 61px; font-size: 61px;
color: #000000; color: #000000;
max-width: 251px;
cursor: pointer; cursor: pointer;
transition: color 0.3s ease; transition: color 0.3s ease;
padding: 4px 8px; padding: 4px 8px;
@ -728,6 +826,11 @@ const challengeItems = computed(() => [
color: #8b59fa; color: #8b59fa;
} }
} }
.ellipsis {
white-space: nowrap; /* 防止文本换行 */
overflow: hidden; /* 隐藏溢出内容 */
text-overflow: ellipsis; /* 文本溢出时显示省略号 */
}
.title { .title {
font-size: 113px; font-size: 113px;
@ -832,4 +935,98 @@ const challengeItems = computed(() => [
height: 100%; height: 100%;
background-color: rgba(230, 234, 238, 0.5) !important; background-color: rgba(230, 234, 238, 0.5) !important;
} }
/* 语言选择模态框样式 */
.language-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
touch-action: none; /* 防止触摸事件影响背景 */
}
.language-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1001;
}
.language-modal-content {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
max-height: 90vh; /* 最大高度为视口高度的90% */
background-color: #fff;
z-index: 1002;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden; /* 内容溢出隐藏 */
}
.modal-header {
padding: 67px 77px 113px 77px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 82px;
color: #000000;
position: relative;
flex-shrink: 0; /* 不允许头部收缩 */
}
.close-btn {
cursor: pointer;
}
.language-modal-body {
padding: 10px 0;
overflow-y: auto; /* 只有内容区域可滚动 */
-webkit-overflow-scrolling: touch; /* 为iOS设备提供平滑滚动 */
flex: 1; /* 内容区域填充剩余空间 */
}
.language-option {
padding: 30px 0;
font-size: 72px;
color: #333;
cursor: pointer;
transition: background-color 0.3s;
text-align: center;
}
.language-option:hover,
.language-option.active {
background-color: #f5f5f5;
color: #8b59f7;
}
.language-modal-footer {
padding: 113px 77px;
display: flex;
justify-content: center;
flex-shrink: 0; /* 不允许底部收缩 */
}
.confirm-btn {
background-color: #8b59f7;
color: white;
border: none;
border-radius: 20px;
padding: 46px 0;
width: 100%;
font-size: 82px;
cursor: pointer;
}
.confirm-btn:hover {
background-color: #7a48e6;
}
</style> </style>

View File

@ -12,24 +12,67 @@
:class="{ active: currentTab === 'home' }" :class="{ active: currentTab === 'home' }"
@click="handleTabClick('home')" @click="handleTabClick('home')"
> >
{{ $t("home.nav.home") }} {{ t("home.nav.home") }}
</div> </div>
<div <div
class="tab-item" class="tab-item"
:class="{ active: currentTab === 'companyprofil' }" :class="{ active: currentTab === 'companyprofil' }"
@click="handleTabClick('companyprofil')" @click="handleTabClick('companyprofil')"
> >
{{ $t("home.nav.company") }} {{ t("home.nav.company") }}
</div> </div>
<div <div
class="tab-item" class="tab-item"
:class="{ active: currentTab === 'businessintroduction' }" :class="{ active: currentTab === 'businessintroduction' }"
@click="handleTabClick('businessintroduction')" @click="handleTabClick('businessintroduction')"
> >
{{ $t("home.nav.businessintroduction") }} {{ t("home.nav.businessintroduction") }}
</div>
<div class="tab-item" @click="handleLanguageChange">
{{ currentLanguageLabel }}
<img
src="@/assets/image/home/768/icon-language.png"
alt="language"
class="w-[18px] ml-[13px]"
/>
</div> </div>
</div> </div>
</header> </header>
<!-- 语言选择模态框 - 使用纯div实现 -->
<div
v-if="showLanguageModal"
class="language-modal"
@touchmove.prevent.stop
>
<div class="language-modal-backdrop" @click="closeLanguageModal"></div>
<div class="language-modal-content">
<div class="modal-header font-semibold flex items-center">
{{ t("home.nav.please_select") }}
<img
src="@/assets/image/home/375/icon-close.png"
alt="close"
class="close-btn w-[51px]"
@click="closeLanguageModal"
/>
</div>
<div class="language-modal-body" @wheel.stop @touchmove.stop>
<div
v-for="option in languageOptions"
:key="option.value"
class="language-option"
:class="{ active: selectedLanguage === option.value }"
@click="selectedLanguage = option.value"
>
{{ option.label }}
</div>
</div>
<div class="language-modal-footer">
<button class="confirm-btn" @click="confirmLanguageChange">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
<main> <main>
<section className="relative pb-[900px] section-first bg-[#F8F9FF]"> <section className="relative pb-[900px] section-first bg-[#F8F9FF]">
<div class="pt-[718px] relative"> <div class="pt-[718px] relative">
@ -464,13 +507,20 @@
</template> </template>
<script setup> <script setup>
import { useI18n } from "vue-i18n";
import { NDivider } from "naive-ui"; import { NDivider } from "naive-ui";
import { onUnmounted, ref, onMounted, reactive, nextTick } from "vue"; import { onUnmounted, ref, onMounted, reactive, watch, nextTick } from "vue";
import gsap from "gsap"; import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger"; import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useHome } from "@/store/home/index.js"; import { useHome } from "@/store/home/index.js";
import { useTransitionComposable } from "@/composables/transition-composable"; import { useTransitionComposable } from "@/composables/transition-composable";
import { useLanguage } from "@/utils/changeLanguage.js";
const {
languageOptions,
currentLanguageLabel,
changeLanguage,
initLanguage,
currentLang,
} = useLanguage();
// //
defineEmits(["sendCode"]); defineEmits(["sendCode"]);
@ -478,6 +528,7 @@ defineEmits(["sendCode"]);
// ScrollTrigger // ScrollTrigger
gsap.registerPlugin(ScrollTrigger); gsap.registerPlugin(ScrollTrigger);
import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const { transitionState } = useTransitionComposable(); const { transitionState } = useTransitionComposable();
@ -615,6 +666,51 @@ const goToSection = (i) => {
}); });
}); });
}; };
//
const showLanguageModal = ref(false);
// localStorage
const selectedLanguage = ref(localStorage.getItem("language") || "zh");
//
const handleLanguageChange = () => {
showLanguageModal.value = true;
//
selectedLanguage.value = localStorage.getItem("language") || "zh";
//
document.body.style.overflow = "hidden";
};
const currentLanguage = ref(localStorage.getItem("language") || "zh");
//
watch(
() => localStorage.getItem("language"),
(newLang) => {
if (newLang) {
currentLanguage.value = newLang;
}
},
{ immediate: true }
);
//
const confirmLanguageChange = () => {
changeLanguage(selectedLanguage.value);
//
currentLanguage.value = selectedLanguage.value;
//
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
//
const closeLanguageModal = () => {
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -779,4 +875,98 @@ const goToSection = (i) => {
text-overflow: ellipsis; text-overflow: ellipsis;
max-height: calc(1.5em * 2); max-height: calc(1.5em * 2);
} }
/* 语言选择模态框样式 */
.language-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
touch-action: none; /* 防止触摸事件影响背景 */
}
.language-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1001;
}
.language-modal-content {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
max-height: 90vh; /* 最大高度为视口高度的90% */
background-color: #fff;
z-index: 1002;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden; /* 内容溢出隐藏 */
}
.modal-header {
padding: 33px 38px 55px 38px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 40px;
color: #000000;
position: relative;
flex-shrink: 0; /* 不允许头部收缩 */
}
.close-btn {
cursor: pointer;
}
.language-modal-body {
padding: 10px 0;
overflow-y: auto; /* 只有内容区域可滚动 */
-webkit-overflow-scrolling: touch; /* 为iOS设备提供平滑滚动 */
flex: 1; /* 内容区域填充剩余空间 */
overscroll-behavior: contain; /* 阻止滚动穿透 */
}
.language-option {
padding: 30px 0;
font-size: 40px;
color: #333;
cursor: pointer;
transition: background-color 0.3s;
text-align: center;
}
.language-option:hover,
.language-option.active {
background-color: #f5f5f5;
color: #8b59f7;
}
.language-modal-footer {
padding: 40px 38px;
display: flex;
justify-content: center;
flex-shrink: 0; /* 不允许底部收缩 */
}
.confirm-btn {
background-color: #8b59f7;
color: white;
border: none;
border-radius: 10px;
padding: 23px 0;
width: 100%;
font-size: 40px;
cursor: pointer;
}
.confirm-btn:hover {
background-color: #7a48e6;
}
</style> </style>

View File

@ -8,28 +8,68 @@
</div> </div>
<div class="tabs"> <div class="tabs">
<div <div
class="tab-item" class="tab-item ellipsis""
:class="{ active: currentTab === 'home' }" :class="{ active: currentTab === 'home' }"
@click="handleTabClick('home')" @click="handleTabClick('home')"
> >
{{ $t("home.nav.home") }} {{ t("home.nav.home") }}
</div> </div>
<div <div
class="tab-item" class="tab-item ellipsis""
:class="{ active: currentTab === 'companyprofil' }" :class="{ active: currentTab === 'companyprofil' }"
@click="handleTabClick('companyprofil')" @click="handleTabClick('companyprofil')"
> >
{{ $t("home.nav.company") }} {{ t("home.nav.company") }}
</div> </div>
<div <div
class="tab-item" class="tab-item ellipsis""
:class="{ active: currentTab === 'businessintroduction' }" :class="{ active: currentTab === 'businessintroduction' }"
@click="handleTabClick('businessintroduction')" @click="handleTabClick('businessintroduction')"
> >
{{ $t("home.nav.businessintroduction") }} {{ t("home.nav.businessintroduction") }}
</div>
<div class="tab-item ellipsis"" @click="handleLanguageChange">
{{ currentLanguageLabel }}
<img
src="@/assets/image/home/375/icon-language.png"
alt="language"
class="w-[36px] ml-[26px]"
/>
</div> </div>
</div> </div>
</header> </header>
<!-- 语言选择模态框 - 使用纯div实现 -->
<div v-if="showLanguageModal" class="language-modal">
<div class="language-modal-backdrop" @click="closeLanguageModal"></div>
<div class="language-modal-content">
<div class="modal-header font-semibold flex items-center">
{{ t("home.nav.please_select") }}
<img
src="@/assets/image/home/375/icon-close.png"
alt="close"
class="close-btn w-[102px]"
@click="closeLanguageModal"
/>
</div>
<div class="language-modal-body">
<div
v-for="option in languageOptions"
:key="option.value"
class="language-option"
:class="{ active: selectedLanguage === option.value }"
@click="selectedLanguage = option.value"
>
{{ option.label }}
</div>
</div>
<div class="language-modal-footer">
<button class="confirm-btn" @click="confirmLanguageChange">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
<main> <main>
<section style="background: #ffffff" className="relative"> <section style="background: #ffffff" className="relative">
<div class="relative section-first h-[3990px] box-contain pt-[2300px]"> <div class="relative section-first h-[3990px] box-contain pt-[2300px]">
@ -432,12 +472,13 @@
<script setup> <script setup>
import { NDivider } from "naive-ui"; import { NDivider } from "naive-ui";
import { onUnmounted, ref, onMounted, reactive, nextTick } from "vue"; import { onUnmounted, ref, onMounted, reactive, watch, nextTick } from "vue";
import gsap from "gsap"; import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger"; import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useHome } from "@/store/home/index.js"; import { useHome } from "@/store/home/index.js";
import { useTransitionComposable } from "@/composables/transition-composable"; import { useTransitionComposable } from "@/composables/transition-composable";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
// //
defineEmits(["sendCode"]); defineEmits(["sendCode"]);
@ -453,7 +494,16 @@ const moveRefs = ref(
); );
const { currentTab } = useHome(); const { currentTab } = useHome();
let ctx; let ctx;
import { useRouter } from "vue-router";
import { useLanguage } from "@/utils/changeLanguage.js";
const {
languageOptions,
currentLanguageLabel,
changeLanguage,
initLanguage,
currentLang,
} = useLanguage();
const router = useRouter();
// //
import carouselShow1 from "@/assets/image/companyprofil/375/carouselShow-1.png"; import carouselShow1 from "@/assets/image/companyprofil/375/carouselShow-1.png";
import carouselShow2 from "@/assets/image/companyprofil/375/carouselShow-2.png"; import carouselShow2 from "@/assets/image/companyprofil/375/carouselShow-2.png";
@ -527,6 +577,7 @@ const handleResize = () => {
// //
onMounted(() => { onMounted(() => {
initLanguage();
ctx = gsap.context(() => { ctx = gsap.context(() => {
moveRefs.value.forEach((moveRef, index) => { moveRefs.value.forEach((moveRef, index) => {
if (moveRef.value) { if (moveRef.value) {
@ -720,8 +771,7 @@ const handleDragEnd = (e) => {
} }
} }
}; };
import { useRouter } from "vue-router";
const router = useRouter();
const handleCarouselClick = (item, event) => { const handleCarouselClick = (item, event) => {
// //
if (event.type === "click") { if (event.type === "click") {
@ -735,6 +785,48 @@ const handleCarouselClick = (item, event) => {
}); });
} }
}; };
//
const showLanguageModal = ref(false);
// localStorage
const selectedLanguage = ref(localStorage.getItem("language") || "zh");
//
const handleLanguageChange = () => {
showLanguageModal.value = true;
//
selectedLanguage.value = localStorage.getItem("language") || "zh";
//
document.body.style.overflow = "hidden";
};
const currentLanguage = ref(localStorage.getItem("language") || "zh");
//
watch(
() => localStorage.getItem("language"),
(newLang) => {
if (newLang) {
currentLanguage.value = newLang;
}
},
{ immediate: true }
);
//
const confirmLanguageChange = () => {
changeLanguage(selectedLanguage.value);
//
currentLanguage.value = selectedLanguage.value;
//
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
//
const closeLanguageModal = () => {
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -752,7 +844,7 @@ const handleCarouselClick = (item, event) => {
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
padding: 0 138px; padding: 0 102px;
background-color: #fff; background-color: #fff;
} }
.logo { .logo {
@ -764,16 +856,17 @@ const handleCarouselClick = (item, event) => {
.tabs { .tabs {
display: flex; display: flex;
gap: 128px; gap: 77px;
margin-right: 32px; margin-left: 67px;
margin-left: 60px;
} }
.tab-item { .tab-item {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 61px; font-size: 61px;
color: #000000; color: #000000;
max-width: 251px;
cursor: pointer; cursor: pointer;
transition: color 0.3s ease; transition: color 0.3s ease;
padding: 4px 8px; padding: 4px 8px;
@ -786,7 +879,11 @@ const handleCarouselClick = (item, event) => {
color: #8b59fa; color: #8b59fa;
} }
} }
.ellipsis {
white-space: nowrap; /* 防止文本换行 */
overflow: hidden; /* 隐藏溢出内容 */
text-overflow: ellipsis; /* 文本溢出时显示省略号 */
}
.title { .title {
font-size: 113px; font-size: 113px;
font-weight: 600; font-weight: 600;
@ -899,4 +996,98 @@ const handleCarouselClick = (item, event) => {
height: 100%; height: 100%;
background-color: rgba(230, 234, 238, 0.5) !important; background-color: rgba(230, 234, 238, 0.5) !important;
} }
/* 语言选择模态框样式 */
.language-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
touch-action: none; /* 防止触摸事件影响背景 */
}
.language-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1001;
}
.language-modal-content {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
max-height: 90vh; /* 最大高度为视口高度的90% */
background-color: #fff;
z-index: 1002;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden; /* 内容溢出隐藏 */
}
.modal-header {
padding: 67px 77px 113px 77px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 82px;
color: #000000;
position: relative;
flex-shrink: 0; /* 不允许头部收缩 */
}
.close-btn {
cursor: pointer;
}
.language-modal-body {
padding: 10px 0;
overflow-y: auto; /* 只有内容区域可滚动 */
-webkit-overflow-scrolling: touch; /* 为iOS设备提供平滑滚动 */
flex: 1; /* 内容区域填充剩余空间 */
}
.language-option {
padding: 30px 0;
font-size: 72px;
color: #333;
cursor: pointer;
transition: background-color 0.3s;
text-align: center;
}
.language-option:hover,
.language-option.active {
background-color: #f5f5f5;
color: #8b59f7;
}
.language-modal-footer {
padding: 113px 77px;
display: flex;
justify-content: center;
flex-shrink: 0; /* 不允许底部收缩 */
}
.confirm-btn {
background-color: #8b59f7;
color: white;
border: none;
border-radius: 20px;
padding: 46px 0;
width: 100%;
font-size: 82px;
cursor: pointer;
}
.confirm-btn:hover {
background-color: #7a48e6;
}
</style> </style>

View File

@ -12,24 +12,67 @@
:class="{ active: currentTab === 'home' }" :class="{ active: currentTab === 'home' }"
@click="handleTabClick('home')" @click="handleTabClick('home')"
> >
{{ $t("home.nav.home") }} {{ t("home.nav.home") }}
</div> </div>
<div <div
class="tab-item" class="tab-item"
:class="{ active: currentTab === 'companyprofil' }" :class="{ active: currentTab === 'companyprofil' }"
@click="handleTabClick('companyprofil')" @click="handleTabClick('companyprofil')"
> >
{{ $t("home.nav.company") }} {{ t("home.nav.company") }}
</div> </div>
<div <div
class="tab-item" class="tab-item"
:class="{ active: currentTab === 'businessintroduction' }" :class="{ active: currentTab === 'businessintroduction' }"
@click="handleTabClick('businessintroduction')" @click="handleTabClick('businessintroduction')"
> >
{{ $t("home.nav.businessintroduction") }} {{ t("home.nav.businessintroduction") }}
</div>
<div class="tab-item" @click="handleLanguageChange">
{{ currentLanguageLabel }}
<img
src="@/assets/image/home/768/icon-language.png"
alt="language"
class="w-[18px] ml-[13px]"
/>
</div> </div>
</div> </div>
</header> </header>
<!-- 语言选择模态框 - 使用纯div实现 -->
<div
v-if="showLanguageModal"
class="language-modal"
@touchmove.prevent.stop
>
<div class="language-modal-backdrop" @click="closeLanguageModal"></div>
<div class="language-modal-content">
<div class="modal-header font-semibold flex items-center">
{{ t("home.nav.please_select") }}
<img
src="@/assets/image/home/375/icon-close.png"
alt="close"
class="close-btn w-[51px]"
@click="closeLanguageModal"
/>
</div>
<div class="language-modal-body" @wheel.stop @touchmove.stop>
<div
v-for="option in languageOptions"
:key="option.value"
class="language-option"
:class="{ active: selectedLanguage === option.value }"
@click="selectedLanguage = option.value"
>
{{ option.label }}
</div>
</div>
<div class="language-modal-footer">
<button class="confirm-btn" @click="confirmLanguageChange">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
<main> <main>
<section style="background: #ffffff" className="relative"> <section style="background: #ffffff" className="relative">
<div <div
@ -409,15 +452,21 @@
<script setup> <script setup>
import { NDivider } from "naive-ui"; import { NDivider } from "naive-ui";
import { onUnmounted, ref, onMounted, reactive, nextTick } from "vue"; import { onUnmounted, ref, onMounted, reactive, nextTick, watch } from "vue";
import gsap from "gsap"; import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger"; import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useHome } from "@/store/home/index.js"; import { useHome } from "@/store/home/index.js";
import { useTransitionComposable } from "@/composables/transition-composable"; import { useTransitionComposable } from "@/composables/transition-composable";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
// i18n
const { t } = useI18n(); const { t } = useI18n();
import { useLanguage } from "@/utils/changeLanguage.js";
const {
languageOptions,
currentLanguageLabel,
changeLanguage,
initLanguage,
currentLang,
} = useLanguage();
// //
defineEmits(["sendCode"]); defineEmits(["sendCode"]);
@ -727,6 +776,51 @@ const handleCarouselClick = (item, event) => {
}); });
} }
}; };
//
const showLanguageModal = ref(false);
// localStorage
const selectedLanguage = ref(localStorage.getItem("language") || "zh");
//
const handleLanguageChange = () => {
showLanguageModal.value = true;
//
selectedLanguage.value = localStorage.getItem("language") || "zh";
//
document.body.style.overflow = "hidden";
};
const currentLanguage = ref(localStorage.getItem("language") || "zh");
//
watch(
() => localStorage.getItem("language"),
(newLang) => {
if (newLang) {
currentLanguage.value = newLang;
}
},
{ immediate: true }
);
//
const confirmLanguageChange = () => {
changeLanguage(selectedLanguage.value);
//
currentLanguage.value = selectedLanguage.value;
//
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
//
const closeLanguageModal = () => {
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -886,4 +980,98 @@ const handleCarouselClick = (item, event) => {
width: 1px; width: 1px;
height: 100%; height: 100%;
} }
/* 语言选择模态框样式 */
.language-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
touch-action: none; /* 防止触摸事件影响背景 */
}
.language-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1001;
}
.language-modal-content {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
max-height: 90vh; /* 最大高度为视口高度的90% */
background-color: #fff;
z-index: 1002;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden; /* 内容溢出隐藏 */
}
.modal-header {
padding: 33px 38px 55px 38px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 40px;
color: #000000;
position: relative;
flex-shrink: 0; /* 不允许头部收缩 */
}
.close-btn {
cursor: pointer;
}
.language-modal-body {
padding: 10px 0;
overflow-y: auto; /* 只有内容区域可滚动 */
-webkit-overflow-scrolling: touch; /* 为iOS设备提供平滑滚动 */
flex: 1; /* 内容区域填充剩余空间 */
overscroll-behavior: contain; /* 阻止滚动穿透 */
}
.language-option {
padding: 30px 0;
font-size: 40px;
color: #333;
cursor: pointer;
transition: background-color 0.3s;
text-align: center;
}
.language-option:hover,
.language-option.active {
background-color: #f5f5f5;
color: #8b59f7;
}
.language-modal-footer {
padding: 40px 38px;
display: flex;
justify-content: center;
flex-shrink: 0; /* 不允许底部收缩 */
}
.confirm-btn {
background-color: #8b59f7;
color: white;
border: none;
border-radius: 10px;
padding: 23px 0;
width: 100%;
font-size: 40px;
cursor: pointer;
}
.confirm-btn:hover {
background-color: #7a48e6;
}
</style> </style>

View File

@ -8,28 +8,68 @@
</div> </div>
<div class="tabs"> <div class="tabs">
<div <div
class="tab-item" class="tab-item ellipsis""
:class="{ active: currentTab === 'home' }" :class="{ active: currentTab === 'home' }"
@click="handleTabClick('home')" @click="handleTabClick('home')"
> >
{{ $t("home.nav.home") }} {{ t("home.nav.home") }}
</div> </div>
<div <div
class="tab-item" class="tab-item ellipsis""
:class="{ active: currentTab === 'companyprofil' }" :class="{ active: currentTab === 'companyprofil' }"
@click="handleTabClick('companyprofil')" @click="handleTabClick('companyprofil')"
> >
{{ $t("home.nav.company") }} {{ t("home.nav.company") }}
</div> </div>
<div <div
class="tab-item" class="tab-item ellipsis""
:class="{ active: currentTab === 'businessintroduction' }" :class="{ active: currentTab === 'businessintroduction' }"
@click="handleTabClick('businessintroduction')" @click="handleTabClick('businessintroduction')"
> >
{{ $t("home.nav.businessintroduction") }} {{ t("home.nav.businessintroduction") }}
</div>
<div class="tab-item ellipsis"" @click="handleLanguageChange">
{{ currentLanguageLabel }}
<img
src="@/assets/image/home/375/icon-language.png"
alt="language"
class="w-[36px] ml-[26px]"
/>
</div> </div>
</div> </div>
</header> </header>
<!-- 语言选择模态框 - 使用纯div实现 -->
<div v-if="showLanguageModal" class="language-modal">
<div class="language-modal-backdrop" @click="closeLanguageModal"></div>
<div class="language-modal-content">
<div class="modal-header font-semibold flex items-center">
{{ t("home.nav.please_select") }}
<img
src="@/assets/image/home/375/icon-close.png"
alt="close"
class="close-btn w-[102px]"
@click="closeLanguageModal"
/>
</div>
<div class="language-modal-body">
<div
v-for="option in languageOptions"
:key="option.value"
class="language-option"
:class="{ active: selectedLanguage === option.value }"
@click="selectedLanguage = option.value"
>
{{ option.label }}
</div>
</div>
<div class="language-modal-footer">
<button class="confirm-btn" @click="confirmLanguageChange">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
<main> <main>
<section <section
className="section-first" className="section-first"
@ -224,10 +264,20 @@ import {
reactive, reactive,
nextTick, nextTick,
onBeforeMount, onBeforeMount,
watch,
} from "vue"; } from "vue";
import { useHome } from "@/store/home/index.js"; import { useHome } from "@/store/home/index.js";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { useLanguage } from "@/utils/changeLanguage.js";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const {
languageOptions,
currentLanguageLabel,
changeLanguage,
initLanguage,
currentLang,
} = useLanguage();
const state = reactive({ const state = reactive({
pageTitleNo: "", pageTitleNo: "",
}); });
@ -238,13 +288,57 @@ const route = useRoute();
onBeforeMount(() => { onBeforeMount(() => {
state.pageTitleNo = route.query.titleNo; state.pageTitleNo = route.query.titleNo;
}); });
onMounted(() => {}); onMounted(() => {
initLanguage();
});
onUnmounted(() => {}); onUnmounted(() => {});
const handleTabClick = (tab) => { const handleTabClick = (tab) => {
currentTab.value = tab; currentTab.value = tab;
router.push("/" + tab); router.push("/" + tab);
}; };
//
const showLanguageModal = ref(false);
// localStorage
const selectedLanguage = ref(localStorage.getItem("language") || "zh");
//
const handleLanguageChange = () => {
showLanguageModal.value = true;
//
selectedLanguage.value = localStorage.getItem("language") || "zh";
//
document.body.style.overflow = "hidden";
};
const currentLanguage = ref(localStorage.getItem("language") || "zh");
//
watch(
() => localStorage.getItem("language"),
(newLang) => {
if (newLang) {
currentLanguage.value = newLang;
}
},
{ immediate: true }
);
//
const confirmLanguageChange = () => {
changeLanguage(selectedLanguage.value);
//
currentLanguage.value = selectedLanguage.value;
//
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
//
const closeLanguageModal = () => {
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -253,7 +347,6 @@ const handleTabClick = (tab) => {
overflow-x: hidden; overflow-x: hidden;
position: relative; position: relative;
} }
.header { .header {
width: 100%; width: 100%;
height: 260px; height: 260px;
@ -262,7 +355,7 @@ const handleTabClick = (tab) => {
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
padding: 0 138px; padding: 0 102px;
background-color: #fff; background-color: #fff;
} }
.logo { .logo {
@ -274,13 +367,17 @@ const handleTabClick = (tab) => {
.tabs { .tabs {
display: flex; display: flex;
gap: 128px; gap: 77px;
margin-right: 32px; margin-left: 67px;
} }
.tab-item { .tab-item {
display: flex;
align-items: center;
font-size: 61px; font-size: 61px;
color: #000000; color: #000000;
max-width: 251px;
cursor: pointer; cursor: pointer;
transition: color 0.3s ease; transition: color 0.3s ease;
padding: 4px 8px; padding: 4px 8px;
@ -293,6 +390,12 @@ const handleTabClick = (tab) => {
color: #8b59fa; color: #8b59fa;
} }
} }
.ellipsis {
white-space: nowrap; /* 防止文本换行 */
overflow: hidden; /* 隐藏溢出内容 */
text-overflow: ellipsis; /* 文本溢出时显示省略号 */
}
.title { .title {
font-size: 113px; font-size: 113px;
font-weight: 600; font-weight: 600;
@ -323,4 +426,98 @@ const handleTabClick = (tab) => {
height: 100%; height: 100%;
background-color: rgba(230, 234, 238, 0.5) !important; background-color: rgba(230, 234, 238, 0.5) !important;
} }
/* 语言选择模态框样式 */
.language-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
touch-action: none; /* 防止触摸事件影响背景 */
}
.language-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1001;
}
.language-modal-content {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
max-height: 90vh; /* 最大高度为视口高度的90% */
background-color: #fff;
z-index: 1002;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden; /* 内容溢出隐藏 */
}
.modal-header {
padding: 67px 77px 113px 77px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 82px;
color: #000000;
position: relative;
flex-shrink: 0; /* 不允许头部收缩 */
}
.close-btn {
cursor: pointer;
}
.language-modal-body {
padding: 10px 0;
overflow-y: auto; /* 只有内容区域可滚动 */
-webkit-overflow-scrolling: touch; /* 为iOS设备提供平滑滚动 */
flex: 1; /* 内容区域填充剩余空间 */
}
.language-option {
padding: 30px 0;
font-size: 72px;
color: #333;
cursor: pointer;
transition: background-color 0.3s;
text-align: center;
}
.language-option:hover,
.language-option.active {
background-color: #f5f5f5;
color: #8b59f7;
}
.language-modal-footer {
padding: 113px 77px;
display: flex;
justify-content: center;
flex-shrink: 0; /* 不允许底部收缩 */
}
.confirm-btn {
background-color: #8b59f7;
color: white;
border: none;
border-radius: 20px;
padding: 46px 0;
width: 100%;
font-size: 82px;
cursor: pointer;
}
.confirm-btn:hover {
background-color: #7a48e6;
}
</style> </style>

View File

@ -12,24 +12,67 @@
:class="{ active: currentTab === 'home' }" :class="{ active: currentTab === 'home' }"
@click="handleTabClick('home')" @click="handleTabClick('home')"
> >
{{ $t("home.nav.home") }} {{ t("home.nav.home") }}
</div> </div>
<div <div
class="tab-item" class="tab-item"
:class="{ active: currentTab === 'companyprofil' }" :class="{ active: currentTab === 'companyprofil' }"
@click="handleTabClick('companyprofil')" @click="handleTabClick('companyprofil')"
> >
{{ $t("home.nav.company") }} {{ t("home.nav.company") }}
</div> </div>
<div <div
class="tab-item" class="tab-item"
:class="{ active: currentTab === 'businessintroduction' }" :class="{ active: currentTab === 'businessintroduction' }"
@click="handleTabClick('businessintroduction')" @click="handleTabClick('businessintroduction')"
> >
{{ $t("home.nav.businessintroduction") }} {{ t("home.nav.businessintroduction") }}
</div>
<div class="tab-item" @click="handleLanguageChange">
{{ currentLanguageLabel }}
<img
src="@/assets/image/home/768/icon-language.png"
alt="language"
class="w-[18px] ml-[13px]"
/>
</div> </div>
</div> </div>
</header> </header>
<!-- 语言选择模态框 - 使用纯div实现 -->
<div
v-if="showLanguageModal"
class="language-modal"
@touchmove.prevent.stop
>
<div class="language-modal-backdrop" @click="closeLanguageModal"></div>
<div class="language-modal-content">
<div class="modal-header font-semibold flex items-center">
{{ t("home.nav.please_select") }}
<img
src="@/assets/image/home/375/icon-close.png"
alt="close"
class="close-btn w-[51px]"
@click="closeLanguageModal"
/>
</div>
<div class="language-modal-body" @wheel.stop @touchmove.stop>
<div
v-for="option in languageOptions"
:key="option.value"
class="language-option"
:class="{ active: selectedLanguage === option.value }"
@click="selectedLanguage = option.value"
>
{{ option.label }}
</div>
</div>
<div class="language-modal-footer">
<button class="confirm-btn" @click="confirmLanguageChange">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
<main> <main>
<section <section
className="section-first" className="section-first"
@ -223,6 +266,7 @@ import {
onMounted, onMounted,
reactive, reactive,
nextTick, nextTick,
watch,
onBeforeMount, onBeforeMount,
} from "vue"; } from "vue";
import { useHome } from "@/store/home/index.js"; import { useHome } from "@/store/home/index.js";
@ -230,9 +274,20 @@ const state = reactive({
pageTitleNo: "", pageTitleNo: "",
}); });
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const { currentTab } = useHome(); const { currentTab } = useHome();
import { useLanguage } from "@/utils/changeLanguage.js";
const {
languageOptions,
currentLanguageLabel,
changeLanguage,
initLanguage,
currentLang,
} = useLanguage();
onBeforeMount(() => { onBeforeMount(() => {
state.pageTitleNo = route.query.titleNo; state.pageTitleNo = route.query.titleNo;
}); });
@ -242,6 +297,51 @@ const handleTabClick = (tab) => {
currentTab.value = tab; currentTab.value = tab;
router.push("/" + tab); router.push("/" + tab);
}; };
//
const showLanguageModal = ref(false);
// localStorage
const selectedLanguage = ref(localStorage.getItem("language") || "zh");
//
const handleLanguageChange = () => {
showLanguageModal.value = true;
//
selectedLanguage.value = localStorage.getItem("language") || "zh";
//
document.body.style.overflow = "hidden";
};
const currentLanguage = ref(localStorage.getItem("language") || "zh");
//
watch(
() => localStorage.getItem("language"),
(newLang) => {
if (newLang) {
currentLanguage.value = newLang;
}
},
{ immediate: true }
);
//
const confirmLanguageChange = () => {
changeLanguage(selectedLanguage.value);
//
currentLanguage.value = selectedLanguage.value;
//
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
//
const closeLanguageModal = () => {
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -320,4 +420,98 @@ const handleTabClick = (tab) => {
width: 1px; width: 1px;
height: 100%; height: 100%;
} }
/* 语言选择模态框样式 */
.language-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
touch-action: none; /* 防止触摸事件影响背景 */
}
.language-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1001;
}
.language-modal-content {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
max-height: 90vh; /* 最大高度为视口高度的90% */
background-color: #fff;
z-index: 1002;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden; /* 内容溢出隐藏 */
}
.modal-header {
padding: 33px 38px 55px 38px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 40px;
color: #000000;
position: relative;
flex-shrink: 0; /* 不允许头部收缩 */
}
.close-btn {
cursor: pointer;
}
.language-modal-body {
padding: 10px 0;
overflow-y: auto; /* 只有内容区域可滚动 */
-webkit-overflow-scrolling: touch; /* 为iOS设备提供平滑滚动 */
flex: 1; /* 内容区域填充剩余空间 */
overscroll-behavior: contain; /* 阻止滚动穿透 */
}
.language-option {
padding: 30px 0;
font-size: 40px;
color: #333;
cursor: pointer;
transition: background-color 0.3s;
text-align: center;
}
.language-option:hover,
.language-option.active {
background-color: #f5f5f5;
color: #8b59f7;
}
.language-modal-footer {
padding: 40px 38px;
display: flex;
justify-content: center;
flex-shrink: 0; /* 不允许底部收缩 */
}
.confirm-btn {
background-color: #8b59f7;
color: white;
border: none;
border-radius: 10px;
padding: 23px 0;
width: 100%;
font-size: 40px;
cursor: pointer;
}
.confirm-btn:hover {
background-color: #7a48e6;
}
</style> </style>

View File

@ -6,30 +6,71 @@
</div> </div>
<div class="tabs"> <div class="tabs">
<div <div
class="tab-item" class="tab-item ellipsis"
:class="{ active: currentTab === 'home' }" :class="{ active: currentTab === 'home' }"
@click="handleTabClick('home')" @click="handleTabClick('home')"
> >
{{ t("home.nav.home") }} {{ t("home.nav.home") }}
</div> </div>
<div <div
class="tab-item" class="tab-item ellipsis"
:class="{ active: currentTab === 'companyprofil' }" :class="{ active: currentTab === 'companyprofil' }"
@click="handleTabClick('companyprofil')" @click="handleTabClick('companyprofil')"
> >
{{ t("home.nav.company") }} {{ t("home.nav.company") }}
</div> </div>
<div <div
class="tab-item" class="tab-item ellipsis"
:class="{ active: currentTab === 'businessintroduction' }" :class="{ active: currentTab === 'businessintroduction' }"
@click="handleTabClick('businessintroduction')" @click="handleTabClick('businessintroduction')"
> >
{{ t("home.nav.businessintroduction") }} {{ t("home.nav.businessintroduction") }}
</div> </div>
<div class="tab-item ellipsis" @click="handleLanguageChange">
{{ currentLanguageLabel }}
<img
src="@/assets/image/home/375/icon-language.png"
alt="language"
class="w-[36px] ml-[26px]"
/>
</div>
</div> </div>
</header> </header>
<!-- 语言选择模态框 - 使用纯div实现 -->
<div v-if="showLanguageModal" class="language-modal">
<div class="language-modal-backdrop" @click="closeLanguageModal"></div>
<div class="language-modal-content">
<div class="modal-header font-semibold flex items-center">
{{ t("home.nav.please_select") }}
<img
src="@/assets/image/home/375/icon-close.png"
alt="close"
class="close-btn w-[102px]"
@click="closeLanguageModal"
/>
</div>
<div class="language-modal-body">
<div
v-for="option in languageOptions"
:key="option.value"
class="language-option"
:class="{ active: selectedLanguage === option.value }"
@click="selectedLanguage = option.value"
>
{{ option.label }}
</div>
</div>
<div class="language-modal-footer">
<button class="confirm-btn" @click="confirmLanguageChange">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
<main ref="main"> <main ref="main">
<div class="scroll-down" :class="{ hide: !scrollDownVisible }"> <div class="scroll-down ellipsis" :class="{ hide: !scrollDownVisible }">
{{ t("home.scroll.tip") }} {{ t("home.scroll.tip") }}
</div> </div>
<section className="panel first-panel"> <section className="panel first-panel">
@ -106,7 +147,7 @@
{{ t("home.section3.features.data.title") }} {{ t("home.section3.features.data.title") }}
</div> </div>
<div <div
class="text-[#455363] text-[72px] mt-[72px] line-4 max-w-[1024px] break-words" class="text-[#455363] text-[72px] line-3 mt-[72px] max-w-[1024px] break-words"
> >
{{ t("home.section3.features.data.desc") }} {{ t("home.section3.features.data.desc") }}
</div> </div>
@ -124,7 +165,7 @@
{{ t("home.section3.features.ai.title") }} {{ t("home.section3.features.ai.title") }}
</div> </div>
<div <div
class="text-[#455363] text-[72px] mt-[72px] line-4 max-w-[1024px] break-words" class="text-[#455363] text-[72px] line-3 mt-[72px] max-w-[1024px] break-words"
> >
{{ t("home.section3.features.ai.desc") }} {{ t("home.section3.features.ai.desc") }}
</div> </div>
@ -142,7 +183,7 @@
{{ t("home.section3.features.cloud.title") }} {{ t("home.section3.features.cloud.title") }}
</div> </div>
<div <div
class="text-[#455363] text-[72px] mt-[72px] line-4 max-w-[1024px] break-words" class="text-[#455363] text-[72px] line-3 mt-[72px] max-w-[1024px] break-words"
> >
{{ t("home.section3.features.cloud.desc") }} {{ t("home.section3.features.cloud.desc") }}
</div> </div>
@ -160,7 +201,7 @@
{{ t("home.section3.features.cooperation.title") }} {{ t("home.section3.features.cooperation.title") }}
</div> </div>
<div <div
class="text-[#455363] text-[72px] mt-[72px] line-4 max-w-[1024px] break-words" class="text-[#455363] text-[72px] line-3 mt-[72px] max-w-[1024px] break-words"
> >
{{ t("home.section3.features.cooperation.desc") }} {{ t("home.section3.features.cooperation.desc") }}
</div> </div>
@ -178,7 +219,7 @@
{{ t("home.section3.features.promotion.title") }} {{ t("home.section3.features.promotion.title") }}
</div> </div>
<div <div
class="text-[#455363] text-[72px] mt-[72px] line-4 max-w-[1024px] break-words" class="text-[#455363] text-[72px] line-3 mt-[72px] max-w-[1024px] break-words"
> >
{{ t("home.section3.features.promotion.desc") }} {{ t("home.section3.features.promotion.desc") }}
</div> </div>
@ -307,6 +348,14 @@ const scrollThreshold = 50; // 添加滚动阈值
let lastScrollTime = 0; // let lastScrollTime = 0; //
const scrollDownVisible = ref(true); const scrollDownVisible = ref(true);
const { t } = useI18n(); const { t } = useI18n();
import { useLanguage } from "@/utils/changeLanguage.js";
const {
languageOptions,
currentLanguageLabel,
changeLanguage,
initLanguage,
currentLang,
} = useLanguage();
// //
const carouselImages = { const carouselImages = {
@ -338,6 +387,12 @@ const carouselImages = {
new URL("@/assets/image/home/375/zh-TW/carousel-4.jpg", import.meta.url) new URL("@/assets/image/home/375/zh-TW/carousel-4.jpg", import.meta.url)
.href, .href,
], ],
de: [
new URL("@/assets/image/home/375/de/carousel-1.png", import.meta.url).href,
new URL("@/assets/image/home/375/de/carousel-2.png", import.meta.url).href,
new URL("@/assets/image/home/375/de/carousel-3.png", import.meta.url).href,
new URL("@/assets/image/home/375/de/carousel-4.png", import.meta.url).href,
],
}; };
const currentLanguage = ref(localStorage.getItem("language") || "zh"); const currentLanguage = ref(localStorage.getItem("language") || "zh");
const currentCarouselImages = computed(() => { const currentCarouselImages = computed(() => {
@ -896,6 +951,38 @@ watch(
}, },
{ immediate: true } { immediate: true }
); );
//
const showLanguageModal = ref(false);
// localStorage
const selectedLanguage = ref(localStorage.getItem("language") || "zh");
//
const handleLanguageChange = () => {
showLanguageModal.value = true;
//
selectedLanguage.value = localStorage.getItem("language") || "zh";
//
document.body.style.overflow = "hidden";
};
//
const confirmLanguageChange = () => {
changeLanguage(selectedLanguage.value);
//
currentLanguage.value = selectedLanguage.value;
//
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
//
const closeLanguageModal = () => {
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -912,7 +999,7 @@ watch(
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
padding: 0 138px; padding: 0 102px;
background-color: #fff; background-color: #fff;
} }
.logo { .logo {
@ -924,9 +1011,8 @@ watch(
.tabs { .tabs {
display: flex; display: flex;
gap: 128px; gap: 77px;
margin-right: 32px; margin-left: 67px;
margin-left: 60px;
} }
.tab-item { .tab-item {
@ -934,6 +1020,7 @@ watch(
align-items: center; align-items: center;
font-size: 61px; font-size: 61px;
color: #000000; color: #000000;
max-width: 251px;
cursor: pointer; cursor: pointer;
transition: color 0.3s ease; transition: color 0.3s ease;
padding: 4px 8px; padding: 4px 8px;
@ -946,7 +1033,11 @@ watch(
color: #8b59fa; color: #8b59fa;
} }
} }
.ellipsis {
white-space: nowrap; /* 防止文本换行 */
overflow: hidden; /* 隐藏溢出内容 */
text-overflow: ellipsis; /* 文本溢出时显示省略号 */
}
.title { .title {
font-size: 113px; font-size: 113px;
font-weight: 600; font-weight: 600;
@ -1189,17 +1280,6 @@ watch(
cursor: pointer; cursor: pointer;
} }
.carousel-track {
display: flex;
will-change: transform;
cursor: grab;
user-select: none;
&:active {
cursor: grabbing;
}
}
.carousel-item { .carousel-item {
width: 785px; width: 785px;
margin-right: 73px; margin-right: 73px;
@ -1242,12 +1322,106 @@ watch(
.panel:last-child .parallax-bg { .panel:last-child .parallax-bg {
height: 100%; // height: 100%; //
} }
.line-4 { .line-3 {
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 4; -webkit-line-clamp: 3;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
max-height: calc(1.5em * 4); max-height: calc(1.5em * 3);
}
/* 语言选择模态框样式 */
.language-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
touch-action: none; /* 防止触摸事件影响背景 */
}
.language-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1001;
}
.language-modal-content {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
max-height: 90vh; /* 最大高度为视口高度的90% */
background-color: #fff;
z-index: 1002;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden; /* 内容溢出隐藏 */
}
.modal-header {
padding: 67px 77px 113px 77px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 82px;
color: #000000;
position: relative;
flex-shrink: 0; /* 不允许头部收缩 */
}
.close-btn {
cursor: pointer;
}
.language-modal-body {
padding: 10px 0;
overflow-y: auto; /* 只有内容区域可滚动 */
-webkit-overflow-scrolling: touch; /* 为iOS设备提供平滑滚动 */
flex: 1; /* 内容区域填充剩余空间 */
}
.language-option {
padding: 30px 0;
font-size: 72px;
color: #333;
cursor: pointer;
transition: background-color 0.3s;
text-align: center;
}
.language-option:hover,
.language-option.active {
background-color: #f5f5f5;
color: #8b59f7;
}
.language-modal-footer {
padding: 113px 77px;
display: flex;
justify-content: center;
flex-shrink: 0; /* 不允许底部收缩 */
}
.confirm-btn {
background-color: #8b59f7;
color: white;
border: none;
border-radius: 20px;
padding: 46px 0;
width: 100%;
font-size: 82px;
cursor: pointer;
}
.confirm-btn:hover {
background-color: #7a48e6;
} }
</style> </style>

View File

@ -29,8 +29,52 @@
> >
{{ t("home.nav.businessintroduction") }} {{ t("home.nav.businessintroduction") }}
</div> </div>
<div class="tab-item" @click="handleLanguageChange">
{{ currentLanguageLabel }}
<img
src="@/assets/image/home/768/icon-language.png"
alt="language"
class="w-[18px] ml-[13px]"
/>
</div>
</div> </div>
</header> </header>
<!-- 语言选择模态框 - 使用纯div实现 -->
<div
v-if="showLanguageModal"
class="language-modal"
@touchmove.prevent.stop
>
<div class="language-modal-backdrop" @click="closeLanguageModal"></div>
<div class="language-modal-content">
<div class="modal-header font-semibold flex items-center">
{{ t("home.nav.please_select") }}
<img
src="@/assets/image/home/375/icon-close.png"
alt="close"
class="close-btn w-[51px]"
@click="closeLanguageModal"
/>
</div>
<div class="language-modal-body" @wheel.stop @touchmove.stop>
<div
v-for="option in languageOptions"
:key="option.value"
class="language-option"
:class="{ active: selectedLanguage === option.value }"
@click="selectedLanguage = option.value"
>
{{ option.label }}
</div>
</div>
<div class="language-modal-footer">
<button class="confirm-btn" @click="confirmLanguageChange">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
<main ref="main"> <main ref="main">
<div class="scroll-down" :class="{ hide: !scrollDownVisible }"> <div class="scroll-down" :class="{ hide: !scrollDownVisible }">
{{ t("home.scroll.tip") }} {{ t("home.scroll.tip") }}
@ -58,10 +102,14 @@
/> />
</div> </div>
<div class="content"> <div class="content">
<div class="text-[#10253E] text-[90px] leading-[115px] font-semibold"> <div
class="text-[#10253E] text-[90px] leading-[115px] font-semibold px-[40px]"
>
{{ t("home.section2.title1") }} {{ t("home.section2.title1") }}
</div> </div>
<div class="text-[#10253E] text-[90px] leading-[115px] font-semibold"> <div
class="text-[#10253E] text-[90px] leading-[115px] font-semibold px-[40px]"
>
{{ t("home.section2.title2") }} {{ t("home.section2.title2") }}
</div> </div>
</div> </div>
@ -270,9 +318,10 @@ import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger"; import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useHome } from "@/store/home/index.js"; import { useHome } from "@/store/home/index.js";
import { useTransitionComposable } from "@/composables/transition-composable"; import { useTransitionComposable } from "@/composables/transition-composable";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const { transitionState } = useTransitionComposable(); const { transitionState } = useTransitionComposable();
const main = ref(); const main = ref();
@ -284,7 +333,14 @@ let isScrolling = false; // 添加滚动状态标记
const scrollThreshold = 50; // const scrollThreshold = 50; //
let lastScrollTime = 0; // let lastScrollTime = 0; //
const scrollDownVisible = ref(true); const scrollDownVisible = ref(true);
const { t } = useI18n(); import { useLanguage } from "@/utils/changeLanguage.js";
const {
languageOptions,
currentLanguageLabel,
changeLanguage,
initLanguage,
currentLang,
} = useLanguage();
// //
const carouselImages = { const carouselImages = {
@ -316,12 +372,15 @@ const carouselImages = {
new URL("@/assets/image/home/768/zh-TW/carousel-4.jpg", import.meta.url) new URL("@/assets/image/home/768/zh-TW/carousel-4.jpg", import.meta.url)
.href, .href,
], ],
de: [
new URL("@/assets/image/home/768/de/carousel-1.png", import.meta.url).href,
new URL("@/assets/image/home/768/de/carousel-2.png", import.meta.url).href,
new URL("@/assets/image/home/768/de/carousel-3.png", import.meta.url).href,
new URL("@/assets/image/home/768/de/carousel-4.png", import.meta.url).href,
],
}; };
const currentLanguage = ref(localStorage.getItem("language") || "zh"); const currentLanguage = ref(localStorage.getItem("language") || "zh");
const currentCarouselImages = computed(() => {
return carouselImages[currentLanguage.value] || carouselImages.zh;
});
// //
watch( watch(
@ -334,6 +393,10 @@ watch(
{ immediate: true } { immediate: true }
); );
const currentCarouselImages = computed(() => {
return carouselImages[currentLanguage.value] || carouselImages.zh;
});
const handleTabClick = (tab) => { const handleTabClick = (tab) => {
currentTab.value = tab; currentTab.value = tab;
router.push("/" + tab); router.push("/" + tab);
@ -947,6 +1010,38 @@ watch(
}, },
{ immediate: true } { immediate: true }
); );
//
const showLanguageModal = ref(false);
// localStorage
const selectedLanguage = ref(localStorage.getItem("language") || "zh");
//
const handleLanguageChange = () => {
showLanguageModal.value = true;
//
selectedLanguage.value = localStorage.getItem("language") || "zh";
//
document.body.style.overflow = "hidden";
};
//
const confirmLanguageChange = () => {
changeLanguage(selectedLanguage.value);
//
currentLanguage.value = selectedLanguage.value;
//
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
//
const closeLanguageModal = () => {
showLanguageModal.value = false;
//
document.body.style.overflow = "";
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -1243,17 +1338,6 @@ watch(
cursor: pointer; cursor: pointer;
} }
.carousel-track {
display: flex;
will-change: transform;
cursor: grab;
user-select: none;
&:active {
cursor: grabbing;
}
}
.carousel-item { .carousel-item {
width: 785px; width: 785px;
margin-right: 73px; margin-right: 73px;
@ -1291,4 +1375,99 @@ watch(
line-height: 1.5; line-height: 1.5;
max-height: calc(1.5em * 1); max-height: calc(1.5em * 1);
} }
/* 语言选择模态框样式 */
.language-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
touch-action: none; /* 防止触摸事件影响背景 */
}
.language-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1001;
}
.language-modal-content {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
max-height: 90vh; /* 最大高度为视口高度的90% */
background-color: #fff;
z-index: 1002;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden; /* 内容溢出隐藏 */
}
.modal-header {
padding: 33px 38px 55px 38px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 40px;
color: #000000;
position: relative;
flex-shrink: 0; /* 不允许头部收缩 */
}
.close-btn {
cursor: pointer;
}
.language-modal-body {
padding: 10px 0;
overflow-y: auto; /* 只有内容区域可滚动 */
-webkit-overflow-scrolling: touch; /* 为iOS设备提供平滑滚动 */
flex: 1; /* 内容区域填充剩余空间 */
overscroll-behavior: contain; /* 阻止滚动穿透 */
}
.language-option {
padding: 30px 0;
font-size: 40px;
color: #333;
cursor: pointer;
transition: background-color 0.3s;
text-align: center;
}
.language-option:hover,
.language-option.active {
background-color: #f5f5f5;
color: #8b59f7;
}
.language-modal-footer {
padding: 40px 38px;
display: flex;
justify-content: center;
flex-shrink: 0; /* 不允许底部收缩 */
}
.confirm-btn {
background-color: #8b59f7;
color: white;
border: none;
border-radius: 10px;
padding: 23px 0;
width: 100%;
font-size: 40px;
cursor: pointer;
}
.confirm-btn:hover {
background-color: #7a48e6;
}
</style> </style>