木邻有你前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

616 lines
17 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <view class="volunteer-apply-page">
  3. <!-- 头部背景图 -->
  4. <view class="header-section">
  5. <image src="../static/volunteer_bg@2x.png" class="bg-image" mode="aspectFit"></image>
  6. </view>
  7. <!-- 提示信息 -->
  8. <view class="tip-section">
  9. <uv-icon name="info-circle-fill" color="#1488DB" size="32"></uv-icon>
  10. <text class="tip-text">以下内容均为必填项请根据您的实际情况认真填写</text>
  11. </view>
  12. <!-- 表单内容 -->
  13. <!-- 如何一键禁用整个表单元素 可不可以设置一个大容器 直接禁止内容的任何点击 -->
  14. <view class="form-section">
  15. <view v-if="status === '0' || status === '1'" class="overlay"></view>
  16. <uv-form ref="form" :model="formData" :rules="rules" labelPosition="left" labelWidth="120">
  17. <!-- 姓名 -->
  18. <uv-form-item label="姓名" prop="name" borderBottom>
  19. <uv-input
  20. v-model="formData.name"
  21. placeholder="请输入您的姓名"
  22. border="none"
  23. clearable
  24. ></uv-input>
  25. </uv-form-item>
  26. <!-- 手机号 -->
  27. <uv-form-item label="手机号" prop="phone" borderBottom>
  28. <uv-input
  29. v-model="formData.phone"
  30. placeholder="请输入您的手机号"
  31. border="none"
  32. clearable
  33. type="number"
  34. ></uv-input>
  35. </uv-form-item>
  36. <!-- 性别 -->
  37. <uv-form-item label="性别" prop="sex" borderBottom @click="openGenderPicker">
  38. <uv-input
  39. v-model="formData.sex"
  40. placeholder="请选择"
  41. border="none"
  42. readonly
  43. suffixIcon="arrow-right"
  44. ></uv-input>
  45. </uv-form-item>
  46. <!-- 所在地区 -->
  47. <uv-form-item label="所在地区" prop="area" borderBottom @click="openRegionPicker">
  48. <uv-input
  49. v-model="formData.area"
  50. placeholder="请选择"
  51. border="none"
  52. readonly
  53. suffixIcon="arrow-right"
  54. ></uv-input>
  55. </uv-form-item>
  56. <!-- 详细地址 -->
  57. <uv-form-item label="详细地址" prop="address" borderBottom>
  58. <uv-input
  59. v-model="formData.address"
  60. placeholder="请输入详细地址"
  61. border="none"
  62. clearable
  63. ></uv-input>
  64. </uv-form-item>
  65. <!-- 职业类型 -->
  66. <uv-form-item label="职业类型" prop="career" borderBottom @click="openProfessionPicker">
  67. <uv-input
  68. v-model="formData.career"
  69. placeholder="请选择"
  70. border="none"
  71. readonly
  72. suffixIcon="arrow-right"
  73. ></uv-input>
  74. </uv-form-item>
  75. <!-- 最高学历 -->
  76. <uv-form-item label="最高学历" prop="qualifications" borderBottom @click="openEducationPicker">
  77. <uv-input
  78. v-model="formData.qualifications"
  79. placeholder="请选择"
  80. border="none"
  81. readonly
  82. suffixIcon="arrow-right"
  83. ></uv-input>
  84. </uv-form-item>
  85. <!-- 技能特长 -->
  86. <uv-form-item label="技能特长" prop="skill">
  87. <uv-textarea
  88. v-model="formData.skill"
  89. placeholder="请输入您的技能特长"
  90. border="none"
  91. :maxlength="200"
  92. count
  93. height="120"
  94. ></uv-textarea>
  95. </uv-form-item>
  96. </uv-form>
  97. <!-- 紧急联系人信息 -->
  98. <view class="emergency-section">
  99. <view class="section-title">紧急联系人信息</view>
  100. <uv-form ref="emergencyForm" :model="emergencyData" labelPosition="left" labelWidth="120">
  101. <!-- 联系人姓名 -->
  102. <uv-form-item label="姓名" prop="emergencyName" borderBottom>
  103. <uv-input
  104. v-model="emergencyData.emergencyName"
  105. placeholder="请输入您的紧急联系人姓名"
  106. border="none"
  107. clearable
  108. ></uv-input>
  109. </uv-form-item>
  110. <!-- 联系人手机号 -->
  111. <uv-form-item label="手机号" prop="emergencyPhone" borderBottom>
  112. <uv-input
  113. v-model="emergencyData.emergencyPhone"
  114. placeholder="请输入您的紧急联系人手机号"
  115. border="none"
  116. clearable
  117. type="number"
  118. ></uv-input>
  119. </uv-form-item>
  120. </uv-form>
  121. </view>
  122. </view>
  123. <!-- 提交按钮 -->
  124. <view class="submit-section">
  125. <!-- 圆角的 -->
  126. <!-- 如果还在审核中 按钮无法点击 -->
  127. <uv-button
  128. v-if="status !== '1'"
  129. type="primary"
  130. shape="circle"
  131. size="large"
  132. :loading="submitting"
  133. :disabled="status === '0'"
  134. @click="submitApplication"
  135. >
  136. {{statusTips}}
  137. </uv-button>
  138. </view>
  139. <!-- 性别选择器 -->
  140. <uv-picker
  141. ref="genderPicker"
  142. :columns="genderOptions"
  143. @confirm="onGenderConfirm"
  144. ></uv-picker>
  145. <!-- 地区选择器 -->
  146. <uv-picker
  147. ref="regionPicker"
  148. :columns="addressList"
  149. @confirm="onAddressConfirm"
  150. @change="onAddressChange"
  151. keyName="name"
  152. ></uv-picker>
  153. <!-- 职业选择器 -->
  154. <uv-picker
  155. ref="professionPicker"
  156. :columns="professionOptions"
  157. @confirm="onProfessionConfirm"
  158. ></uv-picker>
  159. <!-- 学历选择器 -->
  160. <uv-picker
  161. ref="educationPicker"
  162. :columns="educationOptions"
  163. @confirm="onEducationConfirm"
  164. ></uv-picker>
  165. </view>
  166. </template>
  167. <script>
  168. import chinaRegions from '@/static/china-regions.json'
  169. export default {
  170. name: 'VolunteerApply',
  171. data() {
  172. return {
  173. submitting: false,
  174. formData: {
  175. name: '',
  176. phone: '',
  177. sex: '',
  178. // sexText: '',
  179. area: '',
  180. address: '',
  181. career: '',
  182. // careerText: '',
  183. qualifications: '',
  184. // qualificationsText: '',
  185. skill: ''
  186. },
  187. emergencyData: {
  188. emergencyName: '',
  189. emergencyPhone: ''
  190. },
  191. rules: {
  192. name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
  193. phone: [
  194. { required: true, message: '请输入手机号', trigger: 'blur' },
  195. { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
  196. ],
  197. sex: [{ required: true, message: '请选择性别', trigger: 'change' }],
  198. area: [{ required: true, message: '请选择所在地区', trigger: 'change' }],
  199. address: [{ required: true, message: '请输入详细地址', trigger: 'blur' }],
  200. career: [{ required: true, message: '请选择职业类型', trigger: 'change' }],
  201. qualifications: [{ required: true, message: '请选择最高学历', trigger: 'change' }]
  202. },
  203. // 页面的志愿者状态 0-审核中 1-已通过 2-未通过 3-未申请
  204. status: '3',
  205. genderOptions: [['男', '女']],
  206. provinces: [], //省
  207. citys: [], //市
  208. areas: [], //区
  209. pickerValue: [0, 0, 0],
  210. defaultValue: [3442, 1, 2],
  211. }
  212. },
  213. computed: {
  214. addressList() {
  215. return [this.provinces, this.citys, this.areas];
  216. },
  217. professionOptions() {
  218. return [this.$store.state.careerList.map(item => item.title)]
  219. },
  220. educationOptions() {
  221. return [this.$store.state.qualificationList.map(item => item.title)]
  222. },
  223. // 不同审核状态对应的提示语句
  224. statusTips() {
  225. if (this.status === '0') {
  226. return '您的申请正在审核中,请耐心等待。'
  227. } else if (this.status === '1') {
  228. return '恭喜您,志愿者申请已通过!'
  229. } else if (this.status === '2') {
  230. return '申请未通过,重新提交申请'
  231. } else if (this.status === '3') {
  232. return '申请志愿者'
  233. }
  234. }
  235. },
  236. async onLoad(options) {
  237. // 如果是编辑模式,加载已有数据
  238. if (options.edit && options.data) {
  239. this.loadExistingData(JSON.parse(decodeURIComponent(options.data)));
  240. }
  241. // 查询志愿者信息情况
  242. const {result:info} = await this.$api.home.queryVolunteer()
  243. // 审核状态为0或者1时 禁用整个表单元素
  244. // 审核状态为2时 只禁用提交按钮
  245. // 审核状态为3时 不禁用
  246. if (info !== {} && info.status !== null && info.status !== undefined ){
  247. // 如果有值了 说明申请过志愿者
  248. this.status = info.status
  249. // 回显数据
  250. this.formData = {
  251. name: info.name,
  252. phone: info.phone,
  253. sex: info.sex,
  254. // sexText: info.sex,
  255. area: info.area,
  256. address: info.address,
  257. career: info.careerName,
  258. // careerText: info.career,
  259. qualifications: info.qualificationsName,
  260. // qualificationsText: info.qualifications,
  261. skill: info.skill
  262. }
  263. // 回显紧急联系人
  264. this.emergencyData = {
  265. emergencyName: info.emergencyName,
  266. emergencyPhone: info.emergencyPhone
  267. }
  268. }else{
  269. this.status = '3'
  270. }
  271. },
  272. created() {
  273. this.getAddressData()
  274. // 调试store数据
  275. // console.log('Store careerList:', this.$store.state.careerList)
  276. // console.log('Store qualificationList:', this.$store.state.qualificationList)
  277. },
  278. methods: {
  279. // 加载已有数据(编辑模式)
  280. loadExistingData(data) {
  281. this.formData = {
  282. name: data.name || '李双欢',
  283. phone: data.phone || '15478451233',
  284. sex: data.sex || '男',
  285. // sexText: data.sex || '男',
  286. area: data.area || '湖南省长沙市区',
  287. areaText: data.area || '湖南省长沙市区',
  288. address: data.address || '阳光小区45栋二单元1203',
  289. career: data.career || '专业技术人员',
  290. // careerText: data.career || '专业技术人员',
  291. qualifications: data.qualifications || '本科',
  292. // qualificationsText: data.qualifications || '本科',
  293. skill: data.skill || '计算机、跑步'
  294. };
  295. this.emergencyData = {
  296. emergencyName: data.emergencyName || '李四',
  297. emergencyPhone: data.emergencyPhone || '14563236320'
  298. };
  299. },
  300. // 初始化地区数据
  301. getAddressData() {
  302. console.log('开始加载地区数据');
  303. try {
  304. // 直接使用导入的地区数据(已简化,只包含name字段)
  305. this.provinces = chinaRegions;
  306. // console.log('成功加载地区数据,省份数量:', this.provinces.length);
  307. this.handlePickValueDefault();
  308. // uni.showToast({
  309. // title: '地区数据加载成功',
  310. // icon: 'success'
  311. // });
  312. } catch (error) {
  313. // console.error('加载地区数据失败:', error);
  314. // uni.showToast({
  315. // title: '地区数据加载失败',
  316. // icon: 'error'
  317. // });
  318. }
  319. },
  320. handlePickValueDefault() {
  321. if (this.provinces.length > 0) {
  322. // 设置省(默认选择第一个省份)
  323. this.pickerValue[0] = 0;
  324. // 设置市(默认选择第一个市)
  325. this.citys = this.provinces[0]?.children || [];
  326. this.pickerValue[1] = 0;
  327. // 设置区(默认选择第一个区)
  328. this.areas = this.citys[0]?.children || [];
  329. this.pickerValue[2] = 0;
  330. console.log('初始化地区数据:', {
  331. provinces: this.provinces.length,
  332. citys: this.citys.length,
  333. areas: this.areas.length
  334. });
  335. }
  336. },
  337. // 打开性别选择器
  338. openGenderPicker() {
  339. // console.log('我点击了性别选择去');
  340. this.$refs.genderPicker.open();
  341. },
  342. // 打开地区选择器
  343. openRegionPicker() {
  344. this.$refs.regionPicker.open();
  345. },
  346. // 打开职业选择器
  347. openProfessionPicker() {
  348. this.$refs.professionPicker.open();
  349. },
  350. // 打开学历选择器
  351. openEducationPicker() {
  352. this.$refs.educationPicker.open();
  353. },
  354. // 性别选择确认
  355. onGenderConfirm(value) {
  356. this.formData.sex = value.value[0];
  357. },
  358. // 地区选择变化(三级联动)
  359. onAddressChange(e) {
  360. console.log('地区选择变化:', e);
  361. const { columnIndex, index, value } = e;
  362. if (columnIndex === 0) {
  363. // 选择省份时,更新市级数据
  364. this.citys = this.provinces[index]?.children || [];
  365. this.areas = this.citys[0]?.children || [];
  366. this.pickerValue = [index, 0, 0];
  367. } else if (columnIndex === 1) {
  368. // 选择市时,更新区级数据
  369. this.areas = this.citys[index]?.children || [];
  370. this.pickerValue[1] = index;
  371. this.pickerValue[2] = 0;
  372. } else if (columnIndex === 2) {
  373. // 选择区
  374. this.pickerValue[2] = index;
  375. }
  376. },
  377. // 地区选择确认
  378. onAddressConfirm(e) {
  379. console.log('确认选择的地区:', e);
  380. if (e.value && e.value.length >= 3) {
  381. const selectedArea = `${e.value[0].name}/${e.value[1].name}/${e.value[2].name}`;
  382. this.formData.area = selectedArea; // 给area字段赋值用于表单验证
  383. uni.showToast({
  384. icon: 'success',
  385. title: '地区选择成功'
  386. });
  387. } else {
  388. uni.showToast({
  389. icon: 'none',
  390. title: '请选择完整的省市区信息'
  391. });
  392. }
  393. },
  394. // 职业选择确认
  395. onProfessionConfirm(value) {
  396. console.log('职业选择确认:', value.value[0]);
  397. this.formData.career = value.value[0];
  398. },
  399. // 学历选择确认
  400. onEducationConfirm(value) {
  401. console.log('学历选择确认:', value.value[0]);
  402. this.formData.qualifications = value.value[0];
  403. },
  404. // 提交申请
  405. async submitApplication() {
  406. try {
  407. // 验证主表单
  408. const valid = await this.$refs.form.validate();
  409. if (!valid) return;
  410. // 验证紧急联系人信息
  411. if (!this.emergencyData.emergencyName || !this.emergencyData.emergencyPhone) {
  412. uni.showToast({
  413. title: '请填写紧急联系人信息',
  414. icon: 'none'
  415. });
  416. return;
  417. }
  418. // 电话格式的校验
  419. if (!this.$utils.checkPhone(this.formData.phone)) {
  420. uni.showToast({
  421. title: '请输入正确的电话号码',
  422. icon: 'none'
  423. });
  424. return;
  425. }
  426. // 电话格式的校验
  427. if (!this.$utils.checkPhone(this.emergencyData.emergencyPhone)) {
  428. uni.showToast({
  429. title: '请输入正确的紧急联系人电话',
  430. icon: 'none'
  431. });
  432. return;
  433. }
  434. this.submitting = true;
  435. // 模拟提交
  436. await this.submitVolunteerApplication();
  437. } catch (error) {
  438. console.error('提交失败:', error);
  439. uni.showToast({
  440. title: '提交失败,请重试',
  441. icon: 'none'
  442. });
  443. } finally {
  444. this.submitting = false;
  445. }
  446. },
  447. // 提交志愿者申请API
  448. async submitVolunteerApplication() {
  449. // 要求通过title获取id
  450. const career = this.$store.state.careerList.find(item => item.title === this.formData.career).id
  451. const qualifications = this.$store.state.qualificationList.find(item => item.title === this.formData.qualifications).id
  452. const res = await this.$api.home.applyVolunteer({...this.formData, ...this.emergencyData, career, qualifications})
  453. if (res.code === 200){
  454. uni.showToast({
  455. title: `${res.message}`,
  456. icon: 'none'
  457. })
  458. setTimeout(() => {
  459. uni.navigateBack();
  460. }, 1000);
  461. }else {
  462. uni.showToast({
  463. title: res.msg,
  464. icon: 'none'
  465. })
  466. }
  467. }
  468. }
  469. }
  470. </script>
  471. <style lang="scss" scoped>
  472. // @import '@/uni.scss';
  473. .overlay {
  474. position: absolute;
  475. inset: 0;
  476. width: 100%;
  477. height: 100%;
  478. background-color: rgba(255, 255, 255, 0.1);
  479. z-index: 10;
  480. }
  481. .volunteer-apply-page {
  482. min-height: 100vh;
  483. background-color: #f5f5f5;
  484. }
  485. .header-section {
  486. position: relative;
  487. width: 96%;
  488. height: 290rpx;
  489. margin: 25rpx auto;
  490. .bg-image {
  491. width: 100%;
  492. height: 100%;
  493. }
  494. }
  495. .tip-section {
  496. display: flex;
  497. align-items: center;
  498. padding: 30rpx;
  499. background-color: #fff;
  500. margin: 20rpx 30rpx;
  501. border-radius: 16rpx;
  502. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
  503. .tip-text {
  504. margin-left: 16rpx;
  505. font-size: 28rpx;
  506. color: #666;
  507. line-height: 1.5;
  508. }
  509. }
  510. .form-section {
  511. background-color: #fff;
  512. margin: 20rpx 30rpx;
  513. border-radius: 16rpx;
  514. padding: 40rpx 30rpx;
  515. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
  516. position: relative;
  517. }
  518. .emergency-section {
  519. margin-top: 40rpx;
  520. padding-top: 40rpx;
  521. border-top: 1rpx solid #f0f0f0;
  522. .section-title {
  523. font-size: 32rpx;
  524. font-weight: bold;
  525. color: #333;
  526. margin-bottom: 30rpx;
  527. }
  528. }
  529. .submit-section {
  530. padding: 40rpx 30rpx;
  531. padding-bottom: 60rpx;
  532. }
  533. // 覆盖uvUI样式
  534. :deep(.uv-form-item__body__right__content__input) {
  535. font-size: 28rpx !important;
  536. }
  537. :deep(.uv-form-item__body__left__text) {
  538. font-size: 28rpx !important;
  539. color: #333 !important;
  540. }
  541. :deep(.uv-input__content__field-wrapper__field) {
  542. font-size: 28rpx !important;
  543. }
  544. :deep(.uv-textarea__content__field) {
  545. font-size: 28rpx !important;
  546. }
  547. </style>