index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. <script setup>
  2. import { fa } from 'element-plus/es/locale/index.mjs'
  3. const { t, locale } = useI18n()
  4. const { $$t } = useStoreI18n()
  5. const areaCodeList = ref([])
  6. const showCourseType = ref(false)
  7. const showAge = ref(false)
  8. const ageIndex = ref(null)
  9. const loading = ref(false)
  10. const showAreaCode = ref(false) // 区号
  11. const showCountryId = ref(false) //地区
  12. const courseTypeList = ref([
  13. {
  14. value: '1',
  15. label: $$t('beginning')
  16. },
  17. {
  18. value: '2',
  19. label: $$t('growing')
  20. },
  21. {
  22. value: '3',
  23. label: $$t('breaking')
  24. },
  25. {
  26. value: '4',
  27. label: $$t('expanding')
  28. }
  29. ])
  30. const ageList = ref([
  31. {
  32. id: 'old',
  33. label: $$t('ageBracket', ['6-9']),
  34. minAge: 6,
  35. maxAge: 9
  36. },
  37. {
  38. id: 'old',
  39. label: $$t('ageBracket', ['6-12']),
  40. minAge: 6,
  41. maxAge: 12
  42. },
  43. {
  44. id: 'old',
  45. label: $$t('ageBracket', ['9-15']),
  46. minAge: 9,
  47. maxAge: 15
  48. },
  49. {
  50. id: 'old',
  51. label: $$t('ageBracket', ['12-18']),
  52. minAge: 12,
  53. maxAge: 18
  54. }
  55. ])
  56. const studentSignUp = ref({
  57. surname: '',
  58. name: '',
  59. minAge: '',
  60. maxAge: '',
  61. countryId: '',
  62. email: '',
  63. phone: '',
  64. courseType: '',
  65. areaCode: '+86'
  66. })
  67. // 表单错误信息
  68. const formErrors = ref({
  69. surname: '',
  70. name: '',
  71. age: '',
  72. email: '',
  73. countryId: '',
  74. phone: '',
  75. courseType: '',
  76. areaCode: ''
  77. })
  78. // 国家获取区号接口
  79. async function getAreaCode() {
  80. // alert('获取区号')
  81. console.log('获取区号')
  82. loading.value = true
  83. try {
  84. let res = await request('/country/baseCountry/getCountryCode')
  85. console.log(res, '555')
  86. if (res && res.data) {
  87. areaCodeList.value = res.data
  88. loading.value = false
  89. } else {
  90. }
  91. } catch (error) {}
  92. }
  93. const areaCodeLang = computed(() =>
  94. areaCodeList.value.map(({ areaCode, countryNameZh, countryNameEn, id }) => ({
  95. value: areaCode,
  96. label: locale.value == 'zh' ? countryNameZh : countryNameEn,
  97. id
  98. }))
  99. )
  100. const rootContainer = ref(null)
  101. onMounted(() => {
  102. getAreaCode()
  103. // 监听页面关闭 非 i 标签的事件操作
  104. if (rootContainer.value) {
  105. document.body.addEventListener('click', (event) => {
  106. if (event.target.tagName.toLowerCase() != 'i') {
  107. showCourseType.value = false
  108. showAge.value = false
  109. showAreaCode.value = false // 区号
  110. showCountryId.value = false //地区
  111. // alert('feii')
  112. return
  113. }
  114. // alert('i')
  115. })
  116. }
  117. })
  118. const btnLoading = ref(false)
  119. // 用于存储定时器 ID
  120. const timerId = ref(null)
  121. // 学生报名
  122. async function submitStudentSignUp() {
  123. // 清空错误信息
  124. Object.keys(studentSignUp.value).forEach((key) => {
  125. formErrors.value[key] = ''
  126. })
  127. // 校验表单
  128. let isValid = true
  129. if (!studentSignUp.value.surname) {
  130. formErrors.value.surname = $$t('pleaseEnterSurname')
  131. isValid = false
  132. }
  133. if (!studentSignUp.value.name) {
  134. formErrors.value.name = $$t('pleaseEnterName')
  135. isValid = false
  136. }
  137. if (!studentSignUp.value.countryId) {
  138. formErrors.value.countryId = $$t('pleaseSelectRegion')
  139. isValid = false
  140. }
  141. if (!studentSignUp.value.minAge && !studentSignUp.value.maxAge) {
  142. formErrors.value.age = $$t('pleaseEnterAgeRange')
  143. isValid = false
  144. }
  145. if (!studentSignUp.value.email) {
  146. formErrors.value.email = $$t('pleaseEnterEmail')
  147. isValid = false
  148. }
  149. if (!studentSignUp.value.phone) {
  150. formErrors.value.phone = $$t('pleaseEnterPhone')
  151. isValid = false
  152. }
  153. if (!studentSignUp.value.areaCode) {
  154. formErrors.value.areaCode = $$t('pleaseSelect')
  155. isValid = false
  156. }
  157. if (!studentSignUp.value.courseType) {
  158. formErrors.value.courseType = $$t('pleaseSelectCourseType')
  159. isValid = false
  160. }
  161. if (!isValid) {
  162. return
  163. }
  164. let body = {}
  165. body = { ...studentSignUp.value }
  166. console.log(body, 'body')
  167. let item = courseTypeList.value.find((item) => item.label == studentSignUp.value.courseType)
  168. let itemArea = areaCodeLang.value.find((item) => item.label == studentSignUp.value.countryId)
  169. console.log(item, 'item')
  170. body.courseType = item.value
  171. body.countryId = itemArea.id
  172. body.areaCode = body.areaCode.replace('+', '')
  173. try {
  174. btnLoading.value = true
  175. let res = await request('/education/happyEntry/addHappyEntry', {
  176. method: 'post',
  177. body
  178. })
  179. if (res) {
  180. ElMessage.success(t('signUpSuccess'))
  181. timerId.value = setTimeout(() => {
  182. btnLoading.value = false
  183. location.href = '/'
  184. }, 3000)
  185. }
  186. } catch (error) {}
  187. }
  188. function changeValue(name) {
  189. console.log(name, 'changeValue ')
  190. if (studentSignUp.value[name]) {
  191. formErrors.value[name] = ''
  192. if (name == 'maxAge') {
  193. formErrors.value.age = ''
  194. }
  195. } else {
  196. if (!studentSignUp.value.surname) {
  197. formErrors.value.surname = $$t('pleaseEnterSurname')
  198. return
  199. }
  200. if (!studentSignUp.value.name) {
  201. formErrors.value.name = $$t('pleaseEnterName')
  202. return
  203. }
  204. if (!studentSignUp.value.countryId) {
  205. formErrors.value.countryId = $$t('pleaseSelectRegion')
  206. return
  207. }
  208. if (!studentSignUp.value.minAge || !studentSignUp.value.maxAge) {
  209. formErrors.value.age = $$t('pleaseEnterAgeRange')
  210. return
  211. }
  212. if (!studentSignUp.value.email) {
  213. formErrors.value.email = $$t('pleaseEnterEmail')
  214. return
  215. }
  216. if (!studentSignUp.value.phone) {
  217. formErrors.value.phone = $$t('pleaseEnterPhone')
  218. return
  219. }
  220. if (!studentSignUp.value.areaCode) {
  221. formErrors.value.areaCode = $$t('pleaseSelect')
  222. return
  223. }
  224. if (!studentSignUp.value.courseType) {
  225. formErrors.value.courseType = $$t('pleaseSelectCourseType')
  226. showCourseType.value = false
  227. return
  228. }
  229. }
  230. }
  231. // 清除定时器
  232. onUnmounted(() => {
  233. if (timerId.value) {
  234. clearTimeout(timerId.value)
  235. }
  236. })
  237. </script>
  238. <style lang="css" scoped>
  239. .multi-line {
  240. overflow: hidden;
  241. display: -webkit-box;
  242. -webkit-box-orient: vertical;
  243. }
  244. .clamp-1 {
  245. -webkit-line-clamp: 1;
  246. }
  247. .clamp-2 {
  248. -webkit-line-clamp: 2;
  249. }
  250. .clamp-3 {
  251. -webkit-line-clamp: 3;
  252. }
  253. .shrink-0 {
  254. flex-shrink: 0;
  255. }
  256. ::v-deep .form-clt select {
  257. display: block !important;
  258. color: #5c707e !important;
  259. }
  260. ::v-deep .form-clt select option {
  261. color: white;
  262. background: var(--theme);
  263. }
  264. ::v-deep .form-clt select option:active {
  265. color: white;
  266. background: var(--theme);
  267. }
  268. .input {
  269. position: relative;
  270. width: 100%;
  271. }
  272. .submenu {
  273. position: absolute;
  274. top: 100%;
  275. z-index: 20;
  276. width: 100%;
  277. background: var(--white);
  278. position: absolute;
  279. color: var(--header);
  280. box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);
  281. -webkit-box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);
  282. -moz-box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);
  283. inset-inline-start: 0;
  284. transform-origin: top center;
  285. transform: translateY(10px);
  286. transition: all 0.4sease-in-out;
  287. }
  288. .submenu {
  289. position: absolute;
  290. top: 100%;
  291. z-index: 11;
  292. width: 100%;
  293. background: var(--white);
  294. position: absolute;
  295. color: var(--header);
  296. box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);
  297. -webkit-box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);
  298. -moz-box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2);
  299. inset-inline-start: 0;
  300. transform-origin: top center;
  301. transform: translateY(10px);
  302. transition: all 0.4sease-in-out;
  303. }
  304. .submenu li {
  305. width: 100%;
  306. box-sizing: border-box;
  307. }
  308. .submenu li {
  309. font-size: 16px;
  310. font-weight: 600;
  311. color: var(--header);
  312. padding: 6px 25px;
  313. width: 100%;
  314. border-bottom: 1px solid #eeeeee;
  315. }
  316. .submenu li:hover {
  317. background: var(--theme);
  318. color: var(--white) !important;
  319. }
  320. .liactive {
  321. background: var(--theme);
  322. color: var(--white) !important;
  323. }
  324. .error {
  325. color: red;
  326. font-size: 14px;
  327. }
  328. .contact-header-bg {
  329. background-color: white;
  330. }
  331. .iclass {
  332. position: absolute;
  333. top: 50%;
  334. right: 5%;
  335. transform: translateY(-50%);
  336. cursor: pointer;
  337. }
  338. </style>
  339. <template>
  340. <Preloader v-if="loading" />
  341. <template v-else>
  342. <FixArea></FixArea>
  343. <Header-top-section sectionClass="header-top-section-4" />
  344. <TopHeader headerClass="header-1 contact-header-bg" />
  345. <div class="search-wrap">
  346. <div class="search-inner">
  347. <i class="fas fa-times search-close" id="search-close"></i>
  348. <div class="search-cell">
  349. <form method="get">
  350. <div class="search-field-holder">
  351. <input type="search" class="main-search-input" :placeholder="$t('search')" />
  352. </div>
  353. </form>
  354. </div>
  355. </div>
  356. </div>
  357. <div class="breadcrumb-wrapper bg-cover" style="background-image: url('/image/breadcrumb.svg')">
  358. <div class="line-shape">
  359. <img src="/picture/line1.png" alt="shape-img" />
  360. </div>
  361. <div class="plane-shape float-bob-y">
  362. <img src="/picture/plane2.png" alt="shape-img" />
  363. </div>
  364. <div class="doll-shape float-bob-x">
  365. <img src="/picture/doll.png" alt="shape-img" />
  366. </div>
  367. <div class="parasuit-shape float-bob-y">
  368. <img src="/picture/parasuit1.png" alt="shape-img" />
  369. </div>
  370. <div class="frame-shape">
  371. <img src="/picture/frame3.png" alt="shape-img" />
  372. </div>
  373. <div class="bee-shape float-bob-x">
  374. <img src="/picture/bee1.png" alt="shape-img" />
  375. </div>
  376. <div class="container">
  377. <div class="page-heading">
  378. <h1 class="wow fadeInUp" data-wow-delay=".3s">{{ $t('contactUs') }}</h1>
  379. <ul class="breadcrumb-items wow fadeInUp" data-wow-delay=".5s">
  380. <li>
  381. <NuxtLink to="/">{{ $t('home') }}</NuxtLink>
  382. </li>
  383. <li>
  384. <i class="fas fa-chevron-right"></i>
  385. </li>
  386. <li>{{ $t('contactUs') }}</li>
  387. </ul>
  388. </div>
  389. </div>
  390. </div>
  391. <section ref="rootContainer" class="contact-section fix section-padding">
  392. <div class="container">
  393. <div class="contact-wrapper-2">
  394. <div class="row g-4 align-items-center">
  395. <div class="col-lg-24">
  396. <div class="contact-content">
  397. <h2>{{ $t('readyToGetStarted') }}</h2>
  398. <p>{{ $t('description') }}</p>
  399. <form
  400. action="contact.php"
  401. @submit.prevent="submitStudentSignUp"
  402. id="contact-form"
  403. class="contact-form-items"
  404. >
  405. <div class="row g-4">
  406. <div class="col-lg-6 wow fadeInUp" data-wow-delay=".3s">
  407. <div class="form-clt">
  408. <span>
  409. {{ $t('lastName') }}
  410. <span style="color: red">*</span>
  411. </span>
  412. <input
  413. type="text"
  414. v-model="studentSignUp.surname"
  415. name="surname"
  416. id="surname"
  417. :placeholder="$t('pleaseEnter')"
  418. @blur="changeValue('surname')"
  419. />
  420. <p v-if="formErrors.surname" class="error">
  421. {{ formErrors.surname }}
  422. </p>
  423. </div>
  424. </div>
  425. <div class="col-lg-6 wow fadeInUp" data-wow-delay=".5s">
  426. <div class="form-clt">
  427. <span>
  428. {{ $t('firstName') }}
  429. <span style="color: red">*</span>
  430. </span>
  431. <input
  432. type="text"
  433. v-model="studentSignUp.name"
  434. name="name"
  435. id="name"
  436. :placeholder="$t('pleaseEnter')"
  437. @blur="changeValue('name')"
  438. />
  439. <p v-if="formErrors.name" class="error">
  440. {{ formErrors.name }}
  441. </p>
  442. </div>
  443. </div>
  444. <div class="col-lg-6 wow fadeInUp" data-wow-delay=".6s">
  445. <div @click.stop="showAge = !showAge" class="form-clt">
  446. <span>
  447. {{ $t('childAge') }}
  448. <span style="color: red">*</span>
  449. </span>
  450. <div
  451. style="border: 1px solid #e3e3e3; border-radius: 5px; position: relative"
  452. class="d-flex justify-content-between align-items-center"
  453. >
  454. <input
  455. style="width: 48%; border: none"
  456. type="text"
  457. v-model="studentSignUp.minAge"
  458. name="minAge"
  459. id="minAge"
  460. readonly
  461. :placeholder="$t('pleaseSelect')"
  462. />
  463. <span>--</span>
  464. <input
  465. style="width: 48%; border: none"
  466. type="text"
  467. v-model="studentSignUp.maxAge"
  468. name="maxAge"
  469. id="maxAge"
  470. readonly
  471. :placeholder="$t('pleaseSelect')"
  472. />
  473. <i :class="`fas iclass ${showAge ? 'fa-angle-up' : 'fa-angle-down'}`"></i>
  474. <ul v-show="showAge" class="submenu">
  475. <li
  476. v-for="(item, index) in ageList"
  477. :key="item.id + index"
  478. :class="`${ageIndex == index ? 'liactive' : ''}`"
  479. @click.stop="
  480. ;(ageIndex = index),
  481. (studentSignUp.maxAge = item.maxAge),
  482. (studentSignUp.minAge = item.minAge),
  483. (showAge = !showAge),
  484. changeValue('maxAge')
  485. "
  486. >
  487. {{ item.label }}
  488. </li>
  489. </ul>
  490. </div>
  491. </div>
  492. <p v-if="formErrors.age" class="error">
  493. {{ formErrors.age }}
  494. </p>
  495. </div>
  496. <div class="col-lg-6 wow fadeInUp" data-wow-delay=".6s">
  497. <div @click.stop="showCountryId = !showCountryId" class="form-clt">
  498. <span>
  499. {{ $t('region') }}
  500. <span style="color: red">*</span>
  501. </span>
  502. <div class="w-100 h-100 input">
  503. <input
  504. type="text"
  505. v-model="studentSignUp.countryId"
  506. name="countryId"
  507. id="countryId"
  508. readonly
  509. :placeholder="$t('pleaseSelectRegion')"
  510. />
  511. <i
  512. :class="`fas iclass ${showCountryId ? 'fa-angle-up' : 'fa-angle-down'}`"
  513. ></i>
  514. <ul v-show="showCountryId" class="submenu">
  515. <li
  516. v-for="(item, index) in areaCodeLang"
  517. :key="item.id"
  518. :class="`${studentSignUp.countryId == item.label ? 'liactive' : ''}`"
  519. @click.stop="
  520. ;(studentSignUp.countryId = item.label),
  521. (showCountryId = !showCountryId),
  522. changeValue('countryId')
  523. "
  524. >
  525. {{ item.label }}
  526. </li>
  527. </ul>
  528. </div>
  529. </div>
  530. <p v-if="formErrors.countryId" class="error">
  531. {{ formErrors.countryId }}
  532. </p>
  533. </div>
  534. <div class="col-lg-6 wow fadeInUp" data-wow-delay=".7s">
  535. <div class="form-clt">
  536. <span>
  537. {{ $t('phone') }}
  538. <span style="color: red">*</span>
  539. </span>
  540. <div style="flex-wrap: nowrap" class="d-flex">
  541. <div
  542. @click.stop="showAreaCode = !showAreaCode"
  543. style="box-sizing: border-box; position: relative; max-width: 150px"
  544. >
  545. <input
  546. style="width: 100%"
  547. type="text"
  548. v-model="studentSignUp.areaCode"
  549. name="areaCode"
  550. id="areaCode"
  551. readonly
  552. :placeholder="$t('pleaseSelect')"
  553. />
  554. <i
  555. :class="`fas iclass ${
  556. showAreaCode ? 'fa-angle-up' : 'fa-angle-down'
  557. }`"
  558. style="right: 10%"
  559. ></i>
  560. <ul v-show="showAreaCode" style="position: absolute" class="submenu">
  561. <li
  562. v-for="(item, index) in areaCodeLang"
  563. :key="item.value"
  564. :class="`${
  565. studentSignUp.areaCode == '+' + item.value ? 'liactive' : ''
  566. }`"
  567. @click.stop="
  568. ;(studentSignUp.areaCode = '+' + item.value),
  569. (showAreaCode = !showAreaCode)
  570. "
  571. >
  572. {{ item.label }}({{ item.value }})
  573. </li>
  574. </ul>
  575. </div>
  576. <input
  577. style="margin-left: 4%"
  578. type="text"
  579. v-model="studentSignUp.phone"
  580. name="phone"
  581. id="phone"
  582. :placeholder="$t('pleaseEnter')"
  583. @blur="changeValue('phone')"
  584. />
  585. </div>
  586. <p v-if="formErrors.phone || formErrors.areaCode" class="error">
  587. <span style="color: red; font-size: 14px; margin-right: 15%">
  588. {{ formErrors.areaCode }}
  589. </span>
  590. {{ formErrors.phone }}
  591. </p>
  592. </div>
  593. </div>
  594. <div class="col-lg-6 wow fadeInUp" data-wow-delay=".8s">
  595. <div @click.stop="showCourseType = !showCourseType" class="form-clt">
  596. <span>
  597. {{ $t('courseCategory') }}
  598. <span style="color: red">*</span>
  599. </span>
  600. <div class="w-100 h-100 input">
  601. <input
  602. type="text"
  603. v-model="studentSignUp.courseType"
  604. name="courseType"
  605. id="courseType"
  606. readonly
  607. :placeholder="$t('pleaseSelectCourseType')"
  608. />
  609. <!-- @blur="changeValue('courseType')" -->
  610. <i
  611. :class="`fas iclass ${
  612. showCourseType ? 'fa-angle-up' : 'fa-angle-down'
  613. }`"
  614. ></i>
  615. <ul v-show="showCourseType" class="submenu">
  616. <li
  617. v-for="(item, index) in courseTypeList"
  618. :key="item.value"
  619. :class="`${studentSignUp.courseType == item.label ? 'liactive' : ''}`"
  620. @click.stop="
  621. ;(studentSignUp.courseType = item.label),
  622. (showCourseType = !showCourseType),
  623. changeValue('courseType')
  624. "
  625. >
  626. {{ item.label }}
  627. </li>
  628. </ul>
  629. </div>
  630. </div>
  631. <p v-if="formErrors.courseType" class="error">
  632. {{ formErrors.courseType }}
  633. </p>
  634. </div>
  635. <div class="col-lg-12 wow fadeInUp" data-wow-delay=".9s">
  636. <div class="form-clt">
  637. <span>
  638. {{ $t('yourEmail') }}
  639. <span style="color: red">*</span>
  640. </span>
  641. <input
  642. type="text"
  643. v-model="studentSignUp.email"
  644. name="email"
  645. id="email"
  646. :placeholder="$t('pleaseEnterEmail')"
  647. @blur="changeValue('email')"
  648. />
  649. <p v-if="formErrors.email" class="error">
  650. {{ formErrors.email }}
  651. </p>
  652. </div>
  653. </div>
  654. <div
  655. class="col-lg-12 wow fadeInUp d-flex justify-content-center align-items-center"
  656. data-wow-delay=".9s"
  657. >
  658. <button
  659. type="submit"
  660. :disabled="btnLoading"
  661. class="theme-btn"
  662. style="border-radius: 5px"
  663. >
  664. {{ $t('sendMessage') }}
  665. <i class="fa-solid fa-arrow-right-long"></i>
  666. </button>
  667. </div>
  668. </div>
  669. </form>
  670. </div>
  671. </div>
  672. </div>
  673. </div>
  674. </div>
  675. </section>
  676. <FooterSection></FooterSection>
  677. </template>
  678. </template>