Hanye官网
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

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