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

564 lines
15 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. iconSize="36rpx"
  47. size="36rpx"
  48. labelColor="#181818"
  49. labelSize="26rpx"
  50. activeColor="#00A9FF"
  51. >
  52. <uv-radio
  53. v-for="item in configList.periodList"
  54. :key="item.id"
  55. :label="item.title"
  56. :name="item.id"
  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. iconSize="36rpx"
  72. size="36rpx"
  73. labelColor="#181818"
  74. labelSize="26rpx"
  75. activeColor="#00A9FF"
  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. <text class="highlight" @click="$refs.modal.open('service_agreement', '服务协议')">服务协议</text>
  181. <text class="highlight" @click="$refs.modal.open('privacy_policy', '隐私政策')">隐私政策</text>
  182. </view>
  183. </view>
  184. <view class="bar">
  185. <button class="flex btn" @click="onSave">保存</button>
  186. </view>
  187. </view>
  188. </view>
  189. </uv-popup>
  190. <agreementModal ref="modal" @confirm="onConfirmAgreement"></agreementModal>
  191. </view>
  192. </template>
  193. <script>
  194. import formInput from '@/pages_order/components/formInput.vue'
  195. import agreementModal from '@/pages_order/components/agreementModal.vue'
  196. export default {
  197. components: {
  198. formInput,
  199. agreementModal,
  200. },
  201. data() {
  202. return {
  203. id: null,
  204. title: null,
  205. form: {
  206. name: null,
  207. cerNo: null,
  208. periodId: null,
  209. sex: null,
  210. phone: null,
  211. wechat: null,
  212. school: null,
  213. grade: null,
  214. age: null,
  215. remark: null,
  216. emergencyPhone: null,
  217. chaperonPhone: null,
  218. },
  219. rules: {
  220. 'name': {
  221. type: 'string',
  222. required: true,
  223. message: '请输入姓名',
  224. },
  225. 'cerNo': {
  226. type: 'string',
  227. required: true,
  228. message: '请输入身份证号',
  229. },
  230. 'periodId': {
  231. type: 'string',
  232. required: true,
  233. message: '请选择类型',
  234. },
  235. 'sex': {
  236. type: 'string',
  237. required: true,
  238. message: '请选择性别',
  239. },
  240. 'phone': {
  241. type: 'string',
  242. required: true,
  243. message: '请输入手机号',
  244. },
  245. 'emergencyPhone': {
  246. type: 'string',
  247. required: true,
  248. message: '请输入紧急联系人联系方式',
  249. },
  250. 'chaperonPhone': {
  251. type: 'string',
  252. required: true,
  253. message: '请输入监护人联系方式',
  254. },
  255. },
  256. formItemStyle: { padding: 0 },
  257. sexOptions: [
  258. {
  259. id: '001',
  260. label: '男',
  261. value: '0',
  262. },
  263. {
  264. id: '002',
  265. label: '女',
  266. value: '1',
  267. },
  268. ],
  269. checkboxValue: [],
  270. }
  271. },
  272. methods: {
  273. async fetchTravelerDetail(personId) {
  274. try {
  275. const result = await this.$fetch('queryTouristById', { personId })
  276. const {
  277. name,
  278. cerNo,
  279. periodId,
  280. sex,
  281. phone,
  282. wechat,
  283. school,
  284. grade,
  285. age,
  286. remark,
  287. emergencyPhone,
  288. chaperonPhone,
  289. } = result
  290. this.form = {
  291. name: name || '',
  292. cerNo: cerNo || '',
  293. periodId,
  294. sex,
  295. phone: phone || '',
  296. wechat: wechat || '',
  297. school: school || '',
  298. grade: grade || '',
  299. age: age || '',
  300. remark: remark || '',
  301. emergencyPhone: emergencyPhone || '',
  302. chaperonPhone: chaperonPhone || '',
  303. }
  304. } catch (err) {
  305. }
  306. },
  307. open(id) {
  308. if (id) {
  309. this.id = id
  310. this.title = '编辑出行人'
  311. this.fetchTravelerDetail(id)
  312. } else {
  313. this.id = null
  314. this.title = '添加出行人'
  315. this.form = {
  316. name: '',
  317. cerNo: '',
  318. periodId: this.configList?.periodList?.[0]?.id || null,
  319. sex: this.sexOptions?.[0]?.value,
  320. phone: '',
  321. wechat: '',
  322. school: '',
  323. grade: '',
  324. age: '',
  325. remark: '',
  326. emergencyPhone: '',
  327. chaperonPhone: '',
  328. }
  329. }
  330. this.$refs.popup.open()
  331. },
  332. close() {
  333. this.$refs.popup.close()
  334. },
  335. onConfirmAgreement(confirm) {
  336. if (confirm) {
  337. this.checkboxValue = [1]
  338. } else {
  339. this.checkboxValue = []
  340. }
  341. },
  342. async onSave() {
  343. if(!this.checkboxValue.length){
  344. return uni.showToast({
  345. title: '请先同意《服务协议》和《隐私政策》',
  346. icon:'none'
  347. })
  348. }
  349. try {
  350. await this.$refs.form.validate()
  351. const {
  352. name,
  353. cerNo,
  354. periodId,
  355. sex,
  356. phone,
  357. wechat,
  358. school,
  359. grade,
  360. age,
  361. remark,
  362. emergencyPhone,
  363. chaperonPhone,
  364. } = this.form
  365. const params = {
  366. name,
  367. cerNo,
  368. periodId,
  369. sex,
  370. phone,
  371. wechat,
  372. school,
  373. grade,
  374. age,
  375. remark,
  376. emergencyPhone,
  377. chaperonPhone,
  378. }
  379. if (this.id) {
  380. params.id = this.id
  381. await this.$fetch('updateTourist', params)
  382. uni.showToast({
  383. icon: 'success',
  384. title: '修改出行人成功',
  385. });
  386. } else {
  387. await this.$fetch('addTourist', params)
  388. uni.showToast({
  389. icon: 'success',
  390. title: '添加出行人成功',
  391. });
  392. }
  393. this.$emit('submitted')
  394. this.close()
  395. } catch (err) {
  396. console.log('onSave err', err)
  397. }
  398. },
  399. },
  400. }
  401. </script>
  402. <style lang="scss" scoped>
  403. .popup__view {
  404. width: 100vw;
  405. display: flex;
  406. flex-direction: column;
  407. box-sizing: border-box;
  408. background: #FFFFFF;
  409. border-top-left-radius: 32rpx;
  410. border-top-right-radius: 32rpx;
  411. }
  412. .header {
  413. position: relative;
  414. width: 100%;
  415. padding: 24rpx 0;
  416. box-sizing: border-box;
  417. border-bottom: 2rpx solid #EEEEEE;
  418. .title {
  419. font-family: PingFang SC;
  420. font-weight: 500;
  421. font-size: 34rpx;
  422. line-height: 1.4;
  423. color: #181818;
  424. }
  425. .btn {
  426. font-family: PingFang SC;
  427. font-weight: 500;
  428. font-size: 32rpx;
  429. line-height: 1.4;
  430. color: #8B8B8B;
  431. position: absolute;
  432. top: 26rpx;
  433. left: 40rpx;
  434. }
  435. }
  436. .form {
  437. max-height: 75vh;
  438. padding: 32rpx 40rpx;
  439. box-sizing: border-box;
  440. overflow-y: auto;
  441. &-item {
  442. padding: 8rpx 0 6rpx 0;
  443. & + & {
  444. padding-top: 24rpx;
  445. border-top: 2rpx solid #EEEEEE;
  446. }
  447. &-label {
  448. margin-bottom: 14rpx;
  449. display: flex;
  450. align-items: center;
  451. font-family: PingFang SC;
  452. font-weight: 400;
  453. font-size: 26rpx;
  454. line-height: 1.4;
  455. color: #181818;
  456. .icon {
  457. margin-right: 8rpx;
  458. width: 16rpx;
  459. height: auto;
  460. }
  461. }
  462. &-content {
  463. .placeholder {
  464. color: #C6C6C6;
  465. font-size: 32rpx;
  466. font-weight: 400;
  467. }
  468. .region {
  469. min-height: 44rpx;
  470. justify-content: flex-start;
  471. }
  472. }
  473. }
  474. }
  475. .footer {
  476. width: 100%;
  477. .agreement {
  478. display: flex;
  479. padding: 16rpx 40rpx;
  480. background: #E9F8FF;
  481. box-sizing: border-box;
  482. /deep/ .uv-checkbox-group {
  483. flex: none;
  484. }
  485. .desc {
  486. flex: 1;
  487. font-family: PingFang SC;
  488. font-size: 24rpx;
  489. font-weight: 400;
  490. line-height: 40rpx;
  491. color: #8B8B8B;
  492. }
  493. .highlight {
  494. color: $uni-color;
  495. }
  496. }
  497. .bar {
  498. width: 100%;
  499. padding: 32rpx 40rpx;
  500. box-sizing: border-box;
  501. border-top: 2rpx solid #F1F1F1;
  502. }
  503. .btn {
  504. width: 100%;
  505. padding: 14rpx 0;
  506. box-sizing: border-box;
  507. font-family: PingFang SC;
  508. font-weight: 500;
  509. font-size: 36rpx;
  510. line-height: 1.4;
  511. color: #FFFFFF;
  512. background-image: linear-gradient(to right, #21FEEC, #019AF9);
  513. border: 2rpx solid #00A9FF;
  514. border-radius: 41rpx;
  515. }
  516. }
  517. </style>