鸿宇研学生前端代码
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.

586 lines
16 KiB

  1. <template>
  2. <view>
  3. <uv-popup ref="popup" mode="bottom" bgColor="none" >
  4. <view class="popup__view">
  5. <view class="flex header">
  6. <view class="title">{{ title }}</view>
  7. <button class="btn" @click="close">关闭</button>
  8. </view>
  9. <view class="form">
  10. <uv-form
  11. ref="form"
  12. :model="form"
  13. :rules="rules"
  14. errorType="toast"
  15. >
  16. <view class="form-item">
  17. <uv-form-item prop="name" :customStyle="formItemStyle">
  18. <view class="form-item-label">
  19. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  20. 姓名
  21. </view>
  22. <view class="form-item-content">
  23. <formInput v-model="form.name"></formInput>
  24. </view>
  25. </uv-form-item>
  26. </view>
  27. <view class="form-item">
  28. <uv-form-item prop="cerNo" :customStyle="formItemStyle">
  29. <view class="form-item-label">
  30. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  31. 身份证号
  32. </view>
  33. <view class="form-item-content">
  34. <formInput v-model="form.cerNo"></formInput>
  35. </view>
  36. </uv-form-item>
  37. </view>
  38. <view class="form-item">
  39. <uv-form-item prop="type" :customStyle="formItemStyle">
  40. <view class="form-item-label">
  41. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  42. 类型
  43. </view>
  44. <view class="form-item-content">
  45. <uv-radio-group v-model="form.periodId"
  46. iconColor="#00A9FF"
  47. iconSize="36rpx"
  48. size="36rpx"
  49. labelColor="#181818"
  50. labelSize="26rpx"
  51. >
  52. <uv-radio
  53. v-for="(item, index) in periodOptions"
  54. :key="index"
  55. :label="item.label"
  56. :name="item.value"
  57. :customStyle="{ flex: 1 }"
  58. ></uv-radio>
  59. </uv-radio-group>
  60. </view>
  61. </uv-form-item>
  62. </view>
  63. <view class="form-item">
  64. <uv-form-item prop="sex" :customStyle="formItemStyle">
  65. <view class="form-item-label">
  66. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  67. 性别
  68. </view>
  69. <view class="form-item-content">
  70. <uv-radio-group v-model="form.sex"
  71. iconColor="#00A9FF"
  72. iconSize="36rpx"
  73. size="36rpx"
  74. labelColor="#181818"
  75. labelSize="26rpx"
  76. >
  77. <uv-radio
  78. v-for="(item, index) in sexOptions"
  79. :key="index"
  80. :label="item.label"
  81. :name="item.value"
  82. :customStyle="{ flex: 1 }"
  83. ></uv-radio>
  84. </uv-radio-group>
  85. </view>
  86. </uv-form-item>
  87. </view>
  88. <view class="form-item">
  89. <uv-form-item prop="phone" :customStyle="formItemStyle">
  90. <view class="form-item-label">
  91. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  92. 手机号
  93. </view>
  94. <view class="form-item-content">
  95. <formInput v-model="form.phone"></formInput>
  96. </view>
  97. </uv-form-item>
  98. </view>
  99. <view class="form-item">
  100. <uv-form-item prop="wechat" :customStyle="formItemStyle">
  101. <view class="form-item-label">微信号</view>
  102. <view class="form-item-content">
  103. <formInput v-model="form.wechat"></formInput>
  104. </view>
  105. </uv-form-item>
  106. </view>
  107. <view class="form-item">
  108. <uv-form-item prop="school" :customStyle="formItemStyle">
  109. <view class="form-item-label">学校</view>
  110. <view class="form-item-content">
  111. <formInput v-model="form.school"></formInput>
  112. </view>
  113. </uv-form-item>
  114. </view>
  115. <view class="form-item">
  116. <uv-form-item prop="grade" :customStyle="formItemStyle">
  117. <view class="form-item-label">年级</view>
  118. <view class="form-item-content">
  119. <formInput v-model="form.grade"></formInput>
  120. </view>
  121. </uv-form-item>
  122. </view>
  123. <view class="form-item">
  124. <uv-form-item prop="age" :customStyle="formItemStyle">
  125. <view class="form-item-label">年龄</view>
  126. <view class="form-item-content">
  127. <formInput v-model="form.age"></formInput>
  128. </view>
  129. </uv-form-item>
  130. </view>
  131. <view class="form-item">
  132. <uv-form-item prop="remark" :customStyle="formItemStyle">
  133. <view class="form-item-label">特殊需求饮食/健康等备注</view>
  134. <view class="form-item-content">
  135. <formInput v-model="form.remark"></formInput>
  136. </view>
  137. </uv-form-item>
  138. </view>
  139. <view class="form-item">
  140. <uv-form-item prop="emergencyPhone" :customStyle="formItemStyle">
  141. <view class="form-item-label">
  142. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  143. 紧急联系人
  144. </view>
  145. <view class="form-item-content">
  146. <formInput v-model="form.emergencyPhone"></formInput>
  147. </view>
  148. </uv-form-item>
  149. </view>
  150. <view class="form-item">
  151. <uv-form-item prop="chaperonPhone" :customStyle="formItemStyle">
  152. <view class="form-item-label">
  153. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  154. 监护人联系方式
  155. </view>
  156. <view class="form-item-content">
  157. <formInput v-model="form.chaperonPhone"></formInput>
  158. </view>
  159. </uv-form-item>
  160. </view>
  161. </uv-form>
  162. </view>
  163. <view class="footer">
  164. <view class="agreement">
  165. <view>
  166. <uv-checkbox-group
  167. v-model="checkboxValue"
  168. shape="circle"
  169. >
  170. <uv-checkbox
  171. size="40rpx"
  172. icon-size="40rpx"
  173. activeColor="#00A9FF"
  174. :name="1"
  175. ></uv-checkbox>
  176. </uv-checkbox-group>
  177. </view>
  178. <view class="desc">
  179. 我已阅读并同意
  180. <!-- todo: 替换配置项key -->
  181. <text class="highlight" @click="$refs.modal.open('config_agreement', '服务协议')">服务协议</text>
  182. <!-- todo: 替换配置项key -->
  183. <text class="highlight" @click="$refs.modal.open('config_privacy', '隐私政策')">隐私政策</text>
  184. </view>
  185. </view>
  186. <view class="bar">
  187. <button class="flex btn" @click="onSave">保存</button>
  188. </view>
  189. </view>
  190. </view>
  191. </uv-popup>
  192. <agreementModal ref="modal" @confirm="onConfirmAgreement"></agreementModal>
  193. </view>
  194. </template>
  195. <script>
  196. import formInput from '@/pages_order/components/formInput.vue'
  197. import agreementModal from '@/pages_order/components/agreementModal.vue'
  198. export default {
  199. components: {
  200. formInput,
  201. agreementModal,
  202. },
  203. data() {
  204. return {
  205. id: null,
  206. title: null,
  207. form: {
  208. name: null,
  209. cerNo: null,
  210. periodId: 0,
  211. sex: 0,
  212. phone: null,
  213. wechat: null,
  214. school: null,
  215. grade: null,
  216. age: null,
  217. remark: null,
  218. emergencyPhone: null,
  219. chaperonPhone: null,
  220. },
  221. rules: {
  222. 'name': {
  223. type: 'string',
  224. required: true,
  225. message: '请输入姓名',
  226. },
  227. 'cerNo': {
  228. type: 'string',
  229. required: true,
  230. message: '请输入身份证号',
  231. },
  232. 'periodId': {
  233. type: 'string',
  234. required: true,
  235. message: '请选择类型',
  236. },
  237. 'sex': {
  238. type: 'string',
  239. required: true,
  240. message: '请选择性别',
  241. },
  242. 'phone': {
  243. type: 'string',
  244. required: true,
  245. message: '请输入手机号',
  246. },
  247. 'emergencyPhone': {
  248. type: 'string',
  249. required: true,
  250. message: '请输入紧急联系人联系方式',
  251. },
  252. 'chaperonPhone': {
  253. type: 'string',
  254. required: true,
  255. message: '请输入监护人联系方式',
  256. },
  257. },
  258. formItemStyle: { padding: 0 },
  259. // todo: fetch
  260. periodOptions: [
  261. {
  262. id: '001',
  263. label: '成人',
  264. value: '0',
  265. },
  266. {
  267. id: '002',
  268. label: '青少年',
  269. value: '1',
  270. },
  271. {
  272. id: '003',
  273. label: '儿童',
  274. value: '2',
  275. },
  276. ],
  277. // todo: check
  278. sexOptions: [
  279. {
  280. id: '001',
  281. label: '男',
  282. value: '0',
  283. },
  284. {
  285. id: '002',
  286. label: '女',
  287. value: '1',
  288. },
  289. ],
  290. checkboxValue: [],
  291. }
  292. },
  293. methods: {
  294. async fetchTravelerDetail(personId) {
  295. try {
  296. const result = await this.$fetch('queryTouristById', { personId })
  297. const {
  298. name,
  299. cerNo,
  300. periodId,
  301. sex,
  302. phone,
  303. wechat,
  304. school,
  305. grade,
  306. age,
  307. remark,
  308. emergencyPhone,
  309. chaperonPhone,
  310. } = result
  311. this.form = {
  312. name,
  313. cerNo,
  314. periodId,
  315. sex,
  316. phone,
  317. wechat,
  318. school,
  319. grade,
  320. age,
  321. remark,
  322. emergencyPhone,
  323. chaperonPhone,
  324. }
  325. } catch (err) {
  326. }
  327. },
  328. open(id) {
  329. if (id) {
  330. this.id = id
  331. this.title = '编辑出行人'
  332. this.fetchTravelerDetail(id)
  333. } else {
  334. this.id = null
  335. this.title = '添加出行人'
  336. this.form = {
  337. name: null,
  338. cerNo: null,
  339. // todo: fetch default
  340. periodId: 0,
  341. sex: 0,
  342. phone: null,
  343. wechat: null,
  344. school: null,
  345. grade: null,
  346. age: null,
  347. remark: null,
  348. emergencyPhone: null,
  349. chaperonPhone: null,
  350. }
  351. }
  352. this.$refs.popup.open()
  353. },
  354. close() {
  355. this.$refs.popup.close()
  356. },
  357. onConfirmAgreement(confirm) {
  358. if (confirm) {
  359. this.checkboxValue = [1]
  360. } else {
  361. this.checkboxValue = []
  362. }
  363. },
  364. async onSave() {
  365. if(!this.checkboxValue.length){
  366. return uni.showToast({
  367. title: '请先同意《服务协议》和《隐私政策》',
  368. icon:'none'
  369. })
  370. }
  371. try {
  372. await this.$refs.form.validate()
  373. const {
  374. name,
  375. cerNo,
  376. periodId,
  377. sex,
  378. phone,
  379. wechat,
  380. school,
  381. grade,
  382. age,
  383. remark,
  384. emergencyPhone,
  385. chaperonPhone,
  386. } = this.form
  387. const params = {
  388. name,
  389. cerNo,
  390. periodId,
  391. sex,
  392. phone,
  393. wechat,
  394. school,
  395. grade,
  396. age,
  397. remark,
  398. emergencyPhone,
  399. chaperonPhone,
  400. }
  401. if (this.id) {
  402. params.id = this.id
  403. await this.$fetch('updateTourist', params)
  404. uni.showToast({
  405. icon: 'success',
  406. title: '修改出行人成功',
  407. });
  408. } else {
  409. await this.$fetch('addTourist', params)
  410. uni.showToast({
  411. icon: 'success',
  412. title: '添加出行人成功',
  413. });
  414. }
  415. this.$emit('submitted')
  416. this.close()
  417. } catch (err) {
  418. console.log('onSave err', err)
  419. }
  420. },
  421. },
  422. }
  423. </script>
  424. <style lang="scss" scoped>
  425. .popup__view {
  426. width: 100vw;
  427. display: flex;
  428. flex-direction: column;
  429. box-sizing: border-box;
  430. background: #FFFFFF;
  431. border-top-left-radius: 32rpx;
  432. border-top-right-radius: 32rpx;
  433. }
  434. .header {
  435. position: relative;
  436. width: 100%;
  437. padding: 24rpx 0;
  438. box-sizing: border-box;
  439. border-bottom: 2rpx solid #EEEEEE;
  440. .title {
  441. font-family: PingFang SC;
  442. font-weight: 500;
  443. font-size: 34rpx;
  444. line-height: 1.4;
  445. color: #181818;
  446. }
  447. .btn {
  448. font-family: PingFang SC;
  449. font-weight: 500;
  450. font-size: 32rpx;
  451. line-height: 1.4;
  452. color: #8B8B8B;
  453. position: absolute;
  454. top: 26rpx;
  455. left: 40rpx;
  456. }
  457. }
  458. .form {
  459. max-height: 75vh;
  460. padding: 32rpx 40rpx;
  461. box-sizing: border-box;
  462. overflow-y: auto;
  463. &-item {
  464. padding: 8rpx 0 6rpx 0;
  465. & + & {
  466. padding-top: 24rpx;
  467. border-top: 2rpx solid #EEEEEE;
  468. }
  469. &-label {
  470. margin-bottom: 14rpx;
  471. display: flex;
  472. align-items: center;
  473. font-family: PingFang SC;
  474. font-weight: 400;
  475. font-size: 26rpx;
  476. line-height: 1.4;
  477. color: #181818;
  478. .icon {
  479. margin-right: 8rpx;
  480. width: 16rpx;
  481. height: auto;
  482. }
  483. }
  484. &-content {
  485. .placeholder {
  486. color: #C6C6C6;
  487. font-size: 32rpx;
  488. font-weight: 400;
  489. }
  490. .region {
  491. min-height: 44rpx;
  492. justify-content: flex-start;
  493. }
  494. }
  495. }
  496. }
  497. .footer {
  498. width: 100%;
  499. .agreement {
  500. display: flex;
  501. padding: 16rpx 40rpx;
  502. background: #E9F8FF;
  503. box-sizing: border-box;
  504. /deep/ .uv-checkbox-group {
  505. flex: none;
  506. }
  507. .desc {
  508. flex: 1;
  509. font-family: PingFang SC;
  510. font-size: 24rpx;
  511. font-weight: 400;
  512. line-height: 40rpx;
  513. color: #8B8B8B;
  514. }
  515. .highlight {
  516. color: $uni-color;
  517. }
  518. }
  519. .bar {
  520. width: 100%;
  521. padding: 32rpx 40rpx;
  522. box-sizing: border-box;
  523. border-top: 2rpx solid #F1F1F1;
  524. }
  525. .btn {
  526. width: 100%;
  527. padding: 14rpx 0;
  528. box-sizing: border-box;
  529. font-family: PingFang SC;
  530. font-weight: 500;
  531. font-size: 36rpx;
  532. line-height: 1.4;
  533. color: #FFFFFF;
  534. background-image: linear-gradient(to right, #21FEEC, #019AF9);
  535. border: 2rpx solid #00A9FF;
  536. border-radius: 41rpx;
  537. }
  538. }
  539. </style>