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

567 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. iconColor="#00A9FF"
  47. iconSize="36rpx"
  48. size="36rpx"
  49. labelColor="#181818"
  50. labelSize="26rpx"
  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. 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. sexOptions: [
  260. {
  261. id: '001',
  262. label: '男',
  263. value: '0',
  264. },
  265. {
  266. id: '002',
  267. label: '女',
  268. value: '1',
  269. },
  270. ],
  271. checkboxValue: [],
  272. }
  273. },
  274. methods: {
  275. async fetchTravelerDetail(personId) {
  276. try {
  277. const result = await this.$fetch('queryTouristById', { personId })
  278. const {
  279. name,
  280. cerNo,
  281. periodId,
  282. sex,
  283. phone,
  284. wechat,
  285. school,
  286. grade,
  287. age,
  288. remark,
  289. emergencyPhone,
  290. chaperonPhone,
  291. } = result
  292. this.form = {
  293. name: name || '',
  294. cerNo: cerNo || '',
  295. periodId,
  296. sex,
  297. phone: phone || '',
  298. wechat: wechat || '',
  299. school: school || '',
  300. grade: grade || '',
  301. age: age || '',
  302. remark: remark || '',
  303. emergencyPhone: emergencyPhone || '',
  304. chaperonPhone: chaperonPhone || '',
  305. }
  306. } catch (err) {
  307. }
  308. },
  309. open(id) {
  310. if (id) {
  311. this.id = id
  312. this.title = '编辑出行人'
  313. this.fetchTravelerDetail(id)
  314. } else {
  315. this.id = null
  316. this.title = '添加出行人'
  317. this.form = {
  318. name: '',
  319. cerNo: '',
  320. // todo: fetch default
  321. periodId: 0,
  322. sex: 0,
  323. phone: '',
  324. wechat: '',
  325. school: '',
  326. grade: '',
  327. age: '',
  328. remark: '',
  329. emergencyPhone: '',
  330. chaperonPhone: '',
  331. }
  332. }
  333. this.$refs.popup.open()
  334. },
  335. close() {
  336. this.$refs.popup.close()
  337. },
  338. onConfirmAgreement(confirm) {
  339. if (confirm) {
  340. this.checkboxValue = [1]
  341. } else {
  342. this.checkboxValue = []
  343. }
  344. },
  345. async onSave() {
  346. if(!this.checkboxValue.length){
  347. return uni.showToast({
  348. title: '请先同意《服务协议》和《隐私政策》',
  349. icon:'none'
  350. })
  351. }
  352. try {
  353. await this.$refs.form.validate()
  354. const {
  355. name,
  356. cerNo,
  357. periodId,
  358. sex,
  359. phone,
  360. wechat,
  361. school,
  362. grade,
  363. age,
  364. remark,
  365. emergencyPhone,
  366. chaperonPhone,
  367. } = this.form
  368. const params = {
  369. name,
  370. cerNo,
  371. periodId,
  372. sex,
  373. phone,
  374. wechat,
  375. school,
  376. grade,
  377. age,
  378. remark,
  379. emergencyPhone,
  380. chaperonPhone,
  381. }
  382. if (this.id) {
  383. params.id = this.id
  384. await this.$fetch('updateTourist', params)
  385. uni.showToast({
  386. icon: 'success',
  387. title: '修改出行人成功',
  388. });
  389. } else {
  390. await this.$fetch('addTourist', params)
  391. uni.showToast({
  392. icon: 'success',
  393. title: '添加出行人成功',
  394. });
  395. }
  396. this.$emit('submitted')
  397. this.close()
  398. } catch (err) {
  399. console.log('onSave err', err)
  400. }
  401. },
  402. },
  403. }
  404. </script>
  405. <style lang="scss" scoped>
  406. .popup__view {
  407. width: 100vw;
  408. display: flex;
  409. flex-direction: column;
  410. box-sizing: border-box;
  411. background: #FFFFFF;
  412. border-top-left-radius: 32rpx;
  413. border-top-right-radius: 32rpx;
  414. }
  415. .header {
  416. position: relative;
  417. width: 100%;
  418. padding: 24rpx 0;
  419. box-sizing: border-box;
  420. border-bottom: 2rpx solid #EEEEEE;
  421. .title {
  422. font-family: PingFang SC;
  423. font-weight: 500;
  424. font-size: 34rpx;
  425. line-height: 1.4;
  426. color: #181818;
  427. }
  428. .btn {
  429. font-family: PingFang SC;
  430. font-weight: 500;
  431. font-size: 32rpx;
  432. line-height: 1.4;
  433. color: #8B8B8B;
  434. position: absolute;
  435. top: 26rpx;
  436. left: 40rpx;
  437. }
  438. }
  439. .form {
  440. max-height: 75vh;
  441. padding: 32rpx 40rpx;
  442. box-sizing: border-box;
  443. overflow-y: auto;
  444. &-item {
  445. padding: 8rpx 0 6rpx 0;
  446. & + & {
  447. padding-top: 24rpx;
  448. border-top: 2rpx solid #EEEEEE;
  449. }
  450. &-label {
  451. margin-bottom: 14rpx;
  452. display: flex;
  453. align-items: center;
  454. font-family: PingFang SC;
  455. font-weight: 400;
  456. font-size: 26rpx;
  457. line-height: 1.4;
  458. color: #181818;
  459. .icon {
  460. margin-right: 8rpx;
  461. width: 16rpx;
  462. height: auto;
  463. }
  464. }
  465. &-content {
  466. .placeholder {
  467. color: #C6C6C6;
  468. font-size: 32rpx;
  469. font-weight: 400;
  470. }
  471. .region {
  472. min-height: 44rpx;
  473. justify-content: flex-start;
  474. }
  475. }
  476. }
  477. }
  478. .footer {
  479. width: 100%;
  480. .agreement {
  481. display: flex;
  482. padding: 16rpx 40rpx;
  483. background: #E9F8FF;
  484. box-sizing: border-box;
  485. /deep/ .uv-checkbox-group {
  486. flex: none;
  487. }
  488. .desc {
  489. flex: 1;
  490. font-family: PingFang SC;
  491. font-size: 24rpx;
  492. font-weight: 400;
  493. line-height: 40rpx;
  494. color: #8B8B8B;
  495. }
  496. .highlight {
  497. color: $uni-color;
  498. }
  499. }
  500. .bar {
  501. width: 100%;
  502. padding: 32rpx 40rpx;
  503. box-sizing: border-box;
  504. border-top: 2rpx solid #F1F1F1;
  505. }
  506. .btn {
  507. width: 100%;
  508. padding: 14rpx 0;
  509. box-sizing: border-box;
  510. font-family: PingFang SC;
  511. font-weight: 500;
  512. font-size: 36rpx;
  513. line-height: 1.4;
  514. color: #FFFFFF;
  515. background-image: linear-gradient(to right, #21FEEC, #019AF9);
  516. border: 2rpx solid #00A9FF;
  517. border-radius: 41rpx;
  518. }
  519. }
  520. </style>