- 在icomoon中新增了两个图标:enter和star,并更新了相关的SVG和CSS文件。 - 更新了demo.html文件,增加了新图标的展示。 - 新增了多个图片资源,包括home-b-1.webp、product-banner.webp和video.webp,丰富了页面内容。 - 在LanguageSwitcher组件中优化了下拉菜单的交互逻辑,增加了鼠标悬停效果。 - 更新了TheHeader和TheFooter组件的样式,提升了页面的视觉效果。 - 在关于我们、联系我们和产品页面中进行了内容更新,增加了SEO优化。 - 新增FAQ页面,提供常见问题解答,提升用户支持。 - 在index.vue和products/index.vue中优化了产品展示逻辑,改善了用户体验。master
@@ -9,10 +9,38 @@ | |||
<link rel="stylesheet" href="style.css"></head> | |||
<body> | |||
<div class="bgc1 clearfix"> | |||
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs: 8)</small></h1> | |||
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs: 10)</small></h1> | |||
</div> | |||
<div class="clearfix mhl ptl"> | |||
<h1 class="mvm mtn fgc1">Grid Size: Unknown</h1> | |||
<div class="glyph fs1"> | |||
<div class="clearfix bshadow0 pbs"> | |||
<span class="icon-enter"></span> | |||
<span class="mls"> icon-enter</span> | |||
</div> | |||
<fieldset class="fs0 size1of1 clearfix hidden-false"> | |||
<input type="text" readonly value="e909" class="unit size1of2" /> | |||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" /> | |||
</fieldset> | |||
<div class="fs0 bshadow0 clearfix hidden-true"> | |||
<span class="unit pvs fgc1">liga: </span> | |||
<input type="text" readonly value="" class="liga unitRight" /> | |||
</div> | |||
</div> | |||
<div class="glyph fs1"> | |||
<div class="clearfix bshadow0 pbs"> | |||
<span class="icon-star"></span> | |||
<span class="mls"> icon-star</span> | |||
</div> | |||
<fieldset class="fs0 size1of1 clearfix hidden-false"> | |||
<input type="text" readonly value="e908" class="unit size1of2" /> | |||
<input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" /> | |||
</fieldset> | |||
<div class="fs0 bshadow0 clearfix hidden-true"> | |||
<span class="unit pvs fgc1">liga: </span> | |||
<input type="text" readonly value="" class="liga unitRight" /> | |||
</div> | |||
</div> | |||
<div class="glyph fs1"> | |||
<div class="clearfix bshadow0 pbs"> | |||
<span class="icon-h1"></span> |
@@ -15,4 +15,6 @@ | |||
<glyph unicode="" glyph-name="h2" horiz-adv-x="1044" d="M671.059 752.461c-7.629 0.006-14.948-3.017-20.352-8.405l-76.928-76.928h-215.593c-7.639 0-14.964-3.035-20.366-8.436-5.402-5.4-8.434-12.725-8.434-20.364s3.033-14.964 8.434-20.364c5.402-5.402 12.727-8.436 20.366-8.436h215.593l76.928-76.885c5.396-5.404 12.717-8.442 20.352-8.448h113.792c7.639 0 14.964 3.035 20.366 8.434 5.4 5.402 8.434 12.727 8.434 20.366 0 7.637-3.035 14.962-8.434 20.364-5.402 5.402-12.727 8.436-20.366 8.436h-101.845l-56.533 56.533 56.533 56.533h101.845c7.639 0 14.964 3.035 20.366 8.436 5.4 5.402 8.434 12.727 8.434 20.364 0 7.639-3.035 14.964-8.434 20.366-5.402 5.402-12.727 8.434-20.366 8.434h-113.792zM215.692 638.352c0.006 14.029 3.454 27.843 10.045 40.225 6.591 12.384 16.122 22.961 27.756 30.799 11.634 7.84 25.015 12.702 38.969 14.161 13.952 1.457 28.050-0.534 41.053-5.797s24.515-13.641 33.524-24.393c9.009-10.754 15.24-23.556 18.146-37.28 2.905-13.726 2.395-27.953-1.485-41.435s-11.010-25.803-20.766-35.885l98.987-210.261h308.777c31.616 0 57.259-25.6 57.259-57.259v-99.541c0-7.639-3.035-14.964-8.436-20.366-5.4-5.4-12.725-8.434-20.364-8.434h-540.459c-7.639 0-14.962 3.035-20.364 8.434-5.402 5.402-8.436 12.727-8.436 20.366v99.541c0 31.616 25.643 57.259 57.259 57.259h111.061l-86.953 184.745c-12.008-1.396-24.174-0.236-35.7 3.405-11.528 3.641-22.154 9.679-31.181 17.719-9.027 8.038-16.25 17.896-21.195 28.928-4.947 11.030-7.501 22.981-7.499 35.068zM301.367 666.47c-7.452 0-14.598-2.96-19.868-8.229-5.268-5.27-8.229-12.416-8.229-19.868s2.962-14.598 8.229-19.866c5.27-5.27 12.416-8.229 19.868-8.229s14.598 2.96 19.868 8.229c5.268 5.268 8.227 12.414 8.227 19.866s-2.96 14.598-8.227 19.868c-5.27 5.27-12.416 8.229-19.868 8.229zM443.106 310.886h-155.607v-70.4h482.859v70.4h-327.253z" /> | |||
<glyph unicode="" glyph-name="h3" horiz-adv-x="1044" d="M506.72 812.567c4.59 2.701 9.821 4.126 15.145 4.126 5.327 0 10.557-1.425 15.147-4.126l288-169.386c4.488-2.641 8.206-6.408 10.787-10.927 2.582-4.521 3.938-9.637 3.933-14.844v-338.816c0.006-5.205-1.351-10.323-3.933-14.842-2.582-4.521-6.3-8.288-10.787-10.929l-288-169.385c-4.59-2.702-9.821-4.126-15.147-4.126s-10.555 1.424-15.145 4.126l-288 169.385c-4.488 2.641-8.206 6.408-10.787 10.929-2.582 4.519-3.938 9.637-3.933 14.842v338.773c-0.006 5.207 1.351 10.323 3.933 14.844s6.3 8.287 10.787 10.927l288 169.43zM263.733 600.3v-304.64l258.133-151.895 258.135 151.851v304.683l-258.135 151.893-258.133-151.893zM657.347 579.499c7.92 0 15.518-3.147 21.118-8.747 5.602-5.6 8.749-13.198 8.749-21.118v-203.264c0-3.923-0.772-7.808-2.272-11.431-1.501-3.625-3.7-6.918-6.475-9.693-2.773-2.773-6.065-4.974-9.689-6.475-3.625-1.501-7.509-2.274-11.431-2.274s-7.806 0.774-11.431 2.274c-3.623 1.501-6.916 3.702-9.689 6.475-2.775 2.775-4.974 6.067-6.475 9.693-1.501 3.623-2.272 7.509-2.272 11.431v203.264c0 7.92 3.147 15.518 8.747 21.118 5.602 5.6 13.2 8.747 21.12 8.747zM521.838 511.746c7.92 0 15.518-3.147 21.118-8.749 5.6-5.6 8.747-13.198 8.747-21.118v-135.509c0-7.922-3.147-15.518-8.747-21.12-5.6-5.6-13.198-8.747-21.118-8.747-7.922 0-15.52 3.147-21.12 8.747-5.6 5.602-8.747 13.198-8.747 21.12v135.509c0 7.92 3.147 15.518 8.747 21.118 5.6 5.602 13.198 8.749 21.12 8.749zM416.195 414.123c-0.002 7.92-3.149 15.516-8.749 21.116s-13.198 8.745-21.118 8.745c-7.92 0-15.516-3.145-21.116-8.745s-8.749-13.196-8.749-21.116v-67.753c-0.002-3.923 0.77-7.808 2.271-11.431 1.501-3.625 3.7-6.918 6.475-9.693 2.773-2.773 6.065-4.974 9.691-6.475 3.623-1.501 7.507-2.274 11.429-2.274s7.808 0.774 11.431 2.274c3.623 1.501 6.916 3.702 9.691 6.475 2.773 2.775 4.972 6.067 6.473 9.693 1.501 3.623 2.272 7.509 2.272 11.431v67.753z" /> | |||
<glyph unicode="" glyph-name="arrow-left" horiz-adv-x="1182" d="M1113.038 501.613h-946.074l362.27 344.949c18.969 18.063 19.704 48.073 1.637 67.037-18.044 18.94-48.063 19.699-67.056 1.636l-413.72-393.971c-17.904-17.92-27.79-41.72-27.79-67.061 0-25.316 9.886-49.139 28.625-67.842l412.909-393.167c9.176-8.743 20.937-13.084 32.698-13.084 12.52 0 25.039 4.931 34.358 14.714 18.068 18.968 17.333 48.955-1.636 67.017l-363.782 344.953h947.563c26.178 0 47.423 21.239 47.423 47.409s-21.246 47.41-47.423 47.41z" /> | |||
<glyph unicode="" glyph-name="star" d="M497.388 957.076c12.138-3.498 23.19-10.018 32.122-18.95s15.452-19.984 18.95-32.122l78.4-272.533 272.536-78.4c15.565-4.481 29.261-13.903 38.997-26.848 9.745-12.943 15.019-28.705 15.019-44.908s-5.274-31.963-15.019-44.907c-9.737-12.944-23.433-22.368-38.997-26.848l-272.536-78.4-78.4-272.532c-4.481-15.573-13.903-29.261-26.848-39.006s-28.705-15.010-44.908-15.010c-16.201 0-31.963 5.265-44.907 15.010s-22.367 23.433-26.848 39.006l-78.4 272.532-272.533 78.4c-15.57 4.48-29.26 13.903-39.004 26.848s-15.014 28.705-15.014 44.907c0 16.202 5.27 31.964 15.014 44.908s23.434 22.367 39.004 26.848l272.533 78.4 78.4 272.533c2.715 9.424 7.26 18.22 13.375 25.887s13.681 14.054 22.264 18.797c8.584 4.743 18.018 7.748 27.763 8.844s19.612 0.262 29.035-2.456zM476.631 831.487l-63.989-222.731-13.74-47.562-270.442-77.952 270.442-77.728 13.74-47.562 63.989-222.805 64.138 222.805 13.739 47.562 47.563 13.739 222.805 64.064-270.368 77.803-13.739 47.563-64.138 222.805zM476.631 831.487l-63.989-222.731-13.74-47.562-270.442-77.952 270.442-77.728 13.74-47.562 63.989-222.805 64.138 222.805 13.739 47.562 47.563 13.739 222.805 64.064-270.368 77.803-13.739 47.563-64.138 222.805z" /> | |||
<glyph unicode="" glyph-name="enter" d="M810.667 704c-23.564 0-42.667-19.103-42.667-42.667v-170.667c0-23.564-19.103-42.667-42.667-42.667v0h-409.173l55.467 55.040c7.753 7.753 12.548 18.463 12.548 30.293 0 23.661-19.181 42.841-42.841 42.841-11.83 0-22.541-4.795-30.293-12.548v0l-128-128c-3.773-3.937-6.808-8.616-8.859-13.791l-0.101-0.289c-2.024-4.793-3.201-10.366-3.201-16.213s1.176-11.42 3.305-16.495l-0.105 0.281c2.152-5.464 5.187-10.143 8.974-14.095l-0.014 0.015 128-128c7.733-7.795 18.45-12.621 30.293-12.621s22.56 4.826 30.291 12.618l0.003 0.003c7.795 7.733 12.621 18.45 12.621 30.293s-4.826 22.56-12.618 30.291l-0.003 0.003-55.467 55.040h409.173c70.692 0 128 57.308 128 128v0 170.667c0 23.564-19.103 42.667-42.667 42.667v0z" /> | |||
</font></defs></svg> |
@@ -1,10 +1,10 @@ | |||
@font-face { | |||
font-family: 'icomoon'; | |||
src: url('fonts/icomoon.eot?ezztx0'); | |||
src: url('fonts/icomoon.eot?ezztx0#iefix') format('embedded-opentype'), | |||
url('fonts/icomoon.ttf?ezztx0') format('truetype'), | |||
url('fonts/icomoon.woff?ezztx0') format('woff'), | |||
url('fonts/icomoon.svg?ezztx0#icomoon') format('svg'); | |||
src: url('fonts/icomoon.eot?7g9lqp'); | |||
src: url('fonts/icomoon.eot?7g9lqp#iefix') format('embedded-opentype'), | |||
url('fonts/icomoon.ttf?7g9lqp') format('truetype'), | |||
url('fonts/icomoon.woff?7g9lqp') format('woff'), | |||
url('fonts/icomoon.svg?7g9lqp#icomoon') format('svg'); | |||
font-weight: normal; | |||
font-style: normal; | |||
font-display: block; | |||
@@ -25,6 +25,12 @@ | |||
-moz-osx-font-smoothing: grayscale; | |||
} | |||
.icon-enter:before { | |||
content: "\e909"; | |||
} | |||
.icon-star:before { | |||
content: "\e908"; | |||
} | |||
.icon-h1:before { | |||
content: "\e904"; | |||
} |
@@ -1,45 +1,37 @@ | |||
<template> | |||
<div class="relative inline-block text-left " ref="dropdownContainerRef"> | |||
<div class="relative inline-block text-left" ref="dropdownContainerRef" @mouseleave="handleMouseLeave"> | |||
<div | |||
@click="toggleDropdown" | |||
class="flex justify-center items-center gap-2 text-white opacity-80 text-sm" | |||
@mouseenter="handleMouseEnter" | |||
class="flex items-center gap-1 text-white opacity-80 text-sm hover:opacity-100 cursor-pointer py-2 transition-opacity" | |||
> | |||
<i class="icon-i18n"></i> | |||
{{ currentLocaleName || "Language" }} | |||
<i class="icon-i18n mr-1"></i> | |||
<span>{{ currentLocaleName || "Language" }}</span> | |||
<svg | |||
class="h-3 w-3 text-white/60 transition-transform duration-200" | |||
:class="{'rotate-180': isDropdownOpen}" | |||
fill="none" viewBox="0 0 24 24" stroke="currentColor" | |||
> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M19 9l-7 7-7-7" /> | |||
</svg> | |||
</div> | |||
<transition | |||
enter-active-class="transition ease-out duration-100" | |||
enter-from-class="transform opacity-0 scale-95" | |||
enter-to-class="transform opacity-100 scale-100" | |||
leave-active-class="transition ease-in duration-75" | |||
leave-from-class="transform opacity-100 scale-100" | |||
leave-to-class="transform opacity-0 scale-95" | |||
> | |||
<transition name="fade-down"> | |||
<div | |||
v-if="isDropdownOpen" | |||
class="origin-top-right absolute right-0 mt-2 w-36 rounded-md shadow-lg bg-[var(--color-bg)] ring-1 ring-black ring-opacity-5 focus:outline-none z-10" | |||
role="menu" | |||
aria-orientation="vertical" | |||
aria-labelledby="options-menu" | |||
@mouseenter="handleMouseEnter" | |||
class="absolute right-0 top-full mt-1 w-max min-w-[120px] bg-slate-800/90 backdrop-blur-md rounded-lg shadow-xl p-2 z-10" | |||
> | |||
<div class="py-1" role="none"> | |||
<a | |||
v-for="locale in availableLocales" | |||
:key="locale.code" | |||
href="#" | |||
@click.prevent="selectLanguage(locale.code)" | |||
:class="[ | |||
'block px-4 py-2 text-sm opacity-80', | |||
currentLocale === locale.code | |||
? 'text-white opacity-100 font-bold' | |||
: 'text-white hover: opacity-100', | |||
]" | |||
role="menuitem" | |||
> | |||
{{ locale.name }} | |||
</a> | |||
</div> | |||
<ul class="space-y-1"> | |||
<li v-for="locale in availableLocales" :key="locale.code"> | |||
<button | |||
@click="selectLanguage(locale.code)" | |||
class="block w-full text-left text-base text-gray-200 hover:text-white hover:bg-white/10 transition-all duration-150 rounded px-3 py-1.5" | |||
:class="[ locale.code === currentLocale ? 'font-bold opacity-100 bg-white/15' : '' ]" | |||
> | |||
{{ locale.name }} | |||
</button> | |||
</li> | |||
</ul> | |||
</div> | |||
</transition> | |||
</div> | |||
@@ -52,7 +44,6 @@ | |||
*/ | |||
import { ref, computed } from "vue"; | |||
import { useI18n } from "#imports"; // 修正 useI18n 导入 | |||
import { onClickOutside } from "@vueuse/core"; // 导入 onClickOutside | |||
// 定义语言代码的类型,应该与 i18n 配置中的一致 | |||
type LocaleCode = "zh" | "en" | "ja"; // 你需要根据你的 i18n 配置更新这个类型 | |||
@@ -60,7 +51,8 @@ type LocaleCode = "zh" | "en" | "ja"; // 你需要根据你的 i18n 配置更新 | |||
const { locale, locales, setLocale } = useI18n(); | |||
const currentLocale = computed(() => locale.value); | |||
const isDropdownOpen = ref(false); | |||
const dropdownContainerRef = ref(null); // 创建 ref | |||
const dropdownContainerRef = ref(null); // 保留 ref,虽然 onClickOutside 移除了,但未来可能有用 | |||
let leaveTimeout: ReturnType<typeof setTimeout> | null = null; // Timeout for mouseleave delay | |||
// 可用语言列表 | |||
const availableLocales = computed(() => { | |||
@@ -72,19 +64,12 @@ const availableLocales = computed(() => { | |||
// 当前选中语言的名称 | |||
const currentLocaleName = computed(() => { | |||
const current = availableLocales.value.find( | |||
(l) => l.code === currentLocale.value | |||
const current = availableLocales.value.find((l: { code: string; name: string }) => | |||
l.code === locale.value | |||
); | |||
return current ? current.name : ""; | |||
}); | |||
/** | |||
* 切换下拉菜单的显示/隐藏状态 | |||
*/ | |||
function toggleDropdown() { | |||
isDropdownOpen.value = !isDropdownOpen.value; | |||
} | |||
/** | |||
* 选择语言并关闭下拉菜单 | |||
* @param {string} langCode - 选择的语言代码 | |||
@@ -99,13 +84,23 @@ async function selectLanguage(langCode: string) { | |||
// 这里可以添加用户反馈,例如显示一个错误提示 | |||
} | |||
} | |||
isDropdownOpen.value = false; // 关闭下拉菜单 | |||
handleMouseLeave(); // 使用 handleMouseLeave 关闭 | |||
} | |||
// 点击外部关闭下拉菜单 | |||
onClickOutside(dropdownContainerRef, () => { | |||
if (isDropdownOpen.value) { | |||
isDropdownOpen.value = false; | |||
// --- Dropdown Logic (like Products dropdown) --- | |||
function handleMouseEnter() { | |||
if (leaveTimeout) { | |||
clearTimeout(leaveTimeout); | |||
leaveTimeout = null; | |||
} | |||
}); | |||
isDropdownOpen.value = true; | |||
} | |||
function handleMouseLeave() { | |||
// Delay closing the dropdown slightly | |||
leaveTimeout = setTimeout(() => { | |||
isDropdownOpen.value = false; | |||
}, 150); // 150ms delay | |||
} | |||
// --- End Dropdown Logic --- | |||
</script> |
@@ -8,7 +8,7 @@ | |||
> | |||
<!-- Logo & 描述 --> | |||
<div | |||
class="flex flex-col lg:items-start :lg:justify-start sm:items-center text-center lg:text-left" | |||
class="flex flex-col lg:items-start :lg:justify-start sm:items-center text-center lg:text-left col-span-2 lg:col-span-1" | |||
> | |||
<h3 class="mb-4"> | |||
<i class="icon-brand text-white text-2xl"></i> |
@@ -1,37 +1,122 @@ | |||
<template> | |||
<!-- Header --> | |||
<header class="fixed top-0 z-50 w-full bg-slate-900/70 backdrop-blur-[50px]"> | |||
<div class="max-w-screen-2xl mx-auto px-4 sm:px-6 lg:px-10"> | |||
<div class="h-[55px] flex justify-between items-center sm:h-[72px]"> | |||
<div class="flex justify-start items-center gap-12 lg:gap-24"> | |||
<nuxt-link :to="homePath" class="mt-[5px] flex-shrink-0"> | |||
<i class="icon-brand text-white text-1xl sm:text-2xl"></i> | |||
<nuxt-link :to="homePath" class="brand-link mt-[5px] flex-shrink-0"> | |||
<i | |||
class="icon-brand text-white text-1xl sm:text-2xl block transition-[transform,filter] duration-500 ease-in-out" | |||
></i> | |||
</nuxt-link> | |||
<!-- Desktop Menu --> | |||
<nav class="hidden md:flex justify-start items-start gap-7 lg:gap-14"> | |||
<nuxt-link | |||
v-for="item in menuItems" | |||
:key="item.path" | |||
class="justify-start text-white text-sm opacity-80 hover:opacity-100 transition-opacity" | |||
:to="item.path" | |||
:class="{ | |||
'text-white font-bold opacity-100': $route.path === item.path, | |||
}" | |||
> | |||
{{ $t(item.label) }} | |||
</nuxt-link> | |||
<template v-for="item in menuItems" :key="item.label"> | |||
<!-- Regular Link --> | |||
<nuxt-link | |||
v-if="!item.isDropdown" | |||
class="main-nav-link relative inline-block justify-start text-white text-sm opacity-80 hover:opacity-100 transition-opacity py-2 px-3 rounded-md" | |||
:to="item.path" | |||
:class="[ | |||
$route.path === item.path | |||
? 'font-bold opacity-100 bg-white/15' | |||
: '', | |||
]" | |||
> | |||
{{ $t(item.label) }} | |||
</nuxt-link> | |||
<!-- Dropdown Container --> | |||
<div | |||
v-else-if="item.isDropdown" | |||
class="relative" | |||
@mouseleave="handleMouseLeave" | |||
> | |||
<!-- Dropdown Trigger --> | |||
<div | |||
@mouseenter="handleMouseEnter(item.label)" | |||
class="justify-start text-white text-sm opacity-80 hover:opacity-100 transition-opacity cursor-pointer flex items-center gap-1 py-2" | |||
:class="{ | |||
'text-white font-bold opacity-100': | |||
openDropdown === item.label || | |||
$route.path.startsWith(item.pathPrefix), | |||
}" | |||
> | |||
<span>{{ $t(item.label) }}</span> | |||
<!-- Dropdown Arrow --> | |||
<svg | |||
class="h-3 w-3 text-white/60" | |||
fill="none" | |||
viewBox="0 0 24 24" | |||
stroke="currentColor" | |||
> | |||
<path | |||
stroke-linecap="round" | |||
stroke-linejoin="round" | |||
stroke-width="3" | |||
d="M19 9l-7 7-7-7" | |||
/> | |||
</svg> | |||
</div> | |||
<!-- Dropdown Panel --> | |||
<transition name="fade-down"> | |||
<div | |||
v-if="item.isDropdown && openDropdown === item.label" | |||
@mouseenter="handleMouseEnter(item.label)" | |||
class="absolute left-0 top-full mt-1 w-max min-w-[450px] bg-slate-800/90 backdrop-blur-md rounded-lg shadow-xl p-4 z-10 grid grid-cols-2 gap-4" | |||
> | |||
<div | |||
v-for="(section, index) in item.children" | |||
:key="index" | |||
class="bg-black/10 p-4 rounded-md" | |||
> | |||
<h3 | |||
class="text-sm font-semibold text-gray-300 uppercase tracking-wider mb-4 flex items-center gap-2" | |||
> | |||
<i | |||
:class="[ | |||
index === 0 ? 'icon-tag' : 'icon-target', | |||
'text-gray-400', | |||
]" | |||
></i> | |||
<span>{{ $t(section.title) }}</span> | |||
</h3> | |||
<ul class="space-y-3"> | |||
<li v-for="link in section.items" :key="link.path"> | |||
<nuxt-link | |||
:to="link.path" | |||
@click="handleMouseLeave" | |||
class="block text-base text-gray-200 hover:text-white hover:bg-white/10 transition-all duration-150 rounded px-2 py-1" | |||
:class="{ | |||
'text-white font-bold bg-white/5': | |||
$route.path === link.path, | |||
}" | |||
> | |||
{{ $t(link.label) }} | |||
</nuxt-link> | |||
</li> | |||
</ul> | |||
</div> | |||
</div> | |||
</transition> | |||
</div> | |||
</template> | |||
</nav> | |||
</div> | |||
<div class="flex justify-start items-center gap-4 md:gap-6"> | |||
<!-- Search --> | |||
<div | |||
class="w-auto h-8 relative items-center opacity-40 rounded-2xl border border-color-[rgba(255,255,255,0.4)] pr-4 hover:opacity-100 transition-opacity duration-300 hidden md:flex" | |||
@click="openSearch" | |||
class="w-auto h-8 relative items-center opacity-40 rounded-2xl pr-4 hover:opacity-100 transition-opacity duration-300 hidden md:flex cursor-pointer" | |||
style="border: 0.5px solid rgba(255, 255, 255, 0.4)" | |||
> | |||
<button | |||
<span | |||
class="flex items-center justify-center w-8 h-8 opacity-80 hover:opacity-100 text-white" | |||
> | |||
<i class="icon-search text-sm"></i> | |||
</button> | |||
</span> | |||
<span | |||
class="hidden lg:inline-block ml-1 text-white text-sm opacity-80" | |||
> | |||
@@ -98,29 +183,134 @@ | |||
id="mobile-menu" | |||
> | |||
<div class="px-2 pt-2 pb-3 space-y-1 sm:px-3"> | |||
<nuxt-link | |||
v-for="item in menuItems" | |||
:key="item.path" | |||
:to="item.path" | |||
@click="closeMobileMenu" | |||
class="block px-3 py-2 rounded-md text-base font-medium" | |||
:class="[ | |||
$route.path === item.path | |||
? 'bg-gray-900 text-white' | |||
: 'text-gray-300 hover:bg-gray-700 hover:text-white', | |||
]" | |||
>{{ $t(item.label) }}</nuxt-link | |||
> | |||
<template v-for="item in menuItems" :key="item.label"> | |||
<!-- Mobile Regular Link --> | |||
<nuxt-link | |||
v-if="!item.isDropdown" | |||
:to="item.path" | |||
@click="closeMobileMenu" | |||
class="block px-3 py-2 rounded-md text-base font-medium" | |||
:class="[ | |||
$route.path === item.path | |||
? 'bg-gray-900 text-white' | |||
: 'text-gray-300 hover:bg-gray-700 hover:text-white', | |||
]" | |||
> | |||
{{ $t(item.label) }} | |||
</nuxt-link> | |||
<!-- Mobile Dropdown Section --> | |||
<div v-else class="mt-2"> | |||
<h3 | |||
class="px-3 pt-2 pb-1 text-sm font-semibold text-gray-400 uppercase tracking-wider" | |||
> | |||
{{ $t(item.label) }} | |||
</h3> | |||
<div | |||
v-for="section in item.children" | |||
:key="section.title" | |||
class="mt-1" | |||
> | |||
<!-- Optional: Section title for mobile? --> | |||
<!-- <h4 class="px-3 pt-1 text-xs font-medium text-gray-500">{{ $t(section.title) }}</h4> --> | |||
<nuxt-link | |||
v-for="link in section.items" | |||
:key="link.path" | |||
:to="link.path" | |||
@click="closeMobileMenu" | |||
class="block pl-6 pr-3 py-2 rounded-md text-base font-medium text-gray-300 hover:bg-gray-700 hover:text-white" | |||
:class="{ | |||
'bg-gray-900 text-white': $route.path === link.path, | |||
}" | |||
> | |||
{{ $t(link.label) }} | |||
</nuxt-link> | |||
</div> | |||
</div> | |||
</template> | |||
</div> | |||
</div> | |||
</transition> | |||
</header> | |||
<!-- Search Layer (Moved outside header) --> | |||
<transition name="search-fade-scale"> | |||
<div | |||
v-if="isSearchOpen" | |||
class="fixed inset-0 z-[60] bg-black/80 backdrop-blur-md flex items-start justify-center pt-20" | |||
@click.self="closeSearch" | |||
> | |||
<div | |||
class="bg-gradient-to-br from-slate-800 to-slate-900 p-8 rounded-lg relative w-full max-w-2xl mx-4 search-modal-content shadow-xl" | |||
> | |||
<button | |||
@click="closeSearch" | |||
class="absolute top-3 right-3 text-gray-500 hover:text-white hover:bg-white/10 rounded-full p-2 transition-colors" | |||
> | |||
<svg | |||
class="h-5 w-5" | |||
fill="none" | |||
viewBox="0 0 24 24" | |||
stroke="currentColor" | |||
> | |||
<path | |||
stroke-linecap="round" | |||
stroke-linejoin="round" | |||
stroke-width="2" | |||
d="M6 18L18 6M6 6l12 12" | |||
/> | |||
</svg> | |||
</button> | |||
<h2 class="text-white text-xl mb-6">{{ $t("common.search") }}</h2> | |||
<!-- Input with Icon --> | |||
<div class="relative mb-6"> | |||
<span class="absolute inset-y-0 left-0 flex items-center pl-3"> | |||
<i class="icon-search text-gray-400 text-sm"></i> | |||
</span> | |||
<input | |||
ref="searchInputRef" | |||
type="text" | |||
:placeholder=" | |||
$t('common.searchPlaceholder') || 'Enter search term...' | |||
" | |||
class="w-full p-3 pl-10 pr-10 rounded bg-slate-700 text-white border border-slate-600 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500" | |||
/> | |||
<!-- Enter Icon --> | |||
<span | |||
class="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer group" | |||
> | |||
<i | |||
class="icon-enter text-gray-400 group-hover:text-blue-400 transition-colors" | |||
></i> | |||
</span> | |||
</div> | |||
<!-- Search results could go here --> | |||
<!-- Hot Keywords Section --> | |||
<div class="mt-6"> | |||
<h3 class="text-gray-400 text-sm mb-3"> | |||
{{ $t("common.hotKeywords") || "热门搜索" }} | |||
</h3> | |||
<div class="flex flex-wrap gap-3"> | |||
<button | |||
v-for="keyword in hotKeywords" | |||
:key="keyword" | |||
@click="searchHotKeyword(keyword)" | |||
class="px-4 py-1.5 bg-slate-700 text-white/80 rounded-full text-sm hover:bg-blue-600 hover:text-white transition-colors duration-200" | |||
> | |||
{{ keyword }} | |||
</button> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</transition> | |||
</template> | |||
<script setup lang="ts"> | |||
import { ref, computed } from "vue"; | |||
import { ref, computed, watch, nextTick } from "#imports"; | |||
import { useI18n } from "vue-i18n"; | |||
import { useRuntimeConfig } from '#app'; // 导入 useRuntimeConfig | |||
/** | |||
* 页面头部组件 | |||
@@ -129,27 +319,61 @@ import { useRuntimeConfig } from '#app'; // 导入 useRuntimeConfig | |||
const { t, locale } = useI18n(); | |||
const config = useRuntimeConfig(); | |||
// 从运行时配置获取默认语言,如果未配置则默认为 'en' | |||
const defaultLocale = config.public.i18n?.defaultLocale || 'en'; | |||
const defaultLocale = config.public.i18n?.defaultLocale || "en"; | |||
const mobileMenuOpen = ref(false); | |||
const isSearchOpen = ref(false); | |||
const searchInputRef = ref<HTMLInputElement | null>(null); | |||
const openDropdown = ref<string | null>(null); | |||
let leaveTimeout: ReturnType<typeof setTimeout> | null = null; // Timeout for mouseleave delay | |||
// 添加热门关键字 | |||
const hotKeywords = ref(["SSD", "SD", "DDR4"]); | |||
// 使用 computed 来定义 homePath,根据是否为默认语言调整路径 | |||
const homePath = computed(() => { | |||
// 如果是默认语言,路径为根路径 '/' | |||
// 否则,路径为 '/<locale>/' | |||
return locale.value === defaultLocale ? '/' : `/${locale.value}/`; | |||
return locale.value === defaultLocale ? "/" : `/${locale.value}/`; | |||
}); | |||
// 使用 computed 来定义 menuItems,根据是否为默认语言调整路径 | |||
const menuItems = computed(() => { | |||
// 判断当前是否为默认语言 | |||
const isDefaultLocale = locale.value === defaultLocale; | |||
// 如果是默认语言,路径前缀为空字符串,否则为 '/<locale>' | |||
const prefix = isDefaultLocale ? '' : `/${locale.value}`; | |||
const prefix = isDefaultLocale ? "" : `/${locale.value}`; | |||
return [ | |||
// 首页路径特殊处理:默认语言为 '/', 其他语言为 '/<locale>/' | |||
{ label: "common.home", path: isDefaultLocale ? '/' : `${prefix}/` }, | |||
{ label: "common.products", path: `${prefix}/products` }, | |||
{ label: "common.home", path: isDefaultLocale ? "/" : `${prefix}/` }, | |||
{ | |||
label: "common.products", | |||
isDropdown: true, | |||
pathPrefix: `${prefix}/products`, | |||
children: [ | |||
{ | |||
title: "common.productCategories", | |||
items: [ | |||
{ label: "SSD", path: `${prefix}/products?category=ssd` }, | |||
{ label: "DRAM", path: `${prefix}/products?category=dram` }, | |||
{ label: "NAND", path: `${prefix}/products?category=nand` }, | |||
], | |||
}, | |||
{ | |||
title: "common.byUsage", | |||
items: [ | |||
{ | |||
label: "Enterprise", | |||
path: `${prefix}/products?usage=enterprise`, | |||
}, | |||
{ label: "Consumer", path: `${prefix}/products?usage=consumer` }, | |||
{ | |||
label: "Industrial", | |||
path: `${prefix}/products?usage=industrial`, | |||
}, | |||
], | |||
}, | |||
], | |||
}, | |||
{ label: "common.faq", path: `${prefix}/faq` }, | |||
{ label: "common.about", path: `${prefix}/about` }, | |||
{ label: "common.contact", path: `${prefix}/contact` }, | |||
@@ -169,6 +393,57 @@ function toggleMobileMenu() { | |||
function closeMobileMenu() { | |||
mobileMenuOpen.value = false; | |||
} | |||
/** | |||
* 打开搜索层 | |||
*/ | |||
function openSearch() { | |||
isSearchOpen.value = true; | |||
} | |||
/** | |||
* 关闭搜索层 | |||
*/ | |||
function closeSearch() { | |||
isSearchOpen.value = false; | |||
} | |||
/** | |||
* 搜索热门关键字 (示例) | |||
* @param keyword | |||
*/ | |||
function searchHotKeyword(keyword: string) { | |||
console.log("Searching for hot keyword:", keyword); | |||
// 可以在这里实现填充输入框或直接执行搜索的逻辑 | |||
// 例如: searchInputValue.value = keyword; | |||
closeSearch(); // 点击后可以关闭搜索层 | |||
} | |||
// 监听搜索层状态,打开时自动聚焦输入框 | |||
watch(isSearchOpen, (newValue: boolean) => { | |||
if (newValue) { | |||
nextTick(() => { | |||
searchInputRef.value?.focus(); | |||
}); | |||
} | |||
}); | |||
// --- Dropdown Logic --- | |||
function handleMouseEnter(label: string) { | |||
if (leaveTimeout) { | |||
clearTimeout(leaveTimeout); | |||
leaveTimeout = null; | |||
} | |||
openDropdown.value = label; | |||
} | |||
function handleMouseLeave() { | |||
// Delay closing the dropdown slightly | |||
leaveTimeout = setTimeout(() => { | |||
openDropdown.value = null; | |||
}, 150); // 150ms delay | |||
} | |||
// --- End Dropdown Logic --- | |||
</script> | |||
<style lang="scss" scoped> | |||
@@ -176,6 +451,21 @@ header { | |||
user-select: none; | |||
} | |||
/* Brand icon hover effect */ | |||
.brand-link { | |||
&:hover { | |||
.icon-brand { | |||
transform: scale(1.05); | |||
filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.6)); | |||
} | |||
} | |||
} | |||
/* Base style for icon to apply will-change */ | |||
.icon-brand { | |||
will-change: transform, filter; /* Hint for browser optimization */ | |||
} | |||
/* Transition for mobile menu */ | |||
.slide-fade-enter-active { | |||
transition: all 0.3s ease-out; | |||
@@ -191,6 +481,39 @@ header { | |||
opacity: 0; | |||
} | |||
/* Transition for dropdown */ | |||
.fade-down-enter-active, | |||
.fade-down-leave-active { | |||
transition: all 0.2s ease-out; | |||
} | |||
.fade-down-enter-from, | |||
.fade-down-leave-to { | |||
opacity: 0; | |||
transform: translateY(-10px); | |||
} | |||
/* Transition for search overlay */ | |||
.search-fade-scale-enter-active, | |||
.search-fade-scale-leave-active { | |||
transition: opacity 0.3s ease-out; | |||
} | |||
.search-fade-scale-enter-from, | |||
.search-fade-scale-leave-to { | |||
opacity: 0; | |||
} | |||
/* 为搜索框内容添加独立的、稍延迟的动画 */ | |||
.search-modal-content { | |||
transition: transform 0.3s ease-out 0.05s; | |||
} | |||
.search-fade-scale-enter-from .search-modal-content, | |||
.search-fade-scale-leave-to .search-modal-content { | |||
transform: scale(0.95) translateY(10px); | |||
} | |||
/* Keep the sticky header consistent */ | |||
.sticky { | |||
position: sticky; |
@@ -0,0 +1,136 @@ | |||
import { ref, computed } from 'vue'; | |||
/** | |||
* 生成随机字符串 | |||
* @param length 字符串长度 | |||
* @returns 随机字符串 | |||
*/ | |||
function generateRandomString(length: number): string { | |||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; | |||
let result = ''; | |||
for (let i = 0; i < length; i++) { | |||
result += characters.charAt(Math.floor(Math.random() * characters.length)); | |||
} | |||
return result; | |||
} | |||
/** | |||
* 为验证码文本生成扭曲的 SVG | |||
* @param text 验证码文本 | |||
* @returns SVG 字符串 | |||
*/ | |||
function generateCaptchaSvg(text: string): string { | |||
const width = 150; | |||
const height = 50; | |||
const fontSize = 30; | |||
const letters = text.split(''); | |||
// 应用于整个 SVG 的滤镜,增加噪点和轻微扭曲 | |||
const filter = ` | |||
<filter id="captchaNoise"> | |||
<feTurbulence baseFrequency="0.7 0.8" numOctaves="3" seed="${Math.random() * 100}" stitchTiles="stitch" type="fractalNoise" result="turbulence"/> | |||
<feDisplacementMap in="SourceGraphic" in2="turbulence" scale="3" xChannelSelector="R" yChannelSelector="G" result="displacement"/> | |||
<feGaussianBlur in="displacement" stdDeviation="0.5" /> | |||
</filter> | |||
`; | |||
// 为每个字母生成 <text> 元素,并应用随机变换 | |||
const textElements = letters.map((char, index) => { | |||
const x = (width / text.length) * (index + 0.5); | |||
const y = height / 2 + fontSize / 3; // 基线调整 | |||
const rotate = Math.random() * 30 - 15; // -15 到 15 度旋转 | |||
const skewX = Math.random() * 10 - 5; // 轻微倾斜 | |||
const fill = `hsl(${Math.random() * 360}, 70%, 70%)`; // 随机 HSL 颜色 | |||
return `<text | |||
x="${x}" | |||
y="${y}" | |||
font-size="${fontSize}" | |||
font-family="Arial, sans-serif" | |||
font-weight="bold" | |||
text-anchor="middle" | |||
fill="${fill}" | |||
transform="rotate(${rotate} ${x} ${y}) skewX(${skewX})" | |||
style="letter-spacing: 1px;" | |||
>${char}</text>`; | |||
}).join(''); | |||
// 添加几条干扰线 | |||
let lines = ''; | |||
for (let i = 0; i < 3; i++) { | |||
lines += `<line | |||
x1="${Math.random() * width * 0.8}" | |||
y1="${Math.random() * height}" | |||
x2="${Math.random() * width * 0.8 + width * 0.2}" | |||
y2="${Math.random() * height}" | |||
stroke="rgba(180, 180, 200, 0.3)" | |||
stroke-width="2" | |||
/>`; | |||
} | |||
return ` | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
width="${width}" | |||
height="${height}" | |||
viewBox="0 0 ${width} ${height}" | |||
style="background-color: rgba(55, 65, 81, 0.3); border-radius: 6px; border: 1px solid rgba(107, 114, 128, 0.5);" | |||
> | |||
<defs> | |||
${filter} | |||
</defs> | |||
<g style="filter: url(#captchaNoise);"> | |||
${textElements} | |||
${lines} | |||
</g> | |||
</svg> | |||
`; | |||
} | |||
export function useCaptcha(length: number = 5) { | |||
const captchaText = ref(''); | |||
const userInput = ref(''); | |||
const captchaSvg = ref(''); | |||
const error = ref(''); | |||
/** | |||
* 生成新的验证码 | |||
*/ | |||
const generateCaptcha = () => { | |||
const newText = generateRandomString(length); | |||
captchaText.value = newText; | |||
captchaSvg.value = generateCaptchaSvg(newText); | |||
userInput.value = ''; // 清空用户输入 | |||
error.value = ''; // 清除错误 | |||
}; | |||
/** | |||
* 验证用户输入 | |||
* @returns 是否验证通过 | |||
*/ | |||
const validateCaptcha = (): boolean => { | |||
if (!userInput.value) { | |||
error.value = '请输入验证码'; | |||
return false; | |||
} | |||
if (userInput.value.toLowerCase() !== captchaText.value.toLowerCase()) { | |||
error.value = '验证码错误'; | |||
generateCaptcha(); // 错误后自动刷新 | |||
return false; | |||
} | |||
error.value = ''; | |||
return true; | |||
}; | |||
// 初始化生成第一个验证码 | |||
generateCaptcha(); | |||
return { | |||
userInput, | |||
captchaSvg, | |||
error, | |||
generateCaptcha, | |||
validateCaptcha, | |||
}; | |||
} |
@@ -1,49 +1,133 @@ | |||
export default { | |||
common: { | |||
home: 'Home', | |||
products: 'Products', | |||
faq: 'FAQ', | |||
about: 'About Us', | |||
contact: 'Contact', | |||
search: 'Search', | |||
language: 'Language' | |||
home: "Home", | |||
products: "Products", | |||
faq: "FAQ", | |||
about: "About Us", | |||
contact: "Contact", | |||
search: "Search", | |||
language: "Language", | |||
searchPlaceholder: "Search products, FAQ, etc.", | |||
hotKeywords: "Hot Keywords", | |||
productCategories: "Product Categories", | |||
byUsage: "By Usage", | |||
}, | |||
home: { | |||
title: 'Welcome to Hanye Website', | |||
description: 'We provide high quality products and services', | |||
learnMore: 'Learn More' | |||
title: "Welcome to Hanye Website", | |||
description: "Providing high-quality products and services.", | |||
learnMore: "Learn More", | |||
}, | |||
products: { | |||
title: 'Our Products', | |||
viewDetails: 'View Details', | |||
consultation: 'Product consultation, quotation is welcome', | |||
consultation_button: 'Contact Us', | |||
usage:'Storage by Use', | |||
usage_title:'Choose storage by use case.', | |||
support:'Support', | |||
support_description:'10+ years of professional tech support.', | |||
development:'Own R&D and Manufacturing', | |||
development_description:'We develop, manufacture, and sell memory products online.', | |||
develop:'Innovation', | |||
develop_description:'Developing innovative solutions continuously.', | |||
strong_point:'Our strengths', | |||
strong_point_title:'Our strengths/Why choose us', | |||
title: "Our Products", | |||
viewDetails: "View Details", | |||
consultation: | |||
"Feel free to inquire about product consultations and quotes.", | |||
consultation_button: "Contact Us", | |||
usage: "Choose storage by usage", | |||
usage_title: "Select storage products based on your needs.", | |||
support: "Support", | |||
support_description: | |||
"Expert technical support from our team with over 10 years of experience.", | |||
development: "Independent Development, Manufacturing, and Sales", | |||
development_description: | |||
"Independent development, manufacturing, and online sales of memory and related products.", | |||
develop: "Towards Continuous Development", | |||
develop_description: | |||
"Diversifying products and relentlessly pursuing new creativity.", | |||
strong_point: "Our Strengths", | |||
strong_point_title: "Our Strengths / Why Choose Us", | |||
}, | |||
faq: { | |||
title: 'Frequently Asked Questions', | |||
searchPlaceholder: 'Search questions' | |||
title: "Frequently Asked Questions", | |||
searchPlaceholder: "Search questions", | |||
}, | |||
about: { | |||
title: 'About Us', | |||
history: 'Company History', | |||
team: 'Our Team', | |||
values: 'Our Values' | |||
title: "About Us", | |||
meta: { | |||
title: "About Us - Hanye", | |||
description: | |||
"Learn about Hanye's company information, history, and business scope. We are dedicated to the development, manufacturing, and sales of memory and related products.", | |||
}, | |||
intro: { | |||
title: "About Hanye", | |||
paragraph1: | |||
"Hanye was established in 2003 with its operational headquarters in Shenyang, China.", | |||
paragraph2: | |||
"From its founding to the present, we have grown into a comprehensive enterprise integrating the development, manufacturing, and sales of memory (storage media) and related products.", | |||
paragraph3: | |||
"We have also established a complete after-sales service system to provide full support. We strive to be a trusted partner for our customers and will continue to make further efforts.", | |||
}, | |||
overview: { | |||
title: "Company Overview", | |||
companyNameLabel: "Company Name", | |||
companyNameValue: "Hanye", | |||
englishNameLabel: "English Name", | |||
englishNameValue: "Hanye Technology Co., Ltd.", | |||
ceoLabel: "CEO / Representative Director", | |||
ceoValue: "ZHENG XIAO DONG", | |||
employeesLabel: "Number of Employees", | |||
employeesValue: "30", | |||
addressLabel: "Address", | |||
addressValue: | |||
"803, NO.6, AiTe, 90-6# SanHao Street, Heping District, ShenYang, China", | |||
telLabel: "TEL", | |||
telValue: "86)024-8399-0696", | |||
faxLabel: "FAX", | |||
faxValue: "86)024-8399-0696", | |||
businessLabel: "Business Activities", | |||
businessValue1: | |||
"Development, manufacturing, and sales of flash memory products", | |||
businessValue2: "Development, manufacturing, and sales of SSD products", | |||
businessValue3: "Related businesses", | |||
}, | |||
contact: { | |||
title: "Contact Information", | |||
emailLabel: "E-mail", | |||
emailValue: "hanye#hanye.cn", | |||
hoursLabel: "Business Hours", | |||
hoursValue1: "Weekdays 9:00 - 18:00", | |||
hoursValue2: "Closed on Saturdays, Sundays, and holidays", | |||
phoneLabel: "Phone", | |||
phoneValue: "86)024-8399-0696", | |||
phoneNote: "Please call for inquiries regarding visits.", | |||
}, | |||
}, | |||
contact: { | |||
title: 'Contact Us', | |||
name: 'Name', | |||
email: 'Email', | |||
message: 'Message', | |||
submit: 'Submit' | |||
} | |||
} | |||
title: "Contact Us", | |||
name: "Name", | |||
email: "Email Address", | |||
message: "Message", | |||
submit: "Send Message", | |||
form: { | |||
title: "Leave us a message", | |||
nameLabel: "Name", | |||
emailLabel: "Email Address", | |||
messageLabel: "Message", | |||
captchaLabel: "Captcha", | |||
submitLabel: "Send Message", | |||
successMessage: "Message sent successfully. We will contact you soon.", | |||
submitLoading: "Sending...", | |||
captchaRefresh: "Refresh Captcha", | |||
captchaRequired: "Please enter the captcha code", | |||
captchaIncorrect: "Incorrect captcha code", | |||
nameRequired: "Please enter your name", | |||
emailRequired: "Please enter your email address", | |||
emailInvalid: "Please enter a valid email address", | |||
messageRequired: "Please enter your message", | |||
}, | |||
info: { | |||
title: "Contact Info", | |||
description: "Leave us a message, and we will get back to you shortly.", | |||
addressLabel: "Address", | |||
addressValue1: "803, NO.6, AiTe, 90-6# SanHao Street,", | |||
addressValue2: "Heping District, ShenYang, China", | |||
phoneLabel: "Phone", | |||
phoneValue: "86)024-8399-0696", | |||
emailLabel: "Email", | |||
emailValue: "info#hanye.com", | |||
hoursLabel: "Business Hours", | |||
hoursValue1: "Mon - Fri: 9:00 AM - 6:00 PM", | |||
hoursValue2: "Sat - Sun: Closed", | |||
}, | |||
}, | |||
}; |
@@ -1,50 +1,121 @@ | |||
export default { | |||
common: { | |||
home: 'ホーム', | |||
products: '製品', | |||
faq: 'よくある質問', | |||
about: '会社概要', | |||
contact: 'お問い合わせ', | |||
search: '検索', | |||
language: '言語' | |||
home: "ホーム", | |||
products: "製品", | |||
faq: "よくある質問", | |||
about: "会社概要", | |||
contact: "お問い合わせ", | |||
search: "検索", | |||
language: "言語", | |||
searchPlaceholder: "製品、FAQ などを検索", | |||
hotKeywords: "人気のキーワード", | |||
productCategories: "製品カテゴリー", | |||
byUsage: "用途で選ぶ", | |||
}, | |||
home: { | |||
title: 'Hanye ウェブサイトへようこそ', | |||
description: '高品質の製品とサービスを提供しています', | |||
learnMore: '詳細を見る' | |||
title: "Hanye ウェブサイトへようこそ", | |||
description: "高品質の製品とサービスを提供しています", | |||
learnMore: "詳細を見る", | |||
}, | |||
products: { | |||
title: '当社の製品', | |||
viewDetails: '詳細を見る', | |||
consultation: '製品に関するご相談、お見積もりはお気軽にどうぞ', | |||
consultation_button: 'お問い合わせ', | |||
usage:'使い方でストレージを選ぼう', | |||
usage_title:'用途に応じてストレージ製品を選ぶ。', | |||
support:'サポート', | |||
support_description:'10年以上のキャリアを誇るチームが専門技術でサポート', | |||
development:'独自に開発・製造、販売', | |||
development_description:'メモリ及び関連製品の独自開発、製造・オンラインショップでの販売', | |||
develop:'たゆまぬ発展へ', | |||
develop_description:'製品の多様化を図り、新たな創意への飽くなき挑戦', | |||
strong_point:'当社の強み', | |||
strong_point_title:'当社の強み/選ばれる理由', | |||
title: "当社の製品", | |||
viewDetails: "詳細を見る", | |||
consultation: "製品に関するご相談、お見積もりはお気軽にどうぞ", | |||
consultation_button: "お問い合わせ", | |||
usage: "使い方でストレージを選ぼう", | |||
usage_title: "用途に応じてストレージ製品を選ぶ。", | |||
support: "サポート", | |||
support_description: "10年以上のキャリアを誇るチームが専門技術でサポート", | |||
development: "独自に開発・製造、販売", | |||
development_description: | |||
"メモリ及び関連製品の独自開発、製造・オンラインショップでの販売", | |||
develop: "たゆまぬ発展へ", | |||
develop_description: "製品の多様化を図り、新たな創意への飽くなき挑戦", | |||
strong_point: "当社の強み", | |||
strong_point_title: "当社の強み/選ばれる理由", | |||
}, | |||
faq: { | |||
title: 'よくある質問', | |||
searchPlaceholder: '質問を検索' | |||
title: "よくある質問", | |||
searchPlaceholder: "質問を検索", | |||
}, | |||
about: { | |||
title: '会社概要', | |||
history: '会社の歴史', | |||
team: 'チームメンバー', | |||
values: '企業価値' | |||
title: "当社について", | |||
meta: { | |||
title: "当社について - Hanye", | |||
description: "Hanyeの会社情報、沿革、事業内容をご覧ください。メモリ及び関連製品の開発・製造・販売に取り組んでいます。" | |||
}, | |||
intro: { | |||
title: "当社について", | |||
paragraph1: "Hanye は中国瀋陽に運営本部をおき 2003年に設立されました。", | |||
paragraph2: "創業から現在に至るまで、メモリ(記憶媒体) 及び 関連製品の開発・製造・販売を統べる総合企業に成長いたしました。", | |||
paragraph3: "また万全なアフターサービス体制も構築し全力でサポートいたします。お客様に信頼いただけるパートナーを目指し 一層の努力を重ねてまいります。", | |||
}, | |||
overview: { | |||
title: "会社概要", | |||
companyNameLabel: "会社名", | |||
companyNameValue: "Hanye", | |||
englishNameLabel: "英文社名", | |||
englishNameValue: "Hanye Technology Co., Ltd.", | |||
ceoLabel: "代表取締役", | |||
ceoValue: "ZHENG XIAO DONG", | |||
employeesLabel: "従業員数", | |||
employeesValue: "30名", | |||
addressLabel: "所在地", | |||
addressValue: "803, NO.6, AiTe, 90-6# SanHao Street, Heping District, ShenYang, China", | |||
telLabel: "TEL", | |||
telValue: "86)024-8399-0696", | |||
faxLabel: "FAX", | |||
faxValue: "86)024-8399-0696", | |||
businessLabel: "事業内容", | |||
businessValue1: "フラッシュメモリーの開発・製造・販売", | |||
businessValue2: "SSD製品の開発・製造・販売", | |||
businessValue3: "その関連事業", | |||
}, | |||
contact: { | |||
title: "連絡先", | |||
emailLabel: "E-mail", | |||
emailValue: "hanye#hanye.cn", | |||
hoursLabel: "営業時間", | |||
hoursValue1: "平日9:00~18:00", | |||
hoursValue2: "土日祝定休", | |||
phoneLabel: "電話", | |||
phoneValue: "86)024-8399-0696", | |||
phoneNote: "ご来社の場合はお電話でお問い合わせください", | |||
} | |||
}, | |||
contact: { | |||
title: 'お問い合わせ', | |||
name: 'お名前', | |||
email: 'メールアドレス', | |||
message: 'メッセージ', | |||
submit: '送信' | |||
} | |||
} | |||
title: "お問い合わせ", | |||
name: "お名前", | |||
email: "メールアドレス", | |||
message: "メッセージ", | |||
submit: "送信", | |||
form: { | |||
title: "メッセージを残してください", | |||
nameLabel: "お名前", | |||
emailLabel: "メールアドレス", | |||
messageLabel: "メッセージ", | |||
captchaLabel: "検証コード", | |||
submitLabel: "送信", | |||
successMessage: "メッセージを送信しました。すぐに連絡いたします。", | |||
submitLoading: "送信中...", | |||
captchaRefresh: "検証コードを更新", | |||
captchaRequired: "検証コードを入力してください", | |||
captchaIncorrect: "検証コードが間違っています", | |||
nameRequired: "お名前を入力してください", | |||
}, | |||
info: { | |||
title: "お問い合わせ", | |||
description: "メッセージを残してください。すぐに連絡いたします。", | |||
addressLabel: "住所", | |||
addressValue1: "803, NO.6, AiTe, 90-6# SanHao Street, Heping District, ShenYang, China", | |||
addressValue2: "", | |||
phoneLabel: "電話番号", | |||
phoneValue: "86)024-8399-0696", | |||
emailLabel: "メールアドレス", | |||
emailValue: "info#hanye.com", | |||
hoursLabel: "営業時間", | |||
hoursValue1: "月曜日 - 金曜日 9:00-18:00", | |||
hoursValue2: "土曜日 - 日曜日 9:00-18:00", | |||
}, | |||
}, | |||
}; |
@@ -1,49 +1,131 @@ | |||
export default { | |||
common: { | |||
home: '首页', | |||
products: '产品', | |||
faq: '常见问题', | |||
about: '关于我们', | |||
contact: '联系我们', | |||
search: '搜索', | |||
language: '语言' | |||
home: "首页", | |||
products: "产品", | |||
faq: "常见问题", | |||
about: "关于我们", | |||
contact: "联系我们", | |||
search: "搜索", | |||
language: "语言", | |||
searchPlaceholder: "搜索关键字, 产品、FAQ 等", | |||
hotKeywords: "热门搜索", | |||
productCategories: "产品分类", | |||
byUsage: "按用途", | |||
}, | |||
home: { | |||
title: '欢迎来到Hanye官网', | |||
description: '我们提供高质量的产品和服务', | |||
learnMore: '了解更多' | |||
title: "欢迎来到Hanye官网", | |||
description: "我们提供高质量的产品和服务", | |||
learnMore: "了解更多", | |||
}, | |||
products: { | |||
title: '我们的产品', | |||
viewDetails: '查看详情', | |||
consultation: '欢迎进行产品咨询,我们将在第一时间回复您', | |||
consultation_button: '联系我们', | |||
usage:'使用方法选择存储', | |||
usage_title:'根据用途选择存储产品。', | |||
support:'支持', | |||
support_description:'拥有10年以上经验的团队提供专业技术支持', | |||
development:'独立开发和制造', | |||
development_description:'独立开发和制造内存及相关产品,并在网上商店销售', | |||
develop:'不断发展', | |||
develop_description:'不断开发和制造产品,提供创新解决方案', | |||
strong_point:'我们的优势', | |||
strong_point_title:'我们的优势/选择我们的理由', | |||
title: "我们的产品", | |||
viewDetails: "查看详情", | |||
consultation: "欢迎进行产品咨询,我们将在第一时间回复您", | |||
consultation_button: "联系我们", | |||
usage: "使用方法选择存储", | |||
usage_title: "根据用途选择存储产品。", | |||
support: "支持", | |||
support_description: "拥有10年以上经验的团队提供专业技术支持", | |||
development: "独立开发和制造", | |||
development_description: "独立开发和制造内存及相关产品,并在网上商店销售", | |||
develop: "不断发展", | |||
develop_description: "不断开发和制造产品,提供创新解决方案", | |||
strong_point: "我们的优势", | |||
strong_point_title: "我们的优势/选择我们的理由", | |||
}, | |||
faq: { | |||
title: '常见问题', | |||
searchPlaceholder: '搜索问题' | |||
title: "常见问题", | |||
searchPlaceholder: "搜索问题", | |||
}, | |||
about: { | |||
title: '关于我们', | |||
history: '公司历史', | |||
team: '团队成员', | |||
values: '企业价值观' | |||
title: "关于我们", | |||
meta: { | |||
title: "关于我们 - Hanye", | |||
description: "了解 Hanye 的公司信息、发展历程和业务范围。我们致力于内存及相关产品的开发、制造和销售。" | |||
}, | |||
intro: { | |||
title: "公司简介", | |||
paragraph1: "Hanye 成立于2003年,运营总部位于中国沈阳。", | |||
paragraph2: "自创立至今,我们已成长为集内存(存储介质)及相关产品的研发、制造、销售于一体的综合性企业。", | |||
paragraph3: "我们建立了完善的售后服务体系,竭诚为您提供支持。我们致力于成为客户信赖的合作伙伴,并将为此付出更多努力。", | |||
}, | |||
overview: { | |||
title: "公司概要", | |||
companyNameLabel: "公司名称", | |||
companyNameValue: "Hanye", | |||
englishNameLabel: "英文名称", | |||
englishNameValue: "Hanye Technology Co., Ltd.", | |||
ceoLabel: "法人代表", | |||
ceoValue: "郑晓东", // Translated name if appropriate, otherwise keep original | |||
employeesLabel: "员工人数", | |||
employeesValue: "30人", | |||
addressLabel: "地址", | |||
addressValue: "中国辽宁省沈阳市和平区三好街90-6号艾特国际6号楼803室", // More detailed Chinese address | |||
telLabel: "电话", | |||
telValue: "86)024-8399-0696", | |||
faxLabel: "传真", | |||
faxValue: "86)024-8399-0696", | |||
businessLabel: "业务范围", | |||
businessValue1: "闪存产品的开发、制造、销售", | |||
businessValue2: "SSD产品的开发、制造、销售", | |||
businessValue3: "及相关业务", | |||
}, | |||
contact: { | |||
title: "联系方式", | |||
emailLabel: "电子邮箱", | |||
emailValue: "hanye#hanye.cn", | |||
hoursLabel: "营业时间", | |||
hoursValue1: "工作日 9:00 - 18:00", | |||
hoursValue2: "周六、周日及法定节假日休息", | |||
phoneLabel: "电话", | |||
phoneValue: "86)024-8399-0696", | |||
phoneNote: "如需来访,请先电话联系。", | |||
} | |||
}, | |||
contact: { | |||
title: '联系我们', | |||
name: '姓名', | |||
email: '邮箱', | |||
message: '消息', | |||
submit: '提交' | |||
} | |||
} | |||
title: "联系我们", | |||
name: "姓名", | |||
email: "邮箱", | |||
message: "消息", | |||
submit: "提交", | |||
form: { | |||
title: "给我们留言", | |||
nameLabel: "姓名", | |||
emailLabel: "邮箱", | |||
messageLabel: "消息", | |||
captchaLabel: "验证码", | |||
submitLabel: "提交", | |||
successMessage: "消息已成功发送,我们会尽快与您联系。", | |||
submitLoading: "正在发送...", | |||
captchaRefresh: "点击刷新验证码", | |||
captchaRequired: "请输入验证码", | |||
captchaIncorrect: "验证码不正确", | |||
nameRequired: "请输入您的姓名", | |||
emailRequired: "请输入您的邮箱", | |||
messageRequired: "请输入您的消息", | |||
emailInvalid: "请输入有效的邮箱地址", | |||
}, | |||
validation: { | |||
nameRequired: "请输入您的姓名", | |||
emailRequired: "请输入您的邮箱", | |||
messageRequired: "请输入您的消息", | |||
emailInvalid: "请输入有效的邮箱地址", | |||
}, | |||
captcha: { | |||
required: "请输入验证码", | |||
incorrect: "验证码不正确", | |||
}, | |||
info: { | |||
title: "联系我们", | |||
description: "欢迎给我们留言,我们将在第一时间回复您", | |||
addressLabel: "地址", | |||
addressValue1: "803, NO.6, AiTe, 90-6# SanHao Street, Heping District, ShenYang, China", | |||
addressValue2: "中国辽宁省沈阳市和平区三好街90-6号艾特国际大厦803室", | |||
phoneLabel: "电话", | |||
emailLabel: "邮箱", | |||
hoursLabel: "工作时间", | |||
hoursValue1: "周一至周五 9:00-18:00 (节假日除外)", | |||
hoursValue2: "周六至周日 9:00-18:00 (节假日除外)", | |||
}, | |||
}, | |||
}; |
@@ -1,88 +1,246 @@ | |||
<template> | |||
<div class="py-8"> | |||
<div class="container-custom"> | |||
<h1 class="text-3xl font-bold mb-8">{{ $t('about.title') }}</h1> | |||
<div class="bg-white border border-gray-200 rounded-lg overflow-hidden"> | |||
<div class="p-8"> | |||
<section class="mb-12"> | |||
<h2 class="text-2xl font-semibold mb-4">{{ $t('about.history') }}</h2> | |||
<p class="text-gray-700 mb-4"> | |||
汉业科技成立于2020年,是一家专注于提供高质量产品和服务的科技公司。 | |||
从创立之初,我们就致力于通过创新技术解决客户面临的挑战,为客户创造价值。 | |||
</p> | |||
<p class="text-gray-700"> | |||
经过多年的发展,我们已成为行业内备受认可的品牌,拥有众多忠实客户。 | |||
我们的产品覆盖了多个领域,为客户提供了全方位的解决方案。 | |||
</p> | |||
</section> | |||
<section class="mb-12"> | |||
<h2 class="text-2xl font-semibold mb-4">{{ $t('about.values') }}</h2> | |||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6"> | |||
<div class="bg-blue-50 p-6 rounded-lg"> | |||
<h3 class="text-xl font-medium mb-2 text-blue-700">创新精神</h3> | |||
<p class="text-gray-700"> | |||
我们不断探索新技术、新方法,为客户提供创新的解决方案。 | |||
</p> | |||
</div> | |||
<div class="bg-green-50 p-6 rounded-lg"> | |||
<h3 class="text-xl font-medium mb-2 text-green-700">专业品质</h3> | |||
<p class="text-gray-700"> | |||
我们坚持高标准,确保每一个产品都经过严格的质量控制。 | |||
</p> | |||
<div class="w-full h-[55px] sm:h-[72px]"></div> | |||
<div | |||
class="max-w-full px-4 py-16 md:px-8 lg:px-10 bg-gray-900 text-gray-300 min-h-screen relative overflow-hidden" | |||
> | |||
<!-- Subtle Pattern Background --> | |||
<div class="absolute inset-0 opacity-[0.03] pointer-events-none" | |||
style="background-image: linear-gradient(45deg, #fff 12%, transparent 12.5%, transparent 87%, #fff 87.5%, #fff), | |||
linear-gradient(-45deg, #fff 12%, transparent 12.5%, transparent 87%, #fff 87.5%, #fff); | |||
background-size: 8px 8px;"> | |||
</div> | |||
<!-- Content Wrapper --> | |||
<div class="relative max-w-screen-xl mx-auto"> | |||
<h1 class="text-4xl md:text-6xl mb-12 text-center font-normal text-white"> | |||
{{ $t("about.title") }} | |||
</h1> | |||
<div class="space-y-12"> | |||
<!-- Introduction Section --> | |||
<section | |||
class="relative bg-gray-800/80 border border-gray-700 rounded-xl overflow-hidden shadow-xl backdrop-blur-sm p-6 sm:p-8 lg:p-10" | |||
> | |||
<div class="mb-8"> | |||
<h2 | |||
class="text-2xl font-semibold text-gray-100 sm:text-3xl inline-block" | |||
> | |||
{{ $t("about.intro.title") }} | |||
</h2> | |||
<div | |||
class="h-1 w-20 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full mt-2" | |||
></div> | |||
</div> | |||
<div class="space-y-4 text-lg text-gray-300 leading-relaxed"> | |||
<p>{{ $t("about.intro.paragraph1") }}</p> | |||
<p>{{ $t("about.intro.paragraph2") }}</p> | |||
<p>{{ $t("about.intro.paragraph3") }}</p> | |||
</div> | |||
</section> | |||
<!-- Company Overview Section --> | |||
<section | |||
class="relative bg-gray-800/80 border border-gray-700 rounded-xl overflow-hidden shadow-xl backdrop-blur-sm p-6 sm:p-8 lg:p-10" | |||
> | |||
<div class="mb-8"> | |||
<h2 | |||
class="text-2xl font-semibold text-gray-100 sm:text-3xl inline-block" | |||
> | |||
{{ $t("about.overview.title") }} | |||
</h2> | |||
<div | |||
class="h-1 w-20 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full mt-2" | |||
></div> | |||
</div> | |||
<dl | |||
class="grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-2 lg:grid-cols-3" | |||
> | |||
<div class="sm:col-span-1"> | |||
<dt class="text-base font-semibold text-gray-100"> | |||
{{ $t("about.overview.companyNameLabel") }} | |||
</dt> | |||
<dd class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.overview.companyNameValue") }} | |||
</dd> | |||
</div> | |||
<div class="sm:col-span-1"> | |||
<dt class="text-base font-semibold text-gray-100"> | |||
{{ $t("about.overview.englishNameLabel") }} | |||
</dt> | |||
<dd class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.overview.englishNameValue") }} | |||
</dd> | |||
</div> | |||
<div class="sm:col-span-1"> | |||
<dt class="text-base font-semibold text-gray-100"> | |||
{{ $t("about.overview.ceoLabel") }} | |||
</dt> | |||
<dd class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.overview.ceoValue") }} | |||
</dd> | |||
</div> | |||
<div class="sm:col-span-1"> | |||
<dt class="text-base font-semibold text-gray-100"> | |||
{{ $t("about.overview.employeesLabel") }} | |||
</dt> | |||
<dd class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.overview.employeesValue") }} | |||
</dd> | |||
</div> | |||
<div class="sm:col-span-2"> | |||
<dt class="text-base font-semibold text-gray-100"> | |||
{{ $t("about.overview.addressLabel") }} | |||
</dt> | |||
<dd class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.overview.addressValue") }} | |||
</dd> | |||
</div> | |||
<div class="sm:col-span-1"> | |||
<dt class="text-base font-semibold text-gray-100"> | |||
{{ $t("about.overview.telLabel") }} | |||
</dt> | |||
<dd class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.overview.telValue") }} | |||
</dd> | |||
</div> | |||
<div class="sm:col-span-1"> | |||
<dt class="text-base font-semibold text-gray-100"> | |||
{{ $t("about.overview.faxLabel") }} | |||
</dt> | |||
<dd class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.overview.faxValue") }} | |||
</dd> | |||
</div> | |||
<div class="sm:col-span-2 lg:col-span-3"> | |||
<dt class="text-base font-semibold text-gray-100"> | |||
{{ $t("about.overview.businessLabel") }} | |||
</dt> | |||
<dd class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.overview.businessValue1") }}<br /> | |||
{{ $t("about.overview.businessValue2") }}<br /> | |||
{{ $t("about.overview.businessValue3") }} | |||
</dd> | |||
</div> | |||
</dl> | |||
</section> | |||
<!-- Contact Info Section --> | |||
<section | |||
class="relative bg-gray-800/80 border border-gray-700 rounded-xl overflow-hidden shadow-xl backdrop-blur-sm p-6 sm:p-8 lg:p-10" | |||
> | |||
<div class="mb-8"> | |||
<h2 | |||
class="text-2xl font-semibold text-gray-100 sm:text-3xl inline-block" | |||
> | |||
{{ $t("about.contact.title") }} | |||
</h2> | |||
<div | |||
class="h-1 w-20 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full mt-2" | |||
></div> | |||
</div> | |||
<div class="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3"> | |||
<div class="flex items-start space-x-4"> | |||
<div | |||
class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-gradient-to-br from-blue-500 to-purple-600 text-white rounded-lg shadow-md" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-5 w-5" | |||
viewBox="0 0 20 20" | |||
fill="currentColor" | |||
> | |||
<path | |||
d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" | |||
/> | |||
<path | |||
d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" | |||
/> | |||
</svg> | |||
</div> | |||
<div class="bg-yellow-50 p-6 rounded-lg"> | |||
<h3 class="text-xl font-medium mb-2 text-yellow-700">客户至上</h3> | |||
<p class="text-gray-700"> | |||
客户的满意是我们最大的追求,我们致力于超越客户期望。 | |||
<div> | |||
<h3 class="text-base font-medium text-gray-100"> | |||
{{ $t("about.contact.emailLabel") }} | |||
</h3> | |||
<p class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.contact.emailValue") }} | |||
</p> | |||
</div> | |||
</div> | |||
</section> | |||
<section> | |||
<h2 class="text-2xl font-semibold mb-4">{{ $t('about.team') }}</h2> | |||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> | |||
<div class="text-center"> | |||
<div class="h-40 w-40 bg-gray-200 rounded-full mx-auto mb-4"></div> | |||
<h3 class="text-xl font-medium">张三</h3> | |||
<p class="text-gray-600">创始人 & CEO</p> | |||
<div class="flex items-start space-x-4"> | |||
<div | |||
class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-gradient-to-br from-blue-500 to-purple-600 text-white rounded-lg shadow-md" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-5 w-5" | |||
viewBox="0 0 20 20" | |||
fill="currentColor" | |||
> | |||
<path | |||
fill-rule="evenodd" | |||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" | |||
clip-rule="evenodd" | |||
/> | |||
</svg> | |||
</div> | |||
<div class="text-center"> | |||
<div class="h-40 w-40 bg-gray-200 rounded-full mx-auto mb-4"></div> | |||
<h3 class="text-xl font-medium">李四</h3> | |||
<p class="text-gray-600">技术总监</p> | |||
<div> | |||
<h3 class="text-base font-medium text-gray-100"> | |||
{{ $t("about.contact.hoursLabel") }} | |||
</h3> | |||
<p class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.contact.hoursValue1") }} | |||
</p> | |||
<p class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.contact.hoursValue2") }} | |||
</p> | |||
</div> | |||
<div class="text-center"> | |||
<div class="h-40 w-40 bg-gray-200 rounded-full mx-auto mb-4"></div> | |||
<h3 class="text-xl font-medium">王五</h3> | |||
<p class="text-gray-600">产品经理</p> | |||
</div> | |||
<div class="flex items-start space-x-4"> | |||
<div | |||
class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-gradient-to-br from-blue-500 to-purple-600 text-white rounded-lg shadow-md" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-5 w-5" | |||
viewBox="0 0 20 20" | |||
fill="currentColor" | |||
> | |||
<path | |||
d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" | |||
/> | |||
</svg> | |||
</div> | |||
<div class="text-center"> | |||
<div class="h-40 w-40 bg-gray-200 rounded-full mx-auto mb-4"></div> | |||
<h3 class="text-xl font-medium">赵六</h3> | |||
<p class="text-gray-600">营销总监</p> | |||
<div> | |||
<h3 class="text-base font-medium text-gray-100"> | |||
{{ $t("about.contact.phoneLabel") }} | |||
</h3> | |||
<p class="mt-1 text-base text-gray-400"> | |||
{{ $t("about.contact.phoneValue") }} | |||
</p> | |||
<p class="mt-1 text-sm text-gray-500"> | |||
{{ $t("about.contact.phoneNote") }} | |||
</p> | |||
</div> | |||
</div> | |||
</section> | |||
</div> | |||
</div> | |||
</section> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup lang="ts"> | |||
/** | |||
* 关于我们页面 | |||
* 展示公司历史、价值观和团队成员 | |||
*/ | |||
import { useI18n } from "vue-i18n"; | |||
const { t } = useI18n(); | |||
// SEO优化 | |||
// SEO | |||
useHead({ | |||
title: '关于我们 - Hanye', | |||
title: t("about.meta.title"), | |||
meta: [ | |||
{ name: 'description', content: '了解我们的公司历史、企业价值观和团队成员。汉业科技致力于提供高质量的产品和服务。' } | |||
] | |||
{ | |||
name: "description", | |||
content: t("about.meta.description"), | |||
}, | |||
], | |||
}); | |||
</script> | |||
</script> |
@@ -1,133 +1,387 @@ | |||
<template> | |||
<div class="py-8"> | |||
<div class="container-custom"> | |||
<h1 class="text-3xl font-bold mb-8">{{ $t('contact.title') }}</h1> | |||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> | |||
<div class="w-full h-[55px] sm:h-[72px]"></div> | |||
<div | |||
class="max-w-full px-4 py-16 md:px-8 lg:px-10 bg-gradient-to-br from-gray-900 via-gray-900 to-black text-gray-300 min-h-screen" | |||
> | |||
<div class="max-w-screen-xl mx-auto"> | |||
<h1 class="text-4xl md:text-6xl mb-12 text-center font-normal text-white"> | |||
{{ $t("contact.title") }} | |||
</h1> | |||
<div class="grid grid-cols-1 gap-10 lg:grid-cols-2 lg:gap-16"> | |||
<!-- 联系表单 --> | |||
<div class="bg-white border border-gray-200 rounded-lg overflow-hidden"> | |||
<div class="p-8"> | |||
<h2 class="text-2xl font-semibold mb-6">给我们留言</h2> | |||
<div | |||
class="relative bg-gray-800/70 border border-gray-700 rounded-xl overflow-hidden shadow-2xl backdrop-blur-sm transition-all duration-300 ease-in-out hover:shadow-blue-500/30 hover:border-blue-500/50 group" | |||
> | |||
<div | |||
class="absolute inset-0 bg-gradient-to-r from-transparent via-blue-900/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" | |||
></div> | |||
<div class="relative p-6 sm:p-8 lg:p-10"> | |||
<div class="mb-8"> | |||
<h2 | |||
class="text-2xl font-semibold text-gray-100 sm:text-3xl inline-block" | |||
> | |||
{{ $t("contact.form.title") }} | |||
</h2> | |||
<div | |||
class="h-1 w-20 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full mt-2" | |||
></div> | |||
</div> | |||
<ErrorBoundary :error="error"> | |||
<form @submit.prevent="submitForm"> | |||
<div class="mb-4"> | |||
<label for="name" class="block text-gray-700 font-medium mb-2">{{ $t('contact.name') }}</label> | |||
<input | |||
type="text" | |||
id="name" | |||
v-model="formData.name" | |||
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" | |||
:class="{ 'border-red-500': formErrors.name }" | |||
<form @submit.prevent="submitForm" class="space-y-10"> | |||
<div class="relative"> | |||
<input | |||
type="text" | |||
id="name" | |||
v-model="formData.name" | |||
class="peer block w-full appearance-none bg-transparent border-0 border-b-2 px-1 pt-5 pb-2 text-base text-gray-100 focus:outline-none focus:ring-0 placeholder-transparent transition-colors duration-300 focus:border-b-[3px]" | |||
:class="[ | |||
formErrors.name | |||
? 'border-red-500 focus:border-red-500' | |||
: 'border-gray-600 focus:border-blue-500', | |||
]" | |||
:placeholder="$t('contact.name')" | |||
required | |||
:aria-invalid="formErrors.name ? 'true' : 'false'" | |||
aria-describedby="name-error" | |||
/> | |||
<label | |||
for="name" | |||
class="absolute left-1 top-5 origin-[0] -translate-y-4 scale-75 transform text-sm duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:font-medium" | |||
:class="[ | |||
formErrors.name | |||
? 'text-red-400 peer-focus:text-red-400' | |||
: 'text-gray-400 peer-focus:text-blue-400', | |||
]" | |||
>{{ $t("contact.name") }}</label | |||
> | |||
<p v-if="formErrors.name" class="mt-1 text-sm text-red-600">{{ formErrors.name }}</p> | |||
<p | |||
v-if="formErrors.name" | |||
id="name-error" | |||
class="mt-1.5 text-xs text-red-400 sm:text-sm" | |||
> | |||
{{ formErrors.name }} | |||
</p> | |||
</div> | |||
<div class="mb-4"> | |||
<label for="email" class="block text-gray-700 font-medium mb-2">{{ $t('contact.email') }}</label> | |||
<input | |||
type="email" | |||
id="email" | |||
v-model="formData.email" | |||
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" | |||
:class="{ 'border-red-500': formErrors.email }" | |||
<div class="relative"> | |||
<input | |||
type="email" | |||
id="email" | |||
v-model="formData.email" | |||
class="peer block w-full appearance-none bg-transparent border-0 border-b-2 px-1 pt-5 pb-2 text-base text-gray-100 focus:outline-none focus:ring-0 placeholder-transparent transition-colors duration-300 focus:border-b-[3px]" | |||
:class="[ | |||
formErrors.email | |||
? 'border-red-500 focus:border-red-500' | |||
: 'border-gray-600 focus:border-blue-500', | |||
]" | |||
:placeholder="$t('contact.email')" | |||
required | |||
:aria-invalid="formErrors.email ? 'true' : 'false'" | |||
aria-describedby="email-error" | |||
/> | |||
<label | |||
for="email" | |||
class="absolute left-1 top-5 origin-[0] -translate-y-4 scale-75 transform text-sm duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:font-medium" | |||
:class="[ | |||
formErrors.email | |||
? 'text-red-400 peer-focus:text-red-400' | |||
: 'text-gray-400 peer-focus:text-blue-400', | |||
]" | |||
>{{ $t("contact.email") }}</label | |||
> | |||
<p v-if="formErrors.email" class="mt-1 text-sm text-red-600">{{ formErrors.email }}</p> | |||
<p | |||
v-if="formErrors.email" | |||
id="email-error" | |||
class="mt-1.5 text-xs text-red-400 sm:text-sm" | |||
> | |||
{{ formErrors.email }} | |||
</p> | |||
</div> | |||
<div class="mb-6"> | |||
<label for="message" class="block text-gray-700 font-medium mb-2">{{ $t('contact.message') }}</label> | |||
<textarea | |||
id="message" | |||
v-model="formData.message" | |||
class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 h-32" | |||
:class="{ 'border-red-500': formErrors.message }" | |||
<div class="relative"> | |||
<textarea | |||
id="message" | |||
v-model="formData.message" | |||
class="peer block w-full appearance-none bg-transparent border-0 border-b-2 px-1 pt-5 pb-2 text-base text-gray-100 focus:outline-none focus:ring-0 placeholder-transparent h-36 resize-none transition-colors duration-300 focus:border-b-[3px]" | |||
:class="[ | |||
formErrors.message | |||
? 'border-red-500 focus:border-red-500' | |||
: 'border-gray-600 focus:border-blue-500', | |||
]" | |||
:placeholder="$t('contact.message')" | |||
required | |||
rows="5" | |||
:aria-invalid="formErrors.message ? 'true' : 'false'" | |||
aria-describedby="message-error" | |||
></textarea> | |||
<p v-if="formErrors.message" class="mt-1 text-sm text-red-600">{{ formErrors.message }}</p> | |||
<label | |||
for="message" | |||
class="absolute left-1 top-5 origin-[0] -translate-y-4 scale-75 transform text-sm duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:font-medium" | |||
:class="[ | |||
formErrors.message | |||
? 'text-red-400 peer-focus:text-red-400' | |||
: 'text-gray-400 peer-focus:text-blue-400', | |||
]" | |||
>{{ $t("contact.message") }}</label | |||
> | |||
<p | |||
v-if="formErrors.message" | |||
id="message-error" | |||
class="mt-1.5 text-xs text-red-400 sm:text-sm" | |||
> | |||
{{ formErrors.message }} | |||
</p> | |||
</div> | |||
<!-- Captcha Section --> | |||
<div class="relative pt-2"> | |||
<div class="flex items-center space-x-3"> | |||
<!-- Captcha Input --> | |||
<div class="flex-grow relative"> | |||
<input | |||
type="text" | |||
id="captcha" | |||
v-model="captcha.userInput.value" | |||
class="peer block w-full appearance-none bg-transparent border-0 border-b-2 px-1 pt-5 pb-2 text-base text-gray-100 focus:outline-none focus:ring-0 placeholder-transparent transition-colors duration-300 focus:border-b-[3px]" | |||
:class="[ | |||
captcha.error.value | |||
? 'border-red-500 focus:border-red-500' | |||
: 'border-gray-600 focus:border-blue-500', | |||
]" | |||
:placeholder="$t('contact.form.captchaLabel')" | |||
required | |||
autocomplete="off" | |||
aria-describedby="captcha-error" | |||
:aria-invalid="captcha.error.value ? 'true' : 'false'" | |||
/> | |||
<label | |||
for="captcha" | |||
class="absolute left-1 top-5 origin-[0] -translate-y-4 scale-75 transform text-sm duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:-translate-y-4 peer-focus:scale-75 peer-focus:font-medium" | |||
:class="[ | |||
captcha.error.value | |||
? 'text-red-400 peer-focus:text-red-400' | |||
: 'text-gray-400 peer-focus:text-blue-400', | |||
]" | |||
>{{ $t("contact.form.captchaLabel") }}</label | |||
> | |||
</div> | |||
<!-- Captcha Image/SVG --> | |||
<div | |||
class="flex-shrink-0 cursor-pointer rounded-md overflow-hidden transition-all duration-200 ease-in-out hover:scale-105 hover:shadow-md active:scale-100" | |||
v-html="captcha.captchaSvg.value" | |||
@click="captcha.generateCaptcha()" | |||
:title="$t('contact.form.captchaRefresh')" | |||
style="line-height: 0" | |||
></div> | |||
<!-- Refresh Button --> | |||
<button | |||
type="button" | |||
@click="captcha.generateCaptcha()" | |||
class="flex-shrink-0 p-2 text-gray-500 hover:text-blue-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-gray-800 rounded-full hover:bg-gray-700/50 transition-all duration-200 ease-in-out" | |||
:aria-label="$t('contact.form.captchaRefresh')" | |||
:title="$t('contact.form.captchaRefresh')" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-5 w-5" | |||
fill="none" | |||
viewBox="0 0 24 24" | |||
stroke="currentColor" | |||
stroke-width="2" | |||
> | |||
<path | |||
stroke-linecap="round" | |||
stroke-linejoin="round" | |||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" | |||
/> | |||
</svg> | |||
</button> | |||
</div> | |||
<p | |||
v-if="captcha.error.value" | |||
id="captcha-error" | |||
class="mt-1.5 text-xs text-red-400 sm:text-sm" | |||
> | |||
{{ | |||
captcha.error.value === "请输入验证码" | |||
? $t("contact.validation.captchaRequired") | |||
: $t("contact.validation.captchaIncorrect") | |||
}} | |||
</p> | |||
</div> | |||
<div> | |||
<button | |||
type="submit" | |||
class="btn btn-primary w-full" | |||
<div class="pt-6"> | |||
<button | |||
type="submit" | |||
class="w-full bg-gradient-to-r from-blue-600 to-purple-600 text-white font-semibold py-3.5 px-6 rounded-lg shadow-lg hover:shadow-xl hover:from-blue-500 hover:to-purple-500 transform hover:-translate-y-0.5 transition-all duration-300 ease-in-out disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none disabled:shadow-md text-base tracking-wide focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-purple-500 focus-visible:ring-offset-gray-900" | |||
:disabled="isLoading" | |||
> | |||
<span v-if="isLoading" class="flex items-center justify-center"> | |||
<span class="animate-spin h-4 w-4 border-2 border-white rounded-full border-t-transparent mr-2"></span> | |||
提交中... | |||
<span | |||
v-if="isLoading" | |||
class="flex items-center justify-center" | |||
> | |||
<span | |||
class="animate-spin h-5 w-5 border-2 border-white rounded-full border-t-transparent mr-2.5" | |||
></span> | |||
{{ $t("contact.form.submitLoading") }} | |||
</span> | |||
<span v-else>{{ $t('contact.submit') }}</span> | |||
<span v-else>{{ $t("contact.submit") }}</span> | |||
</button> | |||
</div> | |||
<div v-if="submitSuccess" class="mt-4 p-3 bg-green-50 text-green-800 rounded-md"> | |||
消息已成功发送,我们会尽快与您联系。 | |||
<div | |||
v-if="submitSuccess" | |||
class="mt-6 p-4 bg-green-600/20 text-green-300 rounded-lg border border-green-500/50 text-sm flex items-center space-x-2" | |||
role="alert" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-5 w-5 flex-shrink-0" | |||
viewBox="0 0 20 20" | |||
fill="currentColor" | |||
> | |||
<path | |||
fill-rule="evenodd" | |||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" | |||
clip-rule="evenodd" | |||
/> | |||
</svg> | |||
<span>{{ $t("contact.form.successMessage") }}</span> | |||
</div> | |||
</form> | |||
</ErrorBoundary> | |||
</div> | |||
</div> | |||
<!-- 联系信息 --> | |||
<div class="bg-white border border-gray-200 rounded-lg overflow-hidden"> | |||
<div class="p-8"> | |||
<h2 class="text-2xl font-semibold mb-6">联系方式</h2> | |||
<div class="space-y-6"> | |||
<div class="flex items-start"> | |||
<div class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-blue-100 text-blue-600 rounded-full"> | |||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> | |||
<path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd" /> | |||
<div | |||
class="relative bg-gray-800/70 border border-gray-700 rounded-xl overflow-hidden shadow-2xl backdrop-blur-sm transition-all duration-300 ease-in-out hover:shadow-purple-500/30 hover:border-purple-500/50 group" | |||
> | |||
<div | |||
class="absolute inset-0 bg-gradient-to-r from-transparent via-purple-900/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" | |||
></div> | |||
<div class="relative p-6 sm:p-8 lg:p-10"> | |||
<div class="mb-8"> | |||
<h2 | |||
class="text-2xl font-semibold text-gray-100 sm:text-3xl inline-block" | |||
> | |||
{{ $t("contact.info.title") }} | |||
</h2> | |||
<div | |||
class="h-1 w-20 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full mt-2" | |||
></div> | |||
</div> | |||
<div class="space-y-8"> | |||
<div class="flex items-center"> | |||
<div | |||
class="flex-shrink-0 h-12 w-12 flex items-center justify-center bg-gradient-to-br from-blue-500 to-purple-600 text-white rounded-full shadow-lg" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-6 w-6" | |||
viewBox="0 0 20 20" | |||
fill="currentColor" | |||
aria-hidden="true" | |||
> | |||
<path | |||
fill-rule="evenodd" | |||
d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" | |||
clip-rule="evenodd" | |||
/> | |||
</svg> | |||
</div> | |||
<div class="ml-4"> | |||
<h3 class="text-lg font-medium text-gray-900">地址</h3> | |||
<p class="mt-1 text-gray-600"> | |||
中国上海市浦东新区张江高科技园区<br> | |||
科技大道123号 | |||
<h3 class="text-lg font-medium text-gray-100"> | |||
{{ $t("contact.info.addressLabel") }} | |||
</h3> | |||
<p class="mt-1 text-base text-gray-400"> | |||
{{ $t("contact.info.addressValue1") }}<br /> | |||
{{ $t("contact.info.addressValue2") }} | |||
</p> | |||
</div> | |||
</div> | |||
<div class="flex items-start"> | |||
<div class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-blue-100 text-blue-600 rounded-full"> | |||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> | |||
<path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" /> | |||
<div class="flex items-center"> | |||
<div | |||
class="flex-shrink-0 h-12 w-12 flex items-center justify-center bg-gradient-to-br from-blue-500 to-purple-600 text-white rounded-full shadow-lg" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-6 w-6" | |||
viewBox="0 0 20 20" | |||
fill="currentColor" | |||
aria-hidden="true" | |||
> | |||
<path | |||
d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" | |||
/> | |||
</svg> | |||
</div> | |||
<div class="ml-4"> | |||
<h3 class="text-lg font-medium text-gray-900">电话</h3> | |||
<p class="mt-1 text-gray-600">+86 123 456 7890</p> | |||
<h3 class="text-lg font-medium text-gray-100"> | |||
{{ $t("contact.info.phoneLabel") }} | |||
</h3> | |||
<p class="mt-1 text-base text-gray-400">+86 123 456 7890</p> | |||
</div> | |||
</div> | |||
<div class="flex items-start"> | |||
<div class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-blue-100 text-blue-600 rounded-full"> | |||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> | |||
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" /> | |||
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" /> | |||
<div class="flex items-center"> | |||
<div | |||
class="flex-shrink-0 h-12 w-12 flex items-center justify-center bg-gradient-to-br from-blue-500 to-purple-600 text-white rounded-full shadow-lg" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-6 w-6" | |||
viewBox="0 0 20 20" | |||
fill="currentColor" | |||
aria-hidden="true" | |||
> | |||
<path | |||
d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" | |||
/> | |||
<path | |||
d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" | |||
/> | |||
</svg> | |||
</div> | |||
<div class="ml-4"> | |||
<h3 class="text-lg font-medium text-gray-900">邮箱</h3> | |||
<p class="mt-1 text-gray-600">contact@example.com</p> | |||
<h3 class="text-lg font-medium text-gray-100"> | |||
{{ $t("contact.info.emailLabel") }} | |||
</h3> | |||
<p class="mt-1 text-base text-gray-400"> | |||
contact@example.com | |||
</p> | |||
</div> | |||
</div> | |||
<div class="flex items-start"> | |||
<div class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-blue-100 text-blue-600 rounded-full"> | |||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> | |||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd" /> | |||
<div class="flex items-center"> | |||
<div | |||
class="flex-shrink-0 h-12 w-12 flex items-center justify-center bg-gradient-to-br from-blue-500 to-purple-600 text-white rounded-full shadow-lg" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-6 w-6" | |||
viewBox="0 0 20 20" | |||
fill="currentColor" | |||
aria-hidden="true" | |||
> | |||
<path | |||
fill-rule="evenodd" | |||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" | |||
clip-rule="evenodd" | |||
/> | |||
</svg> | |||
</div> | |||
<div class="ml-4"> | |||
<h3 class="text-lg font-medium text-gray-900">工作时间</h3> | |||
<p class="mt-1 text-gray-600"> | |||
周一至周五: 9:00 - 18:00<br> | |||
周六、周日: 休息 | |||
<h3 class="text-lg font-medium text-gray-100"> | |||
{{ $t("contact.info.hoursLabel") }} | |||
</h3> | |||
<p class="mt-1 text-base text-gray-400"> | |||
{{ $t("contact.info.hoursValue1") }}<br /> | |||
{{ $t("contact.info.hoursValue2") }} | |||
</p> | |||
</div> | |||
</div> | |||
@@ -144,24 +398,27 @@ | |||
* 联系我们页面 | |||
* 提供联系表单和联系信息 | |||
*/ | |||
import { ref, reactive } from 'vue'; | |||
import { useErrorHandler } from '~/composables/useErrorHandler'; | |||
import { useErrorHandler } from "~/composables/useErrorHandler"; | |||
import { useCaptcha } from "~/composables/useCaptcha"; | |||
import { useI18n } from "vue-i18n"; | |||
const { error, isLoading, wrapAsync } = useErrorHandler(); | |||
const captcha = useCaptcha(); | |||
const submitSuccess = ref(false); | |||
const { t } = useI18n(); | |||
// 表单数据 | |||
const formData = reactive({ | |||
name: '', | |||
email: '', | |||
message: '' | |||
name: "", | |||
email: "", | |||
message: "", | |||
}); | |||
// 表单错误 | |||
const formErrors = reactive({ | |||
name: '', | |||
email: '', | |||
message: '' | |||
name: "", | |||
email: "", | |||
message: "", | |||
}); | |||
/** | |||
@@ -170,33 +427,33 @@ const formErrors = reactive({ | |||
*/ | |||
function validateForm(): boolean { | |||
let isValid = true; | |||
// 重置错误 | |||
formErrors.name = ''; | |||
formErrors.email = ''; | |||
formErrors.message = ''; | |||
formErrors.name = ""; | |||
formErrors.email = ""; | |||
formErrors.message = ""; | |||
// 验证姓名 | |||
if (!formData.name.trim()) { | |||
formErrors.name = '请输入您的姓名'; | |||
formErrors.name = t("contact.validation.nameRequired"); | |||
isValid = false; | |||
} | |||
// 验证邮箱 | |||
if (!formData.email.trim()) { | |||
formErrors.email = '请输入您的邮箱'; | |||
formErrors.email = t("contact.validation.emailRequired"); | |||
isValid = false; | |||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { | |||
formErrors.email = '请输入有效的邮箱地址'; | |||
formErrors.email = t("contact.validation.emailInvalid"); | |||
isValid = false; | |||
} | |||
// 验证消息 | |||
if (!formData.message.trim()) { | |||
formErrors.message = '请输入您的消息'; | |||
formErrors.message = t("contact.validation.messageRequired"); | |||
isValid = false; | |||
} | |||
return isValid; | |||
} | |||
@@ -206,34 +463,45 @@ function validateForm(): boolean { | |||
async function submitForm() { | |||
// 重置成功状态 | |||
submitSuccess.value = false; | |||
// 验证表单 | |||
// 验证表单(姓名、邮箱、消息) | |||
if (!validateForm()) { | |||
return; | |||
} | |||
// 验证验证码 | |||
if (!captcha.validateCaptcha()) { | |||
return; | |||
} | |||
// 提交表单数据 | |||
await wrapAsync(async () => { | |||
// 模拟API请求 | |||
await new Promise(resolve => setTimeout(resolve, 1000)); | |||
console.log("Form Data:", formData); | |||
console.log("Captcha Validated!"); | |||
await new Promise((resolve) => setTimeout(resolve, 1500)); | |||
// 模拟成功响应 | |||
submitSuccess.value = true; | |||
// 清空表单 | |||
formData.name = ''; | |||
formData.email = ''; | |||
formData.message = ''; | |||
// 清空表单和验证码 | |||
formData.name = ""; | |||
formData.email = ""; | |||
formData.message = ""; | |||
captcha.generateCaptcha(); // 成功后也刷新验证码 | |||
return true; | |||
}); | |||
} | |||
// SEO优化 | |||
useHead({ | |||
title: '联系我们 - Hanye', | |||
title: t("contact.meta.title"), | |||
meta: [ | |||
{ name: 'description', content: '联系我们获取更多信息或咨询服务。我们期待收到您的留言。' } | |||
] | |||
{ | |||
name: "description", | |||
content: t("contact.meta.description"), | |||
}, | |||
], | |||
}); | |||
</script> | |||
</script> |
@@ -0,0 +1,220 @@ | |||
<template> | |||
<div class="w-full h-[55px] sm:h-[72px]"></div> | |||
<div | |||
class="max-w-full px-4 py-16 md:px-8 lg:px-10 bg-gradient-to-br from-gray-900 via-gray-900 to-black text-gray-300 min-h-screen" | |||
> | |||
<div class="max-w-screen-xl mx-auto"> | |||
<h1 class="text-4xl md:text-6xl mb-12 text-center font-normal text-white"> | |||
{{ $t("faq.title") }} | |||
</h1> | |||
<!-- Search Bar --> | |||
<div class="mb-12 max-w-xl mx-auto"> | |||
<div class="relative"> | |||
<input | |||
type="search" | |||
v-model="searchTerm" | |||
:placeholder="$t('faq.searchPlaceholder')" | |||
class="block w-full appearance-none rounded-lg border border-gray-600 bg-gray-700/50 px-4 py-3 pl-10 pr-4 text-base text-gray-100 placeholder-gray-400 shadow-inner transition duration-200 ease-in-out focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/50 focus:ring-offset-2 focus:ring-offset-gray-900" | |||
/> | |||
<div | |||
class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="h-5 w-5 text-gray-400" | |||
viewBox="0 0 20 20" | |||
fill="currentColor" | |||
> | |||
<path | |||
fill-rule="evenodd" | |||
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" | |||
clip-rule="evenodd" | |||
/> | |||
</svg> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- FAQ Accordion --> | |||
<div class="space-y-5"> | |||
<div | |||
v-for="faq in filteredFaqs" | |||
:key="faq.id" | |||
class="relative border rounded-xl overflow-hidden shadow-lg backdrop-blur-sm transition-all duration-300 ease-in-out group" | |||
:class="[ | |||
openAccordionIds.has(faq.id) | |||
? 'bg-gray-750/70 border-blue-500/60 shadow-blue-500/20' | |||
: 'bg-gray-800/60 border-gray-700 hover:border-blue-500/40 hover:bg-gray-750/70', | |||
]" | |||
> | |||
<button | |||
@click="toggleAccordion(faq.id)" | |||
class="flex w-full items-center justify-between px-6 py-5 text-left text-lg font-medium text-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-gray-800 transition-colors duration-200" | |||
:class="{ 'bg-gray-700/50': openAccordionIds.has(faq.id) }" | |||
:aria-expanded="openAccordionIds.has(faq.id)" | |||
:aria-controls="`faq-answer-${faq.id}`" | |||
> | |||
<span class="pr-4">{{ faq.question }}</span> | |||
<!-- Plus/Minus Icon --> | |||
<div | |||
class="relative h-6 w-6 flex-shrink-0 text-blue-400 group-hover:text-blue-300" | |||
> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="absolute h-6 w-6 transition-opacity duration-300 ease-in-out" | |||
:class=" | |||
openAccordionIds.has(faq.id) ? 'opacity-0' : 'opacity-100' | |||
" | |||
fill="none" | |||
viewBox="0 0 24 24" | |||
stroke="currentColor" | |||
stroke-width="2" | |||
> | |||
<path | |||
stroke-linecap="round" | |||
stroke-linejoin="round" | |||
d="M12 4v16m8-8H4" | |||
/> | |||
</svg> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
class="absolute h-6 w-6 transition-opacity duration-300 ease-in-out" | |||
:class=" | |||
openAccordionIds.has(faq.id) ? 'opacity-100' : 'opacity-0' | |||
" | |||
fill="none" | |||
viewBox="0 0 24 24" | |||
stroke="currentColor" | |||
stroke-width="2" | |||
> | |||
<path | |||
stroke-linecap="round" | |||
stroke-linejoin="round" | |||
d="M20 12H4" | |||
/> | |||
</svg> | |||
</div> | |||
</button> | |||
<transition | |||
enter-active-class="transition-[grid-template-rows,opacity] ease-in-out duration-300" | |||
enter-from-class="grid-template-rows-[0fr] opacity-0" | |||
enter-to-class="grid-template-rows-[1fr] opacity-100" | |||
leave-active-class="transition-[grid-template-rows,opacity] ease-in-out duration-300" | |||
leave-from-class="grid-template-rows-[1fr] opacity-100" | |||
leave-to-class="grid-template-rows-[0fr] opacity-0" | |||
> | |||
<div | |||
v-show="openAccordionIds.has(faq.id)" | |||
:id="`faq-answer-${faq.id}`" | |||
class="grid overflow-hidden" | |||
role="region" | |||
> | |||
<div class="overflow-hidden"> | |||
<div | |||
class="px-6 pt-4 pb-8 text-base text-gray-300 leading-relaxed" | |||
> | |||
<div | |||
class="prose prose-invert max-w-none prose-p:text-gray-300 prose-a:text-blue-400 hover:prose-a:text-blue-300" | |||
> | |||
<p v-html="faq.answer"></p> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</transition> | |||
</div> | |||
<div | |||
v-if="filteredFaqs.length === 0" | |||
class="text-center text-gray-400 py-8" | |||
> | |||
{{ $t("faq.noResults") }} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup lang="ts"> | |||
import { ref, computed } from "vue"; | |||
import { useI18n } from "vue-i18n"; | |||
const { t } = useI18n(); | |||
const searchTerm = ref(""); | |||
const openAccordionIds = ref<Set<string>>(new Set()); | |||
// Define FAQ item type with actual strings | |||
interface FaqItem { | |||
id: string; | |||
question: string; | |||
answer: string; | |||
} | |||
// Placeholder FAQ data with actual strings (replace with your real data) | |||
const faqs = ref<FaqItem[]>([ | |||
{ | |||
id: "faq-1", | |||
question: "如何购买 Hanye 产品?", // Example Question 1 | |||
answer: | |||
"您可以通过我们的官方在线商店或授权的零售商处购买 Hanye 产品。我们建议您在官方渠道购买以确保正品和售后服务。", // Example Answer 1 | |||
}, | |||
{ | |||
id: "faq-2", | |||
question: "产品保修期是多久?", | |||
answer: | |||
"不同产品的保修期可能不同,请参考具体产品的说明页面或联系我们的客服获取详细信息。通常固态硬盘提供3-5年保修,内存条提供终身保固。", | |||
}, | |||
{ | |||
id: "faq-3", | |||
question: "如何申请售后服务?", | |||
answer: | |||
"如果您需要售后服务,请准备好您的购买凭证,并通过我们的官方网站提交售后申请或直接联系客服中心。", | |||
}, | |||
{ | |||
id: "faq-4", | |||
question: "Hanye SSD 是否兼容我的电脑?", | |||
answer: | |||
"Hanye SSD 兼容大多数台式机和笔记本电脑。请确认您的设备支持相应的接口(如 SATA 或 NVMe)和规格。具体兼容性列表请参考产品页面。", | |||
}, | |||
{ | |||
id: "faq-5", | |||
question: "忘记密码怎么办?", | |||
answer: | |||
'如果是指 Hanye 相关的在线服务账户密码,请使用"忘记密码"功能进行重置。如果是加密 U 盘或 SSD 的密码,很抱歉,为了数据安全,我们无法提供密码破解服务。', | |||
}, | |||
]); | |||
// Filter FAQs based on actual string content | |||
const filteredFaqs = computed(() => { | |||
if (!searchTerm.value) { | |||
return faqs.value; | |||
} | |||
const lowerSearchTerm = searchTerm.value.toLowerCase(); | |||
return faqs.value.filter( | |||
(faq: FaqItem) => | |||
faq.question.toLowerCase().includes(lowerSearchTerm) || | |||
faq.answer.toLowerCase().includes(lowerSearchTerm) | |||
); | |||
}); | |||
// Toggle accordion item (modified for Set) | |||
const toggleAccordion = (id: string) => { | |||
if (openAccordionIds.value.has(id)) { | |||
openAccordionIds.value.delete(id); | |||
} else { | |||
openAccordionIds.value.add(id); | |||
} | |||
}; | |||
// SEO (Still uses i18n) | |||
useHead({ | |||
title: t("faq.meta.title"), | |||
meta: [ | |||
{ | |||
name: "description", | |||
content: t("faq.meta.description"), | |||
}, | |||
], | |||
}); | |||
</script> |
@@ -53,16 +53,16 @@ | |||
</Swiper> | |||
</section> | |||
<!-- 分类产品展示 --> | |||
<section class="max-w-full mb-12 md:mb-32"> | |||
<!-- 按用途产品展示 --> | |||
<section class="max-w-full mb-12 md:mb-32 xl:px-2 lg:px-2 md:px-4 px-4"> | |||
<div class="max-w-screen-2xl mx-auto relative"> | |||
<div | |||
class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4 px-4 md:px-0" | |||
class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4" | |||
> | |||
{{ $t("products.usage") }} | |||
</div> | |||
<div | |||
class="justify-center text-white font-normal mb-8 px-4 md:px-0 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl" | |||
class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl" | |||
> | |||
{{ $t("products.usage_title") }} | |||
</div> | |||
@@ -70,7 +70,7 @@ | |||
<div | |||
class="max-w-screen-2xl mx-auto relative overflow-x-auto whitespace-nowrap scrollbar-hide scroll-smooth" | |||
> | |||
<div class="w-full mb-8 inline-flex items-center gap-4 px-4 md:px-0"> | |||
<div class="w-full mb-8 inline-flex items-center gap-4"> | |||
<div | |||
class="cursor-pointer select-none px-7 py-3 rounded-full outline outline-1 outline-offset-[-1px] outline-cyan-400 inline-flex justify-center items-center gap-2.5" | |||
> | |||
@@ -116,26 +116,27 @@ | |||
</div> | |||
</div> | |||
</div> | |||
<div | |||
class="w-full relative pl-[calc((100vw-1536px)/2)] 2xl:pl-[calc((100vw-1536px)/2)] lg:pl-[0] md:pl-[0] sm:pl-[0] max-w-full" | |||
> | |||
<div class="max-w-screen-2xl mx-auto"> | |||
<div class="w-full"> | |||
<Swiper | |||
:modules="[Navigation]" | |||
:spaceBetween="30" | |||
slidesPerView="auto" | |||
:grid="{ | |||
fill: 'column', | |||
rows: 1, | |||
:slidesPerView="4" | |||
:breakpoints="{ | |||
320: { slidesPerView: 2, spaceBetween: 10 }, | |||
640: { slidesPerView: 3, spaceBetween: 20 }, | |||
1024: { slidesPerView: 4, spaceBetween: 20 }, | |||
1280: { slidesPerView: 4, spaceBetween: 30 }, | |||
1536: { slidesPerView: 4, spaceBetween: 30 }, | |||
}" | |||
:navigation="{ | |||
prevEl: '.swiper-button-prev-2', | |||
nextEl: '.swiper-button-next-2', | |||
}" | |||
class="h-32 sm:h-32 md:h-64 lg:h-96 max-w-full" | |||
class="max-w-full" | |||
> | |||
<SwiperSlide | |||
class="!w-32 sm:!w-32 md:!w-64 lg:!w-96 !h-32 sm:!h-32 md:!h-64 lg:!h-96" | |||
class="w-32 sm:w-32 md:w-64 lg:w-96 h-32 sm:h-32 md:h-64 lg:h-96" | |||
> | |||
<div | |||
class="w-full h-full [background:#181818] rounded-2xl px-2 md:px-10 flex flex-col items-center justify-center" | |||
@@ -146,19 +147,19 @@ | |||
class="w-full h-full object-contain" | |||
/> | |||
<div | |||
class="text-center text-white text-base font-bold leading-tight mb-4 hidden md:block" | |||
class="text-center text-white text-xs sm:text-sm md:text-base font-bold leading-tight mb-2 md:mb-4" | |||
> | |||
Hanye Q60-2TST3 | |||
</div> | |||
<div | |||
class="text-center justify-center text-white text-xs font-normal mb-8 leading-tight hidden md:block opacity-80" | |||
class="text-center justify-center text-white text-[10px] sm:text-xs font-normal mb-4 md:mb-8 leading-tight opacity-80" | |||
> | |||
2TB SSD UP TO 550MB/s | |||
</div> | |||
</div> | |||
</SwiperSlide> | |||
<SwiperSlide | |||
class="!w-32 sm:!w-32 md:!w-64 lg:!w-96 !h-32 sm:!h-32 md:!h-64 lg:!h-96" | |||
class="w-32 sm:w-32 md:w-64 lg:w-96 h-32 sm:h-32 md:h-64 lg:h-96" | |||
> | |||
<div | |||
class="w-full h-full [background:#181818] rounded-2xl px-2 md:px-10 flex flex-col items-center justify-center" | |||
@@ -169,19 +170,19 @@ | |||
class="w-full h-full object-contain" | |||
/> | |||
<div | |||
class="text-center text-white text-base font-bold leading-tight mb-4 hidden md:block" | |||
class="text-center text-white text-xs sm:text-sm md:text-base font-bold leading-tight mb-2 md:mb-4" | |||
> | |||
Hanye Q60-2TST3 | |||
</div> | |||
<div | |||
class="text-center justify-center text-white text-xs font-normal mb-8 leading-tight hidden md:block opacity-80" | |||
class="text-center justify-center text-white text-[10px] sm:text-xs font-normal mb-4 md:mb-8 leading-tight opacity-80" | |||
> | |||
2TB SSD UP TO 550MB/s | |||
</div> | |||
</div> | |||
</SwiperSlide> | |||
<SwiperSlide | |||
class="!w-32 sm:!w-32 md:!w-64 lg:!w-96 !h-32 sm:!h-32 md:!h-64 lg:!h-96" | |||
class="w-32 sm:w-32 md:w-64 lg:w-96 h-32 sm:h-32 md:h-64 lg:h-96" | |||
> | |||
<div | |||
class="w-full h-full [background:#181818] rounded-2xl px-2 md:px-10 flex flex-col items-center justify-center" | |||
@@ -192,19 +193,19 @@ | |||
class="w-full h-full object-contain" | |||
/> | |||
<div | |||
class="text-center text-white text-base font-bold leading-tight mb-4 hidden md:block" | |||
class="text-center text-white text-xs sm:text-sm md:text-base font-bold leading-tight mb-2 md:mb-4" | |||
> | |||
Hanye Q60-2TST3 | |||
</div> | |||
<div | |||
class="text-center justify-center text-white text-xs font-normal mb-8 leading-tight hidden md:block opacity-80" | |||
class="text-center justify-center text-white text-[10px] sm:text-xs font-normal mb-4 md:mb-8 leading-tight opacity-80" | |||
> | |||
2TB SSD UP TO 550MB/s | |||
</div> | |||
</div> | |||
</SwiperSlide> | |||
<SwiperSlide | |||
class="!w-32 sm:!w-32 md:!w-64 lg:!w-96 !h-32 sm:!h-32 md:!h-64 lg:!h-96" | |||
class="w-32 sm:w-32 md:w-64 lg:w-96 h-32 sm:h-32 md:h-64 lg:h-96" | |||
> | |||
<div | |||
class="w-full h-full [background:#181818] rounded-2xl px-2 md:px-10 flex flex-col items-center justify-center" | |||
@@ -215,19 +216,19 @@ | |||
class="w-full h-full object-contain" | |||
/> | |||
<div | |||
class="text-center text-white text-base font-bold leading-tight mb-4 hidden md:block" | |||
class="text-center text-white text-xs sm:text-sm md:text-base font-bold leading-tight mb-2 md:mb-4" | |||
> | |||
Hanye Q60-2TST3 | |||
</div> | |||
<div | |||
class="text-center justify-center text-white text-xs font-normal mb-8 leading-tight hidden md:block opacity-80" | |||
class="text-center justify-center text-white text-[10px] sm:text-xs font-normal mb-4 md:mb-8 leading-tight opacity-80" | |||
> | |||
2TB SSD UP TO 550MB/s | |||
</div> | |||
</div> | |||
</SwiperSlide> | |||
<SwiperSlide | |||
class="!w-32 sm:!w-32 md:!w-64 lg:!w-96 !h-32 sm:!h-32 md:!h-64 lg:!h-96" | |||
class="w-32 sm:w-32 md:w-64 lg:w-96 h-32 sm:h-32 md:h-64 lg:h-96" | |||
> | |||
<div | |||
class="w-full h-full [background:#181818] rounded-2xl px-2 md:px-10 flex flex-col items-center justify-center" | |||
@@ -238,12 +239,12 @@ | |||
class="w-full h-full object-contain" | |||
/> | |||
<div | |||
class="text-center text-white text-base font-bold leading-tight mb-4 hidden md:block" | |||
class="text-center text-white text-xs sm:text-sm md:text-base font-bold leading-tight mb-2 md:mb-4" | |||
> | |||
Hanye Q60-2TST3 | |||
</div> | |||
<div | |||
class="text-center justify-center text-white text-xs font-normal mb-8 leading-tight hidden md:block opacity-80" | |||
class="text-center justify-center text-white text-[10px] sm:text-xs font-normal mb-4 md:mb-8 leading-tight opacity-80" | |||
> | |||
2TB SSD UP TO 550MB/s | |||
</div> | |||
@@ -254,20 +255,258 @@ | |||
</div> | |||
</section> | |||
<!-- 按用途产品展示 --> | |||
<!-- 按分类产品展示 --> | |||
<section class="max-w-full mb-12 md:mb-32 xl:px-2 lg:px-2 md:px-4 px-4"> | |||
<div class="max-w-screen-2xl mx-auto relative"> | |||
<div | |||
class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4" | |||
> | |||
Hanye独自に開発・製造、販売 | |||
</div> | |||
<div | |||
class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl" | |||
> | |||
製品カテゴリー | |||
</div> | |||
</div> | |||
<div class="max-w-screen-2xl mx-auto"> | |||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | |||
<nuxt-link | |||
to="/products" | |||
class="bg-zinc-950/10 backdrop-blur-[50px] border border-white/10 rounded-lg flex gap-8 p-4 sm:p-8 justify-between category-item" | |||
> | |||
<div class="col-span-1 flex flex-col gap-4"> | |||
<div class="flex flex-col gap-2 opacity-80"> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>PC高速化</span> | |||
</div> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>起動・読込 高速</span> | |||
</div> | |||
</div> | |||
<div | |||
class="p-2 sm:p-4 mt-auto bg-zinc-500/20 rounded-lg outline outline-1 outline-offset-[-1px] outline-white/10 backdrop-blur-xl inline-flex justify-center items-center gap-3 overflow-hidden" | |||
> | |||
<div | |||
class="justify-start text-neutral-200 text-xs md:text-sm font-medium uppercase leading-relaxed" | |||
> | |||
2.5-inch SSD & M.2 SSD | |||
</div> | |||
</div> | |||
</div> | |||
<div class="w-32 h-32 md:w-44 md:h-44"> | |||
<img | |||
:src="product" | |||
alt="h1" | |||
class="w-full h-full object-contain" | |||
/> | |||
</div> | |||
</nuxt-link> | |||
<nuxt-link | |||
to="/products" | |||
class="bg-zinc-950/10 backdrop-blur-[50px] border border-white/10 rounded-lg flex gap-8 p-4 sm:p-8 justify-between category-item" | |||
> | |||
<div class="col-span-1 flex flex-col gap-4"> | |||
<div class="flex flex-col gap-2 opacity-80"> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>PC高速化</span> | |||
</div> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>起動・読込 高速</span> | |||
</div> | |||
</div> | |||
<div | |||
class="p-2 sm:p-4 mt-auto bg-zinc-500/20 rounded-lg outline outline-1 outline-offset-[-1px] outline-white/10 backdrop-blur-xl inline-flex justify-center items-center gap-3 overflow-hidden" | |||
> | |||
<div | |||
class="justify-start text-neutral-200 text-xs md:text-sm font-medium uppercase leading-relaxed" | |||
> | |||
2.5-inch SSD & M.2 SSD | |||
</div> | |||
</div> | |||
</div> | |||
<div class="w-32 h-32 md:w-44 md:h-44"> | |||
<img | |||
:src="product" | |||
alt="h1" | |||
class="w-full h-full object-contain" | |||
/> | |||
</div> | |||
</nuxt-link> | |||
<nuxt-link | |||
to="/products" | |||
class="bg-zinc-950/10 backdrop-blur-[50px] border border-white/10 rounded-lg flex gap-8 p-4 sm:p-8 justify-between category-item" | |||
> | |||
<div class="col-span-1 flex flex-col gap-4"> | |||
<div class="flex flex-col gap-2 opacity-80"> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>PC高速化</span> | |||
</div> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>起動・読込 高速</span> | |||
</div> | |||
</div> | |||
<div | |||
class="p-2 sm:p-4 mt-auto bg-zinc-500/20 rounded-lg outline outline-1 outline-offset-[-1px] outline-white/10 backdrop-blur-xl inline-flex justify-center items-center gap-3 overflow-hidden" | |||
> | |||
<div | |||
class="justify-start text-neutral-200 text-xs md:text-sm font-medium uppercase leading-relaxed" | |||
> | |||
2.5-inch SSD & M.2 SSD | |||
</div> | |||
</div> | |||
</div> | |||
<div class="w-32 h-32 md:w-44 md:h-44"> | |||
<img | |||
:src="product" | |||
alt="h1" | |||
class="w-full h-full object-contain" | |||
/> | |||
</div> | |||
</nuxt-link> | |||
<nuxt-link | |||
to="/products" | |||
class="bg-zinc-950/10 backdrop-blur-[50px] border border-white/10 rounded-lg flex gap-8 p-4 sm:p-8 justify-between category-item" | |||
> | |||
<div class="col-span-1 flex flex-col gap-4"> | |||
<div class="flex flex-col gap-2 opacity-80"> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>PC高速化</span> | |||
</div> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>起動・読込 高速</span> | |||
</div> | |||
</div> | |||
<div | |||
class="p-2 sm:p-4 mt-auto bg-zinc-500/20 rounded-lg outline outline-1 outline-offset-[-1px] outline-white/10 backdrop-blur-xl inline-flex justify-center items-center gap-3 overflow-hidden" | |||
> | |||
<div | |||
class="justify-start text-neutral-200 text-xs md:text-sm font-medium uppercase leading-relaxed" | |||
> | |||
2.5-inch SSD & M.2 SSD | |||
</div> | |||
</div> | |||
</div> | |||
<div class="w-32 h-32 md:w-44 md:h-44"> | |||
<img | |||
:src="product" | |||
alt="h1" | |||
class="w-full h-full object-contain" | |||
/> | |||
</div> | |||
</nuxt-link> | |||
<nuxt-link | |||
to="/products" | |||
class="bg-zinc-950/10 backdrop-blur-[50px] border border-white/10 rounded-lg flex gap-8 p-4 sm:p-8 justify-between category-item" | |||
> | |||
<div class="col-span-1 flex flex-col gap-4"> | |||
<div class="flex flex-col gap-2 opacity-80"> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>PC高速化</span> | |||
</div> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>起動・読込 高速</span> | |||
</div> | |||
</div> | |||
<div | |||
class="p-2 sm:p-4 mt-auto bg-zinc-500/20 rounded-lg outline outline-1 outline-offset-[-1px] outline-white/10 backdrop-blur-xl inline-flex justify-center items-center gap-3 overflow-hidden" | |||
> | |||
<div | |||
class="justify-start text-neutral-200 text-xs md:text-sm font-medium uppercase leading-relaxed" | |||
> | |||
2.5-inch SSD & M.2 SSD | |||
</div> | |||
</div> | |||
</div> | |||
<div class="w-32 h-32 md:w-44 md:h-44"> | |||
<img | |||
:src="product" | |||
alt="h1" | |||
class="w-full h-full object-contain" | |||
/> | |||
</div> | |||
</nuxt-link> | |||
<nuxt-link | |||
to="/products" | |||
class="bg-zinc-950/10 backdrop-blur-[50px] border border-white/10 rounded-lg flex gap-8 p-4 sm:p-8 justify-between category-item" | |||
> | |||
<div class="col-span-1 flex flex-col gap-4"> | |||
<div class="flex flex-col gap-2 opacity-80"> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>PC高速化</span> | |||
</div> | |||
<div | |||
class="text-white text-sm md:text-base font-normal leading-tight flex gap-2 items-center" | |||
> | |||
<i class="icon-star text-sm"></i> | |||
<span>起動・読込 高速</span> | |||
</div> | |||
</div> | |||
<div | |||
class="p-2 sm:p-4 mt-auto bg-zinc-500/20 rounded-lg outline outline-1 outline-offset-[-1px] outline-white/10 backdrop-blur-xl inline-flex justify-center items-center gap-3 overflow-hidden" | |||
> | |||
<div | |||
class="justify-start text-neutral-200 text-xs md:text-sm font-medium uppercase leading-relaxed" | |||
> | |||
2.5-inch SSD & M.2 SSD | |||
</div> | |||
</div> | |||
</div> | |||
<div class="w-32 h-32 md:w-44 md:h-44"> | |||
<img | |||
:src="product" | |||
alt="h1" | |||
class="w-full h-full object-contain" | |||
/> | |||
</div> | |||
</nuxt-link> | |||
</div> | |||
</div> | |||
</section> | |||
<!-- Tag description --> | |||
<section class="max-w-full mb-12 md:mb-32"> | |||
<!-- 产品核心展示 --> | |||
<section class="max-w-full mb-12 md:mb-32 xl:px-2 lg:px-2 md:px-4 px-4"> | |||
<div | |||
class="max-w-screen-2xl mx-auto grid grid-cols-1 justify-items-center gap-4 sm:grid-cols-2 md:grid-cols-3" | |||
class="max-w-screen-2xl mx-auto grid grid-cols-1 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 gap-4" | |||
> | |||
<div class="inline-flex justify-start items-center gap-5 px-4"> | |||
<div class="inline-flex justify-start items-center gap-5"> | |||
<div | |||
class="w-16 h-16 relative bg-gradient-to-b from-neutral-600 to-slate-400 rounded-xl border-b-[1.50px] border-neutral-700 overflow-hidden" | |||
class="w-12 h-12 md:w-16 md:h-16 relative bg-gradient-to-b from-neutral-600 to-slate-400 rounded-xl border-b-[1.50px] border-neutral-700 overflow-hidden" | |||
> | |||
<div | |||
class="w-12 h-12 left-[9.50px] top-[9.50px] absolute overflow-hidden flex items-center justify-center" | |||
> | |||
<div class="w-full h-full flex items-center justify-center"> | |||
<i class="icon-h1 text-white text-5xl"></i> | |||
</div> | |||
</div> | |||
@@ -284,13 +523,11 @@ | |||
</div> | |||
</div> | |||
</div> | |||
<div class="inline-flex justify-start items-center gap-5 px-4"> | |||
<div class="inline-flex justify-start items-center gap-5"> | |||
<div | |||
class="w-16 h-16 relative bg-gradient-to-b from-neutral-600 to-slate-400 rounded-xl border-b-[1.50px] border-neutral-700 overflow-hidden" | |||
class="w-12 h-12 md:w-16 md:h-16 relative bg-gradient-to-b from-neutral-600 to-slate-400 rounded-xl border-b-[1.50px] border-neutral-700 overflow-hidden" | |||
> | |||
<div | |||
class="w-12 h-12 left-[9.50px] top-[9.50px] absolute overflow-hidden flex items-center justify-center" | |||
> | |||
<div class="w-full h-full flex items-center justify-center"> | |||
<i class="icon-h2 text-white text-5xl"></i> | |||
</div> | |||
</div> | |||
@@ -307,13 +544,11 @@ | |||
</div> | |||
</div> | |||
</div> | |||
<div class="inline-flex justify-start items-center gap-5 px-4"> | |||
<div class="inline-flex justify-start items-center gap-5"> | |||
<div | |||
class="w-16 h-16 relative bg-gradient-to-b from-neutral-600 to-slate-400 rounded-xl border-b-[1.50px] border-neutral-700 overflow-hidden" | |||
class="w-12 h-12 md:w-16 md:h-16 relative bg-gradient-to-b from-neutral-600 to-slate-400 rounded-xl border-b-[1.50px] border-neutral-700 overflow-hidden" | |||
> | |||
<div | |||
class="w-12 h-12 left-[9.50px] top-[9.50px] absolute overflow-hidden flex items-center justify-center" | |||
> | |||
<div class="w-full h-full flex items-center justify-center"> | |||
<i class="icon-h3 text-white text-5xl"></i> | |||
</div> | |||
</div> | |||
@@ -334,15 +569,15 @@ | |||
</section> | |||
<!-- 当社の強み --> | |||
<section class="max-w-full mb-0 md:mb-28"> | |||
<section class="max-w-full mb-12 md:mb-32 xl:px-2 lg:px-2 md:px-4 px-4"> | |||
<div class="max-w-screen-2xl mx-auto relative"> | |||
<div | |||
class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4 px-4 md:px-0" | |||
class="justify-center text-cyan-400 text-base font-normal leading-tight mb-4" | |||
> | |||
{{ $t("products.strong_point") }} | |||
</div> | |||
<div | |||
class="justify-center text-white font-normal mb-8 px-4 md:px-0 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl" | |||
class="justify-center text-white font-normal mb-8 md:mb-16 text-xl sm:text-2xl md:text-4xl lg:text-6xl" | |||
> | |||
{{ $t("products.strong_point_title") }} | |||
</div> | |||
@@ -363,10 +598,8 @@ | |||
</div> | |||
</div> | |||
</div> | |||
<div class="overflow-hidden w-full"> | |||
<div | |||
class="w-full relative pl-[calc((100vw-1536px)/2)] 2xl:pl-[calc((100vw-1536px)/2)] lg:pl-[0] md:pl-[0] sm:pl-[0] max-w-full" | |||
> | |||
<div class="max-w-screen-2xl mx-auto"> | |||
<div class="w-full relative max-w-full"> | |||
<Swiper | |||
:modules="[Navigation, Pagination]" | |||
slides-per-view="auto" | |||
@@ -380,7 +613,7 @@ | |||
> | |||
<SwiperSlide class="!max-w-screen-2xl !w-full"> | |||
<div | |||
class="w-full h-full flex items-center px-4 md:px-20" | |||
class="w-full h-full flex items-center px-0 md:px-20" | |||
:style="{ | |||
backgroundImage: `url(${homeC1Webp})`, | |||
backgroundSize: 'cover', | |||
@@ -389,7 +622,7 @@ | |||
}" | |||
> | |||
<div | |||
class="w-[100%] md:w-[50%] bg-white/5 rounded-2xl backdrop-blur-[50px] px-8 py-12 flex flex-col gap-8 border border-white/10" | |||
class="w-full lg:w-[50%] h-full md:h-auto bg-white/5 rounded-0 md:rounded-2xl backdrop-blur-[50px] px-4 py-8 md:py-12 md:px-8 flex flex-col gap-8 border border-white/10" | |||
> | |||
<div | |||
class="opacity-90 justify-start text-white text-2xl font-normal md:text-4xl" | |||
@@ -397,7 +630,7 @@ | |||
一貫体制による高品質と安定供給 | |||
</div> | |||
<div | |||
class="opacity-70 justify-start text-white text-lg font-normal leading-relaxed hidden sm:block" | |||
class="opacity-70 justify-start text-white text-base md:text-lg font-normal leading-relaxed" | |||
> | |||
「企画・開発から製造、品質管理、販売、オンラインショップ運営まで自社で完結。ISO認証取得の工場で生産された信頼性の高い製品を、安定してお届けします。」 | |||
</div> | |||
@@ -413,28 +646,39 @@ | |||
</section> | |||
<!-- 产品咨询 --> | |||
<section | |||
class="max-w-full h-[240px] md:h-[480px] bg-black/80 hidden md:block" | |||
> | |||
<section class="max-w-full h-[240px] md:h-[480px] bg-black/80 md:block"> | |||
<div class="h-full relative"> | |||
<h1 | |||
class="text-center justify-start text-white font-normal absolute top-1/3 left-4 right-4 -translate-x-0 md:left-1/2 md:-translate-x-1/2 md:right-auto text-xl sm:text-2xl md:text-3xl" | |||
> | |||
{{ $t("products.consultation") }} | |||
</h1> | |||
<div | |||
class="w-40 h-11 bg-zinc-300/10 rounded-lg outline outline-1 flex items-center justify-center gap-2 outline-white/20 backdrop-blur-[10px] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/5" | |||
class="absolute top-0 left-0 w-full h-full flex flex-col gap-6 items-center justify-center z-10" | |||
> | |||
<span class="text-white text-sm font-normal">{{ | |||
$t("products.consultation_button") | |||
}}</span> | |||
<i class="icon-arrow-right text-white text-sm font-normal"></i> | |||
<h1 | |||
class="text-center justify-start text-white font-normal text-xl sm:text-2xl md:text-3xl px-2" | |||
> | |||
{{ $t("products.consultation") }} | |||
</h1> | |||
<nuxt-link | |||
:to="locale === defaultLocale ? '/contact' : `/${locale}/contact`" | |||
class="w-32 h-10 md:w-40 md:h-11 bg-zinc-300/10 rounded-lg outline outline-1 flex items-center justify-center gap-2 outline-white/20 backdrop-blur-[10px] cursor-pointer hover:bg-zinc-300/20 transition-colors duration-200" | |||
> | |||
<span class="text-xs md:text-sm font-normal">{{ | |||
$t("products.consultation_button") | |||
}}</span> | |||
<i class="icon-arrow-right text-sm font-normal"></i> | |||
</nuxt-link> | |||
</div> | |||
<img | |||
v-if="isMobile" | |||
:src="videoWebp" | |||
alt="video" | |||
class="w-full h-full object-cover opacity-20" | |||
/> | |||
<video | |||
v-else | |||
:src="videoSrc" | |||
autoplay | |||
muted | |||
loop | |||
:poster="videoWebp" | |||
class="w-full h-full object-cover opacity-20" | |||
></video> | |||
</div> | |||
@@ -454,13 +698,26 @@ import "swiper/css"; | |||
import "swiper/css/navigation"; | |||
import "swiper/css/pagination"; | |||
import { useBreakpoints, breakpointsTailwind } from "@vueuse/core"; | |||
import { useI18n } from "vue-i18n"; | |||
import video from "@/assets/videos/video.mp4"; | |||
import videoWebp from "@/assets/videos/video.webp"; | |||
import homeA1Webp from "@/assets/images/home-a-1.webp"; | |||
import homeC1Webp from "@/assets/images/home-c-1.webp"; | |||
import product from "@/assets/images/product.png"; | |||
const { t, locale } = useI18n(); | |||
const config = useRuntimeConfig(); | |||
// 从运行时配置获取默认语言,如果未配置则默认为 'en' | |||
const defaultLocale = config.public.i18n?.defaultLocale || "en"; | |||
const videoSrc = ref(video); | |||
// Define breakpoints | |||
const breakpoints = useBreakpoints(breakpointsTailwind); | |||
// Check if the device is mobile (smaller than md) | |||
const isMobile = breakpoints.smaller("md"); | |||
/** | |||
* 网站首页 | |||
* 展示网站主要内容和精选产品 | |||
@@ -509,4 +766,15 @@ useHead({ | |||
:deep(.swiper-pagination-bullet-active) { | |||
background-color: var(--color-text); /* Example color */ | |||
} | |||
.category-item { | |||
background-image: url("@/assets/images/home-b-1.webp"); | |||
background-size: cover; | |||
background-position: center; | |||
background-repeat: no-repeat; | |||
opacity: 0.8; | |||
transition: opacity 0.3s ease; | |||
&:hover { | |||
opacity: 1; | |||
} | |||
} | |||
</style> |
@@ -1,31 +1,221 @@ | |||
<template> | |||
<div class="py-8"> | |||
<div class="container-custom"> | |||
<h1 class="text-3xl font-bold mb-8">{{ $t('products.title') }}</h1> | |||
<ErrorBoundary :error="error"> | |||
<div v-if="isLoading" class="flex justify-center py-12"> | |||
<!-- 加载中 --> | |||
<div class="animate-spin h-8 w-8 border-4 border-blue-500 rounded-full border-t-transparent"></div> | |||
</div> | |||
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> | |||
<div v-for="product in products" :key="product.id" class="bg-white border border-gray-200 rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow"> | |||
<div class="h-48 bg-gray-100 flex items-center justify-center"> | |||
<!-- 产品图片占位符 --> | |||
<span class="text-gray-400">{{ product.title }}</span> | |||
<div> | |||
<div class="w-full h-[55px] sm:h-[72px]"></div> | |||
<ErrorBoundary :error="error"> | |||
<div v-if="isLoading" class="flex justify-center py-12"> | |||
<!-- 加载中 --> | |||
<div | |||
class="animate-spin h-8 w-8 border-4 border-blue-500 rounded-full border-t-transparent" | |||
></div> | |||
</div> | |||
<div v-else> | |||
<div class="w-full mb-12 relative"> | |||
<div class="absolute top-0 left-0 w-full h-full z-10"> | |||
<div | |||
class="max-w-screen-2xl mx-auto h-full flex flex-col justify-center gap-4 p-4" | |||
> | |||
<div | |||
class="justify-start text-white text-2xl font-normal md:text-4xl lg:text-6xl" | |||
> | |||
製品一覧 | |||
</div> | |||
<div | |||
class="text-white text-sm lg:text-lg font-normal leading-loose" | |||
> | |||
卓越した製品は、実績に裏打ちされた優れた技術と、<br />継続的な革新デザインとの融合により、生み出されます。 | |||
</div> | |||
</div> | |||
<div class="p-6"> | |||
<h2 class="text-xl font-semibold mb-2">{{ product.title }}</h2> | |||
<p class="text-gray-600 mb-4">{{ product.description }}</p> | |||
<NuxtLink :to="`/products/${product.id}`" class="text-blue-600 hover:text-blue-800 font-medium"> | |||
{{ $t('products.viewDetails') }} | |||
</NuxtLink> | |||
</div> | |||
<img | |||
:src="banner" | |||
alt="products-banner" | |||
class="w-full object-cover h-60 lg:h-full" | |||
/> | |||
</div> | |||
<div class="max-w-full mb-6 xl:px-2 lg:px-2 md:px-4 px-4"> | |||
<div class="max-w-screen-2xl mx-auto"> | |||
<nuxt-link | |||
to="/" | |||
class="justify-start text-white/60 text-base font-normal" | |||
>ホーム</nuxt-link | |||
> | |||
<span class="text-white/60 text-base font-normal px-2"> / </span> | |||
<nuxt-link to="/products" class="text-white text-base font-normal" | |||
>製品一覧</nuxt-link | |||
> | |||
</div> | |||
</div> | |||
<div | |||
class="max-w-full mb-12 md:mb-20 lg:mb-32 xl:px-2 lg:px-2 md:px-4 px-4" | |||
> | |||
<div class="max-w-screen-2xl mx-auto"> | |||
<div class="w-full grid grid-cols-1 md:grid-cols-10 gap-8 md:gap-2"> | |||
<div | |||
class="col-span-1 md:col-span-2 flex flex-col gap-16 mb-8 md:mb-0" | |||
> | |||
<div class="flex flex-col gap-4"> | |||
<div class="text-white text-3xl font-medium"> | |||
製品カテゴリー | |||
</div> | |||
<div class="flex flex-col gap-4"> | |||
<div | |||
class="opacity-80 justify-start text-white text-base font-normal" | |||
> | |||
PCメモリ | |||
</div> | |||
<div | |||
class="opacity-80 justify-start text-white text-base font-normal" | |||
> | |||
PCメモリ | |||
</div> | |||
</div> | |||
</div> | |||
<div class="flex flex-col gap-4"> | |||
<div class="text-white text-3xl font-medium"> | |||
製品カテゴリー | |||
</div> | |||
<div class="flex flex-col gap-4"> | |||
<div | |||
class="opacity-80 justify-start text-white text-base font-normal" | |||
> | |||
PCメモリ | |||
</div> | |||
<div | |||
class="opacity-80 justify-start text-white text-base font-normal" | |||
> | |||
PCメモリ | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="col-span-1 md:col-span-8"> | |||
<div class="flex flex-col gap-16"> | |||
<div class="flex flex-col gap-4"> | |||
<div class="w-full text-white text-4xl font-normal mb-4"> | |||
2.5-inch SSD | |||
</div> | |||
<div | |||
class="w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4" | |||
> | |||
<div class="bg-zinc-900 rounded-lg"> | |||
<div class="w-full p-8"> | |||
<img | |||
src="https://placehold.co/400x400" | |||
alt="" | |||
class="w-full h-full object-cover rounded-lg mb-4" | |||
/> | |||
<div | |||
class="text-center justify-start text-white text-xl font-normal" | |||
> | |||
Hanye Q60-256GST3 | |||
</div> | |||
<div | |||
class="text-center justify-start text-stone-400 text-base font-normal leading-normal" | |||
> | |||
256GB / 512GB / 1TB / 2TB | |||
</div> | |||
</div> | |||
</div> | |||
<div class="bg-zinc-900 rounded-lg"></div> | |||
<div class="bg-zinc-900 rounded-lg"></div> | |||
<div class="bg-zinc-900 rounded-lg"></div> | |||
</div> | |||
</div> | |||
<div class="flex flex-col gap-4"> | |||
<div class="w-full text-white text-4xl font-normal mb-4"> | |||
2.5-inch SSD | |||
</div> | |||
<div | |||
class="w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4" | |||
> | |||
<div class="bg-zinc-900 rounded-lg"> | |||
<div class="w-full p-8"> | |||
<img | |||
src="https://placehold.co/400x400" | |||
alt="" | |||
class="w-full h-full object-cover rounded-lg mb-4" | |||
/> | |||
<div | |||
class="text-center justify-start text-white text-xl font-normal" | |||
> | |||
Hanye Q60-256GST3 | |||
</div> | |||
<div | |||
class="text-center justify-start text-stone-400 text-base font-normal leading-normal" | |||
> | |||
256GB / 512GB / 1TB / 2TB | |||
</div> | |||
</div> | |||
</div> | |||
<div class="bg-zinc-900 rounded-lg"> | |||
<div class="w-full p-8"> | |||
<img | |||
src="https://placehold.co/400x400" | |||
alt="" | |||
class="w-full h-full object-cover rounded-lg mb-4" | |||
/> | |||
<div | |||
class="text-center justify-start text-white text-xl font-normal" | |||
> | |||
Hanye Q60-256GST3 | |||
</div> | |||
<div | |||
class="text-center justify-start text-stone-400 text-base font-normal leading-normal" | |||
> | |||
256GB / 512GB / 1TB / 2TB | |||
</div> | |||
</div> | |||
</div> | |||
<div class="bg-zinc-900 rounded-lg"> | |||
<div class="w-full p-8"> | |||
<img | |||
src="https://placehold.co/400x400" | |||
alt="" | |||
class="w-full h-full object-cover rounded-lg mb-4" | |||
/> | |||
<div | |||
class="text-center justify-start text-white text-xl font-normal" | |||
> | |||
Hanye Q60-256GST3 | |||
</div> | |||
<div | |||
class="text-center justify-start text-stone-400 text-base font-normal leading-normal" | |||
> | |||
256GB / 512GB / 1TB / 2TB | |||
</div> | |||
</div> | |||
</div> | |||
<div class="bg-zinc-900 rounded-lg"> | |||
<div class="w-full p-8"> | |||
<img | |||
src="https://placehold.co/400x400" | |||
alt="" | |||
class="w-full h-full object-cover rounded-lg mb-4" | |||
/> | |||
<div | |||
class="text-center justify-start text-white text-xl font-normal" | |||
> | |||
Hanye Q60-256GST3 | |||
</div> | |||
<div | |||
class="text-center justify-start text-stone-400 text-base font-normal leading-normal" | |||
> | |||
256GB / 512GB / 1TB / 2TB | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</ErrorBoundary> | |||
</div> | |||
</div> | |||
</ErrorBoundary> | |||
</div> | |||
</template> | |||
@@ -34,8 +224,8 @@ | |||
* 产品列表页面 | |||
* 展示所有产品 | |||
*/ | |||
import { ref, onMounted } from 'vue'; | |||
import { useErrorHandler } from '~/composables/useErrorHandler'; | |||
import { useErrorHandler } from "~/composables/useErrorHandler"; | |||
import banner from "@/assets/images/product-banner.webp"; | |||
// 产品接口定义 | |||
interface Product { | |||
@@ -53,42 +243,42 @@ const products = ref<Product[]>([]); | |||
async function loadProducts() { | |||
await wrapAsync(async () => { | |||
// 模拟API请求延迟 | |||
await new Promise(resolve => setTimeout(resolve, 500)); | |||
await new Promise((resolve) => setTimeout(resolve, 500)); | |||
// 模拟数据,实际项目中应从API获取 | |||
products.value = [ | |||
{ | |||
id: 1, | |||
title: '产品一', | |||
description: '这是产品一的详细描述,介绍产品特点和用途。' | |||
title: "产品一", | |||
description: "这是产品一的详细描述,介绍产品特点和用途。", | |||
}, | |||
{ | |||
id: 2, | |||
title: '产品二', | |||
description: '这是产品二的详细描述,介绍产品特点和用途。' | |||
title: "产品二", | |||
description: "这是产品二的详细描述,介绍产品特点和用途。", | |||
}, | |||
{ | |||
id: 3, | |||
title: '产品三', | |||
description: '这是产品三的详细描述,介绍产品特点和用途。' | |||
title: "产品三", | |||
description: "这是产品三的详细描述,介绍产品特点和用途。", | |||
}, | |||
{ | |||
id: 4, | |||
title: '产品四', | |||
description: '这是产品四的详细描述,介绍产品特点和用途。' | |||
title: "产品四", | |||
description: "这是产品四的详细描述,介绍产品特点和用途。", | |||
}, | |||
{ | |||
id: 5, | |||
title: '产品五', | |||
description: '这是产品五的详细描述,介绍产品特点和用途。' | |||
title: "产品五", | |||
description: "这是产品五的详细描述,介绍产品特点和用途。", | |||
}, | |||
{ | |||
id: 6, | |||
title: '产品六', | |||
description: '这是产品六的详细描述,介绍产品特点和用途。' | |||
} | |||
title: "产品六", | |||
description: "这是产品六的详细描述,介绍产品特点和用途。", | |||
}, | |||
]; | |||
return products.value; | |||
}); | |||
} | |||
@@ -100,9 +290,12 @@ onMounted(() => { | |||
// SEO优化 | |||
useHead({ | |||
title: '产品列表 - Hanye', | |||
title: "产品列表 - Hanye", | |||
meta: [ | |||
{ name: 'description', content: '浏览我们的产品列表,找到适合您的解决方案。' } | |||
] | |||
{ | |||
name: "description", | |||
content: "浏览我们的产品列表,找到适合您的解决方案。", | |||
}, | |||
], | |||
}); | |||
</script> | |||
</script> |