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

371 lines
9.3 KiB

  1. <template>
  2. <view>
  3. <uv-popup ref="popup" mode="bottom" bgColor="none" @change="onPopupChange">
  4. <view class="popup__view">
  5. <view class="flex header">
  6. 选择日期/套餐/人数
  7. </view>
  8. <uv-form
  9. ref="form"
  10. :model="form"
  11. errorType="toast"
  12. >
  13. <view class="section">
  14. <uv-form-item prop="time" :customStyle="formItemStyle">
  15. <view class="flex section-header">
  16. <view>选择团期</view>
  17. <button class="flex btn" @click="openTimePicker">
  18. <view class="highlight">日历选择</view>
  19. <image class="img" src="@/static/image/icon-arrow-right.png" mode="widthFix"></image>
  20. </button>
  21. </view>
  22. <timeCalendarSelect ref="timeCalendarSelect" v-model="form.time" :options="data.timeOptions"></timeCalendarSelect>
  23. <view class="flex section-content">
  24. <timeOptionsSelect style="width: calc(100vw - 40rpx*2);"
  25. v-model="form.time"
  26. :options="data.timeOptions"
  27. ></timeOptionsSelect>
  28. </view>
  29. </uv-form-item>
  30. </view>
  31. <view class="section">
  32. <uv-form-item prop="adults" :customStyle="formItemStyle">
  33. <view class="flex section-header">
  34. <view>选择人数</view>
  35. </view>
  36. <view class="flex section-content">
  37. <peopleNumberInput style="width: calc(100vw - 40rpx*2);"
  38. :adults.sync="form.adults"
  39. :teenager.sync="form.teenager"
  40. :child.sync="form.child"
  41. :adultsPrice="selectTimeObj.adultsPrice"
  42. :teenagerPrice="selectTimeObj.teenagerPrice"
  43. :childPrice="selectTimeObj.childPrice"
  44. ></peopleNumberInput>
  45. </view>
  46. </uv-form-item>
  47. </view>
  48. <view class="section">
  49. <uv-form-item prop="members" :customStyle="formItemStyle">
  50. <view class="flex section-header">
  51. <view>选择人员</view>
  52. <button class="flex btn" @click="jumpToSelectMember">
  53. <view>请选择出行人</view>
  54. <image class="img" src="@/static/image/icon-arrow-right.png" mode="widthFix"></image>
  55. </button>
  56. </view>
  57. <view class="flex section-content member">
  58. <view class="member-item" v-for="item in form.members" :key="item.id">
  59. {{ item.name }}
  60. </view>
  61. </view>
  62. </uv-form-item>
  63. </view>
  64. </uv-form>
  65. <view class="footer">
  66. <button class="flex btn" @click="onConfirm">填写订单</button>
  67. </view>
  68. </view>
  69. </uv-popup>
  70. </view>
  71. </template>
  72. <script>
  73. import { mapState } from 'vuex'
  74. import timeOptionsSelect from '@/pages_order/order/orderConfirm/timeOptionsSelect.vue'
  75. import timeCalendarSelect from '@/pages_order/order/orderConfirm/timeCalendarSelect.vue'
  76. import peopleNumberInput from '@/pages_order/order/orderConfirm/peopleNumberInput.vue'
  77. export default {
  78. components: {
  79. timeOptionsSelect,
  80. timeCalendarSelect,
  81. peopleNumberInput,
  82. },
  83. props: {
  84. data: {
  85. type: Object,
  86. default() {
  87. return {}
  88. }
  89. },
  90. },
  91. data() {
  92. return {
  93. options: [],
  94. form: {
  95. time: null,
  96. adults: 0,
  97. teenager: 0,
  98. child: 0,
  99. members: [],
  100. },
  101. formItemStyle: { padding: 0 },
  102. }
  103. },
  104. computed : {
  105. ...mapState(['configList', 'travelerList']),
  106. selectTimeObj() {
  107. const { time: id } = this.form
  108. const { timeOptions } = this.data
  109. if (id) {
  110. return timeOptions?.find?.(option => option.id === id) || {}
  111. }
  112. return timeOptions?.[0] || {}
  113. },
  114. },
  115. watch: {
  116. travelerList(val) {
  117. if (val?.length) {
  118. this.form.members = val
  119. this.$store.commit('setTravelerList', [])
  120. }
  121. },
  122. form: {
  123. handler(val) {
  124. this.$refs.form.setRules(this.getRules())
  125. },
  126. deep: true
  127. }
  128. },
  129. onReady() {
  130. this.$refs.form.setRules(this.getRules())
  131. },
  132. methods: {
  133. getRules() {
  134. const { adults, teenager, child } = this.form
  135. return {
  136. 'time': {
  137. type: 'string',
  138. required: true,
  139. message: '请选择团期',
  140. },
  141. 'adults': {
  142. type: 'number',
  143. required: true,
  144. message: '请选择人数',
  145. validator: (rule, value, callback) => {
  146. if (adults || teenager || child) {
  147. return true
  148. }
  149. return false
  150. },
  151. },
  152. 'members': {
  153. type: 'array',
  154. required: true,
  155. message: '请选择出行人',
  156. },
  157. }
  158. },
  159. openTimePicker() {
  160. this.$refs.timeCalendarSelect.open()
  161. },
  162. async getDefaultMembers() {
  163. try {
  164. // todo: fetch defalt members
  165. return [
  166. {
  167. id: '001',
  168. name: '李梓发',
  169. idNo: '430223********9999',
  170. type: 0,
  171. },
  172. {
  173. id: '002',
  174. name: '吴彦谦',
  175. idNo: '430223********9999',
  176. type: 0,
  177. },
  178. {
  179. id: '003',
  180. name: '冯云',
  181. idNo: '430223********9999',
  182. type: 1,
  183. },
  184. {
  185. id: '004',
  186. name: '冯思钗',
  187. idNo: '430223********9999',
  188. type: 2,
  189. },
  190. {
  191. id: '005',
  192. name: '李书萍',
  193. idNo: '430223********9999',
  194. type: 0,
  195. },
  196. {
  197. id: '006',
  198. name: '冯艺莲',
  199. idNo: '430223********9999',
  200. type: 1,
  201. },
  202. ]
  203. } catch (err) {
  204. return []
  205. }
  206. },
  207. jumpToSelectMember() {
  208. const { members } = this.form
  209. const selectIds = members.map(item => item.id).join(',')
  210. console.log('jumpToSelectMember', selectIds)
  211. this.$utils.navigateTo(`/pages_order/traveler/travelerList?selectIds=${selectIds}`)
  212. },
  213. async open(data) {
  214. const { selectTime } = data || {}
  215. const defaultMembers = await this.getDefaultMembers()
  216. this.form.time = selectTime || null
  217. this.form.members = defaultMembers
  218. this.$refs.popup.open()
  219. },
  220. close() {
  221. this.$refs.popup.close()
  222. },
  223. async onConfirm() {
  224. try {
  225. await this.$refs.form.validate()
  226. const {
  227. time,
  228. adults,
  229. teenager,
  230. child,
  231. members,
  232. } = this.form
  233. const orderInfo = {
  234. product: this.data,
  235. time,
  236. adults,
  237. teenager,
  238. child,
  239. members,
  240. }
  241. this.$store.commit('setOrderInfo', orderInfo)
  242. uni.navigateTo({
  243. url: '/pages_order/order/orderConfirm/index'
  244. })
  245. } catch (err) {
  246. }
  247. },
  248. onPopupChange(e) {
  249. if (e.show) {
  250. return
  251. }
  252. this.$emit('timeChange', this.form.time)
  253. },
  254. },
  255. }
  256. </script>
  257. <style lang="scss" scoped>
  258. .popup__view {
  259. width: 100vw;
  260. display: flex;
  261. flex-direction: column;
  262. box-sizing: border-box;
  263. font-family: PingFang SC;
  264. font-weight: 400;
  265. line-height: 1.4;
  266. background: #FFFFFF;
  267. border-top-left-radius: 32rpx;
  268. border-top-right-radius: 32rpx;
  269. }
  270. .header {
  271. position: relative;
  272. width: 100%;
  273. padding: 24rpx 0;
  274. box-sizing: border-box;
  275. font-family: PingFang SC;
  276. font-weight: 500;
  277. font-size: 34rpx;
  278. line-height: 1.4;
  279. color: #181818;
  280. border-bottom: 2rpx solid #EEEEEE;
  281. }
  282. .section {
  283. padding: 24rpx 40rpx;
  284. font-family: PingFang SC;
  285. font-weight: 400;
  286. &-header {
  287. justify-content: space-between;
  288. font-size: 32rpx;
  289. font-weight: 500;
  290. color: #181818;
  291. .btn {
  292. column-gap: 4rpx;
  293. font-size: 32rpx;
  294. font-weight: 400;
  295. color: #8B8B8B;
  296. .highlight {
  297. color: #181818;
  298. }
  299. .img {
  300. width: 32rpx;
  301. height: auto;
  302. }
  303. }
  304. }
  305. &-content {
  306. margin-top: 20rpx;
  307. }
  308. }
  309. .member {
  310. display: grid;
  311. grid-template-columns: repeat(3, 1fr);
  312. gap: 12rpx;
  313. &-item {
  314. padding: 16rpx;
  315. text-align: center;
  316. font-size: 28rpx;
  317. color: #181818;
  318. background: #F9F9F9;
  319. border-radius: 16rpx;
  320. }
  321. }
  322. .footer {
  323. width: 100%;
  324. // height: 214rpx;
  325. padding: 32rpx 40rpx;
  326. box-sizing: border-box;
  327. .btn {
  328. width: 100%;
  329. padding: 14rpx 0;
  330. font-family: PingFang SC;
  331. font-weight: 500;
  332. font-size: 36rpx;
  333. line-height: 1.4;
  334. color: #FFFFFF;
  335. background-image: linear-gradient(to right, #21FEEC, #019AF9);
  336. border: 2rpx solid #00A9FF;
  337. border-radius: 41rpx;
  338. }
  339. }
  340. </style>