Hanye官网
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. <template>
  2. <div class="careers-page bg-stone-950 min-h-screen">
  3. <div class="relative z-10">
  4. <!-- 顶部间距 -->
  5. <div class="w-full h-[55px] sm:h-[72px]"></div>
  6. <!-- 错误边界 -->
  7. <ErrorBoundary :error="pageError">
  8. <!-- 页面标题区域 -->
  9. <section class="hero-section relative overflow-hidden">
  10. <!-- 面包屑导航 -->
  11. <div
  12. class="max-w-full xl:px-2 lg:px-2 md:px-4 px-4 mt-6 mb-1 sm:mb-4"
  13. >
  14. <div class="max-w-screen-2xl mx-auto relative z-10">
  15. <nuxt-link
  16. :to="`${homepagePath}/`"
  17. class="justify-start text-white/60 text-base font-normal"
  18. >{{ t("common.breadcrumb.home") }}</nuxt-link
  19. >
  20. <span class="text-white/60 text-base font-normal px-2"> / </span>
  21. <span class="text-white text-base font-normal">{{
  22. t("common.careers")
  23. }}</span>
  24. </div>
  25. </div>
  26. <div class="w-full py-20 md:py-40">
  27. <!-- 图片背景 -->
  28. <div class="absolute inset-0 z-0">
  29. <img
  30. src="/assets/images/careersbg.webp"
  31. alt="Background"
  32. class="w-full h-full object-cover opacity-20"
  33. />
  34. </div>
  35. <div
  36. class="max-w-screen-2xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10"
  37. >
  38. <!-- 标题 -->
  39. <div class="text-center space-y-12">
  40. <h1
  41. class="text-white text-3xl sm:text-4xl md:text-6xl font-bold tracking-wider leading-tight"
  42. >
  43. <span
  44. class="bg-gradient-to-r from-white to-zinc-300 bg-clip-text text-transparent"
  45. >
  46. {{ t("careers.slogan") }}
  47. </span>
  48. </h1>
  49. <p
  50. class="text-zinc-300/80 text-base md:text-xl max-w-4xl mx-auto leading-relaxed"
  51. >
  52. {{ t("careers.subtitle") }}
  53. </p>
  54. </div>
  55. </div>
  56. </div>
  57. </section>
  58. <!-- 企业文化区域 -->
  59. <section class="culture-section">
  60. <div class="section-block w-full py-20 md:py-32 relative">
  61. <div
  62. class="max-w-screen-2xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10"
  63. >
  64. <div class="text-center mb-12 md:mb-20">
  65. <h3
  66. class="text-white text-2xl md:text-4xl font-bold mb-6 tracking-wider"
  67. >
  68. {{ t("careers.culture.title") }}
  69. </h3>
  70. <div class="w-20 h-px bg-indigo-400 mx-auto"></div>
  71. </div>
  72. <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
  73. <!-- 创新驱动 -->
  74. <div class="culture-card group">
  75. <div
  76. class="relative p-8 bg-zinc-800/30 backdrop-blur-sm border border-zinc-700/30 rounded-xl transition-all duration-700 hover:border-indigo-400/50 hover:bg-zinc-800/50 hover:shadow-2xl hover:shadow-indigo-500/10 hover:transform hover:scale-105"
  77. >
  78. <div
  79. class="absolute inset-0 bg-gradient-to-br from-indigo-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 rounded-xl"
  80. ></div>
  81. <div class="relative">
  82. <div
  83. class="w-16 h-16 bg-indigo-500/10 rounded-xl flex items-center justify-center mb-6 group-hover:bg-indigo-500/20 transition-all duration-500 group-hover:rotate-12 group-hover:scale-110"
  84. >
  85. <svg
  86. class="w-8 h-8 text-indigo-400"
  87. fill="none"
  88. stroke="currentColor"
  89. viewBox="0 0 24 24"
  90. >
  91. <path
  92. stroke-linecap="round"
  93. stroke-linejoin="round"
  94. stroke-width="2"
  95. d="M13 10V3L4 14h7v7l9-11h-7z"
  96. ></path>
  97. </svg>
  98. </div>
  99. <h4
  100. class="text-white text-xl font-semibold mb-4 group-hover:text-indigo-400 transition-colors duration-500"
  101. >
  102. {{ t("careers.culture.innovation.title") }}
  103. </h4>
  104. <p class="text-zinc-300/80 text-sm leading-relaxed">
  105. {{ t("careers.culture.innovation.description") }}
  106. </p>
  107. </div>
  108. </div>
  109. </div>
  110. <!-- 团队协作 -->
  111. <div class="culture-card group">
  112. <div
  113. class="relative p-8 bg-zinc-800/30 backdrop-blur-sm border border-zinc-700/30 rounded-xl transition-all duration-700 hover:border-indigo-400/50 hover:bg-zinc-800/50 hover:shadow-2xl hover:shadow-indigo-500/10 hover:transform hover:scale-105"
  114. >
  115. <div
  116. class="absolute inset-0 bg-gradient-to-br from-indigo-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 rounded-xl"
  117. ></div>
  118. <div class="relative">
  119. <div
  120. class="w-16 h-16 bg-indigo-500/10 rounded-xl flex items-center justify-center mb-6 group-hover:bg-indigo-500/20 transition-all duration-500 group-hover:rotate-12 group-hover:scale-110"
  121. >
  122. <svg
  123. class="w-8 h-8 text-indigo-400"
  124. fill="none"
  125. stroke="currentColor"
  126. viewBox="0 0 24 24"
  127. >
  128. <path
  129. stroke-linecap="round"
  130. stroke-linejoin="round"
  131. stroke-width="2"
  132. d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"
  133. ></path>
  134. </svg>
  135. </div>
  136. <h4
  137. class="text-white text-xl font-semibold mb-4 group-hover:text-indigo-400 transition-colors duration-500"
  138. >
  139. {{ t("careers.culture.teamwork.title") }}
  140. </h4>
  141. <p class="text-zinc-300/80 text-sm leading-relaxed">
  142. {{ t("careers.culture.teamwork.description") }}
  143. </p>
  144. </div>
  145. </div>
  146. </div>
  147. <!-- 品质第一 -->
  148. <div class="culture-card group">
  149. <div
  150. class="relative p-8 bg-zinc-800/30 backdrop-blur-sm border border-zinc-700/30 rounded-xl transition-all duration-700 hover:border-indigo-400/50 hover:bg-zinc-800/50 hover:shadow-2xl hover:shadow-indigo-500/10 hover:transform hover:scale-105"
  151. >
  152. <div
  153. class="absolute inset-0 bg-gradient-to-br from-indigo-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 rounded-xl"
  154. ></div>
  155. <div class="relative">
  156. <div
  157. class="w-16 h-16 bg-indigo-500/10 rounded-xl flex items-center justify-center mb-6 group-hover:bg-indigo-500/20 transition-all duration-500 group-hover:rotate-12 group-hover:scale-110"
  158. >
  159. <svg
  160. class="w-8 h-8 text-indigo-400"
  161. fill="none"
  162. stroke="currentColor"
  163. viewBox="0 0 24 24"
  164. >
  165. <path
  166. stroke-linecap="round"
  167. stroke-linejoin="round"
  168. stroke-width="2"
  169. d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
  170. ></path>
  171. </svg>
  172. </div>
  173. <h4
  174. class="text-white text-xl font-semibold mb-4 group-hover:text-indigo-400 transition-colors duration-500"
  175. >
  176. {{ t("careers.culture.quality.title") }}
  177. </h4>
  178. <p class="text-zinc-300/80 text-sm leading-relaxed">
  179. {{ t("careers.culture.quality.description") }}
  180. </p>
  181. </div>
  182. </div>
  183. </div>
  184. <!-- 共同成长 -->
  185. <div class="culture-card group">
  186. <div
  187. class="relative p-8 bg-zinc-800/30 backdrop-blur-sm border border-zinc-700/30 rounded-xl transition-all duration-700 hover:border-indigo-400/50 hover:bg-zinc-800/50 hover:shadow-2xl hover:shadow-indigo-500/10 hover:transform hover:scale-105"
  188. >
  189. <div
  190. class="absolute inset-0 bg-gradient-to-br from-indigo-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 rounded-xl"
  191. ></div>
  192. <div class="relative">
  193. <div
  194. class="w-16 h-16 bg-indigo-500/10 rounded-xl flex items-center justify-center mb-6 group-hover:bg-indigo-500/20 transition-all duration-500 group-hover:rotate-12 group-hover:scale-110"
  195. >
  196. <svg
  197. class="w-8 h-8 text-indigo-400"
  198. fill="none"
  199. stroke="currentColor"
  200. viewBox="0 0 24 24"
  201. >
  202. <path
  203. stroke-linecap="round"
  204. stroke-linejoin="round"
  205. stroke-width="2"
  206. d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"
  207. ></path>
  208. </svg>
  209. </div>
  210. <h4
  211. class="text-white text-xl font-semibold mb-4 group-hover:text-indigo-400 transition-colors duration-500"
  212. >
  213. {{ t("careers.culture.growth.title") }}
  214. </h4>
  215. <p class="text-zinc-300/80 text-sm leading-relaxed">
  216. {{ t("careers.culture.growth.description") }}
  217. </p>
  218. </div>
  219. </div>
  220. </div>
  221. </div>
  222. </div>
  223. </div>
  224. </section>
  225. <!-- 职位列表区域 -->
  226. <section class="jobs-section">
  227. <div class="section-block w-full py-20 md:py-32 relative">
  228. <!-- 背景网格pattern -->
  229. <div class="absolute inset-0 opacity-5">
  230. <div
  231. class="absolute inset-0 bg-[linear-gradient(rgba(52,211,153,0.1)_1px,transparent_1px),linear-gradient(90deg,rgba(52,211,153,0.1)_1px,transparent_1px)] bg-[size:100px_100px]"
  232. ></div>
  233. </div>
  234. <div
  235. class="max-w-screen-2xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10"
  236. >
  237. <div class="text-center mb-12 md:mb-20">
  238. <h3
  239. class="text-white text-2xl md:text-4xl font-bold mb-6 tracking-wider"
  240. >
  241. {{ t("careers.jobs.title") }}
  242. </h3>
  243. <div class="w-20 h-px bg-emerald-400 mx-auto mb-8"></div>
  244. </div>
  245. <!-- 加载状态 -->
  246. <div v-if="jobsLoading" class="flex justify-center py-20">
  247. <div
  248. class="animate-spin h-8 w-8 border-4 border-emerald-400 rounded-full border-t-transparent"
  249. ></div>
  250. </div>
  251. <!-- 职位列表 -->
  252. <div v-else-if="jobsList.length > 0">
  253. <!-- 职位卡片网格 -->
  254. <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
  255. <JobCard v-for="job in jobsList" :key="job.id" :job="job" />
  256. </div>
  257. </div>
  258. <!-- 无职位状态 -->
  259. <div v-else class="text-center py-20">
  260. <div
  261. class="relative inline-flex items-center justify-center w-24 h-24 mb-8"
  262. >
  263. <svg
  264. class="w-12 h-12 text-zinc-500"
  265. fill="none"
  266. stroke="currentColor"
  267. viewBox="0 0 24 24"
  268. >
  269. <path
  270. stroke-linecap="round"
  271. stroke-linejoin="round"
  272. stroke-width="2"
  273. d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2-2v2m8 0H8m8 0v2a2 2 0 002 2M8 6V4h8V6M8 6v2a2 2 0 002 2h4a2 2 0 002-2V6"
  274. ></path>
  275. </svg>
  276. </div>
  277. <h4 class="text-zinc-400 text-xl font-medium mb-3">
  278. {{ t("careers.jobs.noPositions") }}
  279. </h4>
  280. <p
  281. class="text-zinc-500 text-base max-w-md mx-auto leading-relaxed"
  282. >
  283. {{ t("careers.jobs.checkLater") }}
  284. </p>
  285. </div>
  286. </div>
  287. </div>
  288. </section>
  289. <!-- 底部CTA区域 -->
  290. <section class="cta-section">
  291. <div class="section-block w-full py-20 md:py-32 relative">
  292. <!-- 图片背景 -->
  293. <img
  294. src="/assets/images/careers.webp"
  295. alt="CTA Background"
  296. class="absolute inset-0 w-full h-full object-cover opacity-40 z-0"
  297. />
  298. <div
  299. class="max-w-screen-2xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10 text-center"
  300. >
  301. <div class="max-w-3xl mx-auto space-y-8">
  302. <h3
  303. class="text-white text-2xl md:text-4xl font-bold leading-tight"
  304. >
  305. {{ t("careers.cta.title") }}
  306. </h3>
  307. <p
  308. class="text-zinc-300/80 text-base md:text-xl leading-relaxed"
  309. >
  310. {{ t("careers.cta.description") }}
  311. </p>
  312. <div class="pt-4">
  313. <nuxt-link
  314. :to="`${homepagePath}/contact`"
  315. class="inline-flex w-32 h-10 md:w-40 md:h-11 bg-zinc-300/10 rounded-lg outline outline-1 items-center justify-center gap-2 outline-white/20 backdrop-blur-[10px] cursor-pointer hover:bg-zinc-300/20 transition-colors duration-200"
  316. >
  317. <span class="text-sm md:text-base font-normal text-white">{{
  318. t("careers.cta.button")
  319. }}</span>
  320. <i
  321. class="icon-arrow-right text-xs md:text-sm font-normal text-white"
  322. ></i>
  323. </nuxt-link>
  324. </div>
  325. </div>
  326. </div>
  327. </div>
  328. </section>
  329. </ErrorBoundary>
  330. </div>
  331. </div>
  332. </template>
  333. <script setup lang="ts">
  334. import type { Job, JobsApiResponse } from "~/data/jobs";
  335. import { localJobs } from "~/data/jobs";
  336. const { t, locale } = useI18n();
  337. // 计算首页路径
  338. const homepagePath = computed(() => {
  339. return locale.value === "zh" ? "" : `/${locale.value}`;
  340. });
  341. // SEO和页面元数据
  342. useSeoMeta({
  343. title: `${t("careers.title")} - Hanye`,
  344. description: t("careers.subtitle"),
  345. keywords: t("careers.keywords"),
  346. });
  347. // 响应式数据
  348. const pageError = ref<Error | null>(null);
  349. const jobsLoading = ref(false);
  350. const jobsList = ref<Job[]>([]);
  351. const totalJobs = ref(0);
  352. // API配置
  353. const API_BASE_URL = "https://digital.sohomall.jp/prod-api/system/workInfo";
  354. /**
  355. * 从API获取职位数据
  356. * @param pageNum 页码
  357. * @param pageSize 页面大小
  358. * @returns Promise<JobsApiResponse>
  359. */
  360. const fetchJobsFromApi = async (
  361. pageNum: number = 1,
  362. pageSize: number = 20
  363. ): Promise<JobsApiResponse> => {
  364. const response = await $fetch<JobsApiResponse>(
  365. `${API_BASE_URL}/noVerifyList`,
  366. {
  367. params: {
  368. pageNum,
  369. pageSize,
  370. },
  371. timeout: 10000, // 10秒超时
  372. }
  373. );
  374. if (response.code !== 200) {
  375. throw new Error(response.msg || "获取职位信息失败");
  376. }
  377. return response;
  378. };
  379. /**
  380. * 初始化职位数据
  381. */
  382. const initializeJobs = async () => {
  383. try {
  384. jobsLoading.value = true;
  385. pageError.value = null;
  386. // 尝试从API获取数据
  387. const apiResponse = await fetchJobsFromApi();
  388. console.log(apiResponse);
  389. // 过滤掉已禁用的职位 (isDisabled为"1"表示启用,"0"表示禁用)
  390. const availableJobs = apiResponse.rows.filter(
  391. (job: Job) => job.isDisabled === "1"
  392. );
  393. jobsList.value = availableJobs;
  394. totalJobs.value = apiResponse.total;
  395. console.log(`成功加载 ${availableJobs.length} 个职位信息`);
  396. } catch (error) {
  397. console.warn("API获取失败,使用本地数据:", error);
  398. // API失败时使用本地数据作为fallback
  399. // 过滤掉已禁用的职位
  400. const availableLocalJobs = localJobs.filter(
  401. (job: Job) => job.isDisabled === "0"
  402. );
  403. jobsList.value = availableLocalJobs;
  404. totalJobs.value = availableLocalJobs.length;
  405. // 不设置pageError,因为有fallback数据
  406. console.log(`使用本地数据加载 ${availableLocalJobs.length} 个职位信息`);
  407. } finally {
  408. jobsLoading.value = false;
  409. }
  410. };
  411. /**
  412. * 处理联系按钮点击
  413. */
  414. const handleContactClick = () => {
  415. navigateTo("/contact");
  416. };
  417. /**
  418. * 刷新职位数据
  419. */
  420. const refreshJobs = async () => {
  421. await initializeJobs();
  422. };
  423. // 组件挂载时初始化数据
  424. onMounted(async () => {
  425. await initializeJobs();
  426. // 添加页面加载动画
  427. nextTick(() => {
  428. const cards = document.querySelectorAll(".culture-card, .job-card");
  429. cards.forEach((card, index) => {
  430. setTimeout(() => {
  431. (card as HTMLElement).style.opacity = "1";
  432. (card as HTMLElement).style.transform = "translateY(0)";
  433. }, 100 * index);
  434. });
  435. // 面包屑动画
  436. const breadcrumb = document.querySelector("nav");
  437. if (breadcrumb) {
  438. setTimeout(() => {
  439. breadcrumb.classList.remove("opacity-0");
  440. breadcrumb.classList.add("opacity-100");
  441. }, 200);
  442. }
  443. });
  444. });
  445. </script>
  446. <style scoped>
  447. .careers-page {
  448. scroll-behavior: smooth;
  449. }
  450. .culture-card,
  451. .job-card {
  452. opacity: 0;
  453. transform: translateY(20px);
  454. transition: opacity 0.8s ease, transform 0.8s ease;
  455. }
  456. @keyframes fade-in {
  457. from {
  458. opacity: 0;
  459. transform: translateY(10px);
  460. }
  461. to {
  462. opacity: 1;
  463. transform: translateY(0);
  464. }
  465. }
  466. .animate-fade-in {
  467. animation: fade-in 0.6s ease-out forwards;
  468. }
  469. /* 自定义滚动条 */
  470. ::-webkit-scrollbar {
  471. width: 6px;
  472. }
  473. ::-webkit-scrollbar-track {
  474. background: rgba(0, 0, 0, 0.1);
  475. }
  476. ::-webkit-scrollbar-thumb {
  477. background: #35f1ff;
  478. border-radius: 3px;
  479. }
  480. ::-webkit-scrollbar-thumb:hover {
  481. background: #35f1ff;
  482. }
  483. /* 动画关键帧 */
  484. @keyframes float {
  485. 0%,
  486. 100% {
  487. transform: translateY(0px);
  488. }
  489. 50% {
  490. transform: translateY(-10px);
  491. }
  492. }
  493. @keyframes pulse-glow {
  494. 0%,
  495. 100% {
  496. box-shadow: 0 0 5px rgba(53, 241, 255, 0.3);
  497. }
  498. 50% {
  499. box-shadow: 0 0 20px rgba(53, 241, 255, 0.6);
  500. }
  501. }
  502. .animate-float {
  503. animation: float 6s ease-in-out infinite;
  504. }
  505. .animate-pulse-glow {
  506. animation: pulse-glow 2s ease-in-out infinite;
  507. }
  508. /* 蒙层和光泽效果 */
  509. .section-block {
  510. position: relative;
  511. transition: all 0.5s ease;
  512. overflow: hidden;
  513. }
  514. .section-block::before {
  515. content: "";
  516. position: absolute;
  517. inset: 0;
  518. opacity: 0;
  519. transition: opacity 0.5s ease;
  520. pointer-events: none;
  521. z-index: 5;
  522. }
  523. .section-block:hover::before {
  524. opacity: 1;
  525. }
  526. /* 为每个模块单独定义蒙层颜色 */
  527. .culture-section .section-block::before {
  528. background: linear-gradient(
  529. 45deg,
  530. transparent,
  531. rgba(99, 102, 241, 0.1),
  532. /* Indigo */ transparent
  533. );
  534. }
  535. .jobs-section .section-block::before {
  536. background: linear-gradient(
  537. 45deg,
  538. transparent,
  539. rgba(52, 211, 153, 0.1),
  540. /* Emerald */ transparent
  541. );
  542. }
  543. .cta-section .section-block::before {
  544. background: linear-gradient(
  545. 45deg,
  546. transparent,
  547. rgba(241, 252, 255, 0.021),
  548. /* Rose */ transparent
  549. );
  550. }
  551. </style>