index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. <template>
  2. <view class="interview-container">
  3. <!-- 顶部导航栏 -->
  4. <!-- 面试信息 -->
  5. <!-- <view class="interview-info">
  6. <view class="info-item">
  7. <text class="label">面试岗位:</text>
  8. <text class="value">前端岗位</text>
  9. </view>
  10. <view class="info-item">
  11. <text class="label">面试岗位:</text>
  12. <text class="value">2024年06月28日 14:48</text>
  13. </view>
  14. <view class="info-item">
  15. <text class="value">2024年06月28日 16:47</text>
  16. </view>
  17. <view class="info-item">
  18. <text class="label">公司全称:</text>
  19. <text class="value">敏龙科技集团有限公司</text>
  20. </view>
  21. </view> -->
  22. <!-- 职位列表 -->
  23. <view class="job-list-container" v-if="!userInfoFilled">
  24. <view class="job-list-title">可选职位列表</view>
  25. <view class="job-list">
  26. <view v-for="(job, index) in jobList" :key="index" class="job-item"
  27. :class="{'job-selected': selectedJobId === job.id}" @click="selectJob(job)">
  28. <view class="job-name">{{job.title}}</view>
  29. <view class="job-details">
  30. <text class="job-salary">{{job.publish_date}}</text>
  31. <text class="job-location">{{job.location}}</text>
  32. </view>
  33. </view>
  34. </view>
  35. <button class="apply-btn" :disabled="!selectedJobId" @click="applyForJob">申请面试</button>
  36. </view>
  37. <!-- 表单信息 -->
  38. <view class="form-container" v-if="userInfoFilled">
  39. <view class="form-title">填写报名信息</view>
  40. <view class="form-subtitle">如确认报名(此次面试),请填写以下信息</view>
  41. <view class="form-item">
  42. <text class="form-label">姓名 <text class="required">*</text></text>
  43. <input type="text" v-model="formData.name" placeholder="请输入姓名" />
  44. </view>
  45. <view class="form-item">
  46. <text class="form-label">性别 <text class="required">*</text></text>
  47. <view class="radio-group">
  48. <view class="radio-item" @click="formData.gender = '男'">
  49. <view class="radio-circle" :class="{'radio-selected': formData.gender === '男'}"></view>
  50. <text class="radio-text">男</text>
  51. </view>
  52. <view class="radio-item" @click="formData.gender = '女'">
  53. <view class="radio-circle" :class="{'radio-selected': formData.gender === '女'}"></view>
  54. <text class="radio-text">女</text>
  55. </view>
  56. </view>
  57. </view>
  58. <view class="form-item">
  59. <text class="form-label">身份证号 <text class="required">*</text></text>
  60. <input type="text" v-model="formData.idCard" placeholder="请输入有效身份证号" maxlength="18" />
  61. </view>
  62. <view class="form-item">
  63. <text class="form-label">手机号 <text class="required">*</text></text>
  64. <input type="number" v-model="formData.phone" placeholder="请输入手机号" maxlength="11" />
  65. </view>
  66. <view class="form-item">
  67. <text class="form-label">紧急联系人 <text class="required">*</text></text>
  68. <input type="text" v-model="formData.emergencyContact" placeholder="请输入紧急联系人姓名" />
  69. </view>
  70. <view class="form-item">
  71. <text class="form-label">紧急联系人电话 <text class="required">*</text></text>
  72. <input type="number" v-model="formData.emergencyPhone" placeholder="请输入紧急联系人电话" maxlength="11" />
  73. </view>
  74. <view class="form-item">
  75. <text class="form-label">与紧急联系人关系 <text class="required">*</text></text>
  76. <picker @change="relationChange" :value="relationIndex" :range="relationOptions">
  77. <view class="picker-view">
  78. <text>{{formData.relation || '请选择关系'}}</text>
  79. <text class="picker-arrow">▼</text>
  80. </view>
  81. </picker>
  82. </view>
  83. <view class="agreement">
  84. <checkbox :checked="isAgreed" @tap="toggleAgreement" color="#6c5ce7" />
  85. <text class="agreement-text">
  86. 我已阅读并同意
  87. <text class="agreement-link">《身份验证服务协议》</text>
  88. <text class="agreement-link">《隐私保护政策》</text>
  89. <text class="agreement-link">《网络安全协议》</text>
  90. </text>
  91. </view>
  92. <button class="submit-btn" :disabled="!canSubmit" @click="submitForm">提交</button>
  93. </view>
  94. </view>
  95. </template>
  96. <script>
  97. import {
  98. fillUserInfo,
  99. getUserInfo,
  100. getJobList
  101. } from '@/api/user';
  102. export default {
  103. data() {
  104. return {
  105. formData: {
  106. name: '',
  107. gender: '',
  108. phone: '',
  109. email: '',
  110. idCard: '',
  111. emergencyContact: '',
  112. emergencyPhone: '',
  113. relation: ''
  114. },
  115. relationOptions: ['父母', '配偶', '子女', '兄弟姐妹', '朋友', '其他'],
  116. relationIndex: 0,
  117. isAgreed: false,
  118. userInfoFilled: false,
  119. jobList: [],
  120. selectedJobId: null,
  121. selectedJob: null
  122. }
  123. },
  124. onLoad() {
  125. this.checkUserInfo();
  126. this.fetchJobList();
  127. },
  128. computed: {
  129. canSubmit() {
  130. return this.formData.name.trim() &&
  131. this.formData.gender &&
  132. this.formData.phone.trim() &&
  133. (/^1\d{10}$/.test(this.formData.phone)) &&
  134. (!this.formData.email || /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(this.formData.email)) &&
  135. this.formData.idCard.trim() &&
  136. this.formData.emergencyContact.trim() &&
  137. this.formData.emergencyPhone.trim() &&
  138. (/^1\d{10}$/.test(this.formData.emergencyPhone)) &&
  139. this.formData.relation &&
  140. this.isAgreed;
  141. }
  142. },
  143. methods: {
  144. goHome() {
  145. uni.navigateBack({
  146. delta: 1
  147. });
  148. },
  149. toggleAgreement() {
  150. this.isAgreed = !this.isAgreed;
  151. },
  152. relationChange(e) {
  153. this.relationIndex = e.detail.value;
  154. this.formData.relation = this.relationOptions[this.relationIndex];
  155. },
  156. checkUserInfo() {
  157. uni.showLoading({
  158. title: '加载中...'
  159. });
  160. console.log('id:', JSON.parse(uni.getStorageSync('userInfo')).id);
  161. getUserInfo(JSON.parse(uni.getStorageSync('userInfo')).id)
  162. .then(res => {
  163. uni.hideLoading();
  164. if (res.code === 200 && res.data) {
  165. const userData = res.data;
  166. if (userData.name && userData.phone) {
  167. this.userInfoFilled = true;
  168. this.formData.name = userData.name || '';
  169. this.formData.gender = userData.gender || '';
  170. this.formData.phone = userData.phone || '';
  171. this.formData.idCard = userData.id_card || '';
  172. this.formData.emergencyContact = userData.emergency_contact || '';
  173. this.formData.emergencyPhone = userData.emergency_phone || '';
  174. this.formData.relation = userData.relation || '';
  175. if (userData.relation) {
  176. const index = this.relationOptions.findIndex(item => item === userData.relation);
  177. if (index !== -1) {
  178. this.relationIndex = index;
  179. }
  180. }
  181. uni.navigateTo({
  182. url: '/pages/success/success'
  183. });
  184. }
  185. }
  186. })
  187. .catch(err => {
  188. uni.hideLoading();
  189. console.error('获取用户信息失败:', err);
  190. uni.showToast({
  191. title: '获取用户信息失败',
  192. icon: 'none'
  193. });
  194. });
  195. },
  196. fetchJobList() {
  197. uni.showLoading({
  198. title: '加载职位列表...'
  199. });
  200. getJobList()
  201. .then(res => {
  202. uni.hideLoading();
  203. console.log(res);
  204. this.jobList = res;
  205. })
  206. .catch(err => {
  207. uni.hideLoading();
  208. console.error('获取职位列表失败:', err);
  209. uni.showToast({
  210. title: '网络错误,请稍后重试',
  211. icon: 'none'
  212. });
  213. });
  214. },
  215. selectJob(job) {
  216. this.selectedJobId = job.id;
  217. this.selectedJob = job;
  218. },
  219. applyForJob() {
  220. if (!this.selectedJobId) {
  221. uni.showToast({
  222. title: '请选择一个职位',
  223. icon: 'none'
  224. });
  225. return;
  226. }
  227. // 保存所选职位信息
  228. uni.setStorageSync('selectedJob', JSON.stringify(this.selectedJob));
  229. uni.navigateTo({
  230. url: '/pages/interview-notice/interview-notice',
  231. fail: (err) => {
  232. console.error('页面跳转失败:', err);
  233. uni.showToast({
  234. title: '页面跳转失败',
  235. icon: 'none'
  236. });
  237. }
  238. });
  239. // 显示填写个人信息表单
  240. // this.userInfoFilled = true;
  241. },
  242. submitForm() {
  243. if (!this.formData.name.trim()) {
  244. uni.showToast({
  245. title: '请输入姓名',
  246. icon: 'none'
  247. });
  248. return;
  249. }
  250. if (!this.formData.gender) {
  251. uni.showToast({
  252. title: '请选择性别',
  253. icon: 'none'
  254. });
  255. return;
  256. }
  257. if (!this.formData.phone.trim()) {
  258. uni.showToast({
  259. title: '请输入手机号',
  260. icon: 'none'
  261. });
  262. return;
  263. }
  264. if (!/^1\d{10}$/.test(this.formData.phone)) {
  265. uni.showToast({
  266. title: '请输入正确的手机号',
  267. icon: 'none'
  268. });
  269. return;
  270. }
  271. if (this.formData.email && !/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(this.formData.email)) {
  272. uni.showToast({
  273. title: '请输入正确的邮箱',
  274. icon: 'none'
  275. });
  276. return;
  277. }
  278. if (!this.formData.idCard.trim()) {
  279. uni.showToast({
  280. title: '请输入身份证号',
  281. icon: 'none'
  282. });
  283. return;
  284. }
  285. const idCardReg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
  286. if (!idCardReg.test(this.formData.idCard)) {
  287. uni.showToast({
  288. title: '请输入正确的身份证号',
  289. icon: 'none'
  290. });
  291. return;
  292. }
  293. if (!this.formData.emergencyContact.trim()) {
  294. uni.showToast({
  295. title: '请输入紧急联系人',
  296. icon: 'none'
  297. });
  298. return;
  299. }
  300. if (!this.formData.emergencyPhone.trim()) {
  301. uni.showToast({
  302. title: '请输入紧急联系人电话',
  303. icon: 'none'
  304. });
  305. return;
  306. }
  307. if (!/^1\d{10}$/.test(this.formData.emergencyPhone)) {
  308. uni.showToast({
  309. title: '请输入正确的紧急联系人电话',
  310. icon: 'none'
  311. });
  312. return;
  313. }
  314. if (!this.formData.relation) {
  315. uni.showToast({
  316. title: '请选择与紧急联系人关系',
  317. icon: 'none'
  318. });
  319. return;
  320. }
  321. if (!this.isAgreed) {
  322. uni.showToast({
  323. title: '请阅读并同意相关协议',
  324. icon: 'none'
  325. });
  326. return;
  327. }
  328. const submitData = {
  329. openid: JSON.parse(uni.getStorageSync('userInfo')).openid || '',
  330. name: this.formData.name,
  331. phone: this.formData.phone,
  332. id_card: this.formData.idCard,
  333. status: 1,
  334. source: 'mini',
  335. examine: 0,
  336. tenant_id: '1',
  337. emergency_contact: this.formData.emergencyContact,
  338. emergency_phone: this.formData.emergencyPhone,
  339. relation: this.formData.relation,
  340. age: '20',
  341. job_id: this.selectedJobId
  342. };
  343. uni.showLoading({
  344. title: '提交中...'
  345. });
  346. fillUserInfo(submitData)
  347. .then(res => {
  348. uni.hideLoading();
  349. this.updateLocalUserInfo();
  350. uni.showToast({
  351. title: '提交成功',
  352. icon: 'success',
  353. duration: 1500,
  354. success: () => {
  355. setTimeout(() => {
  356. uni.navigateTo({
  357. url: '/pages/success/success',
  358. fail: (err) => {
  359. console.error('页面跳转失败:', err);
  360. uni.showToast({
  361. title: '页面跳转失败',
  362. icon: 'none'
  363. });
  364. }
  365. });
  366. }, 1500);
  367. }
  368. });
  369. // } else {
  370. // uni.showToast({
  371. // title: res.msg || '提交失败,请重试',
  372. // icon: 'none'
  373. // });
  374. // }
  375. })
  376. .catch(err => {
  377. uni.hideLoading();
  378. console.error('提交表单失败:', err);
  379. uni.showToast({
  380. title: '网络错误,请稍后重试',
  381. icon: 'none'
  382. });
  383. });
  384. },
  385. updateLocalUserInfo() {
  386. getUserInfo()
  387. .then(res => {
  388. if (res.code === 200 && res.data) {
  389. let userInfo = {};
  390. try {
  391. userInfo = JSON.parse(uni.getStorageSync('userInfo') || '{}');
  392. } catch (e) {
  393. console.error('解析本地存储用户信息失败:', e);
  394. userInfo = {};
  395. }
  396. const updatedUserInfo = {
  397. ...userInfo,
  398. ...res.data
  399. };
  400. uni.setStorageSync('userInfo', JSON.stringify(updatedUserInfo));
  401. }
  402. })
  403. .catch(err => {
  404. console.error('更新本地用户信息失败:', err);
  405. });
  406. }
  407. }
  408. }
  409. </script>
  410. <style>
  411. .interview-container {
  412. display: flex;
  413. flex-direction: column;
  414. min-height: 100vh;
  415. background-color: #f5f7fa;
  416. }
  417. .nav-bar {
  418. display: flex;
  419. justify-content: space-between;
  420. align-items: center;
  421. padding: 20rpx 30rpx;
  422. background-color: #6c5ce7;
  423. color: #fff;
  424. }
  425. .scan-btn {
  426. display: flex;
  427. flex-direction: column;
  428. align-items: center;
  429. background-color: #ff9f43;
  430. padding: 10rpx 30rpx;
  431. border-radius: 30rpx;
  432. font-size: 24rpx;
  433. }
  434. .interview-info {
  435. background-color: #6c5ce7;
  436. color: #fff;
  437. padding: 20rpx 30rpx 40rpx;
  438. }
  439. .info-item {
  440. margin: 10rpx 0;
  441. font-size: 28rpx;
  442. }
  443. .label {
  444. margin-right: 10rpx;
  445. }
  446. .form-container {
  447. flex: 1;
  448. background-color: #fff;
  449. border-radius: 20rpx 20rpx 0 0;
  450. margin-top: -20rpx;
  451. padding: 40rpx 30rpx;
  452. }
  453. .form-title {
  454. font-size: 32rpx;
  455. font-weight: bold;
  456. margin-bottom: 10rpx;
  457. }
  458. .form-subtitle {
  459. font-size: 24rpx;
  460. color: #999;
  461. margin-bottom: 40rpx;
  462. }
  463. .form-item {
  464. margin-bottom: 30rpx;
  465. }
  466. .form-label {
  467. display: block;
  468. font-size: 28rpx;
  469. margin-bottom: 10rpx;
  470. }
  471. .required {
  472. color: #ff4757;
  473. }
  474. input {
  475. width: 100%;
  476. height: 80rpx;
  477. border: 1px solid #eee;
  478. border-radius: 8rpx;
  479. padding: 0 20rpx;
  480. font-size: 28rpx;
  481. box-sizing: border-box;
  482. }
  483. .submit-btn {
  484. width: 100%;
  485. height: 90rpx;
  486. line-height: 90rpx;
  487. background-color: #6c5ce7;
  488. color: #fff;
  489. border-radius: 45rpx;
  490. font-size: 32rpx;
  491. margin-top: 60rpx;
  492. }
  493. .agreement {
  494. display: flex;
  495. align-items: flex-start;
  496. margin-top: 20rpx;
  497. }
  498. .agreement-text {
  499. font-size: 24rpx;
  500. color: #666;
  501. line-height: 1.5;
  502. margin-left: 10rpx;
  503. }
  504. .agreement-link {
  505. color: #6c5ce7;
  506. }
  507. .submit-btn[disabled] {
  508. background-color: #b2b2b2;
  509. color: #fff;
  510. }
  511. .radio-group {
  512. display: flex;
  513. flex-direction: row;
  514. margin-top: 10rpx;
  515. }
  516. .radio-item {
  517. display: flex;
  518. align-items: center;
  519. margin-right: 60rpx;
  520. }
  521. .radio-circle {
  522. width: 36rpx;
  523. height: 36rpx;
  524. border-radius: 50%;
  525. border: 2rpx solid #999;
  526. margin-right: 10rpx;
  527. position: relative;
  528. }
  529. .radio-selected {
  530. border-color: #6c5ce7;
  531. }
  532. .radio-selected:after {
  533. content: '';
  534. position: absolute;
  535. width: 24rpx;
  536. height: 24rpx;
  537. background-color: #6c5ce7;
  538. border-radius: 50%;
  539. top: 50%;
  540. left: 50%;
  541. transform: translate(-50%, -50%);
  542. }
  543. .radio-text {
  544. font-size: 28rpx;
  545. }
  546. .picker-view {
  547. width: 100%;
  548. height: 80rpx;
  549. border: 1px solid #eee;
  550. border-radius: 8rpx;
  551. padding: 0 20rpx;
  552. font-size: 28rpx;
  553. box-sizing: border-box;
  554. display: flex;
  555. align-items: center;
  556. justify-content: space-between;
  557. }
  558. .picker-arrow {
  559. font-size: 24rpx;
  560. color: #999;
  561. }
  562. /* 职位列表样式 */
  563. .job-list-container {
  564. flex: 1;
  565. background-color: #fff;
  566. border-radius: 20rpx 20rpx 0 0;
  567. margin-top: -20rpx;
  568. padding: 40rpx 30rpx;
  569. }
  570. .job-list-title {
  571. font-size: 32rpx;
  572. font-weight: bold;
  573. margin-bottom: 30rpx;
  574. }
  575. .job-list {
  576. max-height: 800rpx;
  577. overflow-y: auto;
  578. }
  579. .job-item {
  580. padding: 30rpx;
  581. border-radius: 12rpx;
  582. background-color: #f8f9fa;
  583. margin-bottom: 20rpx;
  584. border: 2rpx solid transparent;
  585. }
  586. .job-selected {
  587. border-color: #6c5ce7;
  588. background-color: rgba(108, 92, 231, 0.1);
  589. }
  590. .job-name {
  591. font-size: 30rpx;
  592. font-weight: bold;
  593. margin-bottom: 10rpx;
  594. }
  595. .job-details {
  596. display: flex;
  597. justify-content: space-between;
  598. font-size: 24rpx;
  599. color: #666;
  600. }
  601. .job-salary {
  602. color: #ff6b6b;
  603. }
  604. .apply-btn {
  605. width: 100%;
  606. height: 90rpx;
  607. line-height: 90rpx;
  608. background-color: #6c5ce7;
  609. color: #fff;
  610. border-radius: 45rpx;
  611. font-size: 32rpx;
  612. margin-top: 40rpx;
  613. }
  614. .apply-btn[disabled] {
  615. background-color: #b2b2b2;
  616. color: #fff;
  617. }
  618. </style>