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

332 lines
8.4 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. return (await this.$fetch('queryTouristList', { isDefault: '1' })).records
  165. } catch (err) {
  166. return []
  167. }
  168. },
  169. jumpToSelectMember() {
  170. const { members } = this.form
  171. const selectIds = members.map(item => item.id).join(',')
  172. console.log('jumpToSelectMember', selectIds)
  173. this.$utils.navigateTo(`/pages_order/traveler/travelerList?selectIds=${selectIds}`)
  174. },
  175. async open(data) {
  176. const { selectTime } = data || {}
  177. const defaultMembers = await this.getDefaultMembers()
  178. this.form.time = selectTime || null
  179. this.form.members = defaultMembers
  180. this.$refs.popup.open()
  181. },
  182. close() {
  183. this.$refs.popup.close()
  184. },
  185. async onConfirm() {
  186. try {
  187. await this.$refs.form.validate()
  188. const {
  189. time,
  190. adults,
  191. teenager,
  192. child,
  193. members,
  194. } = this.form
  195. const orderInfo = {
  196. product: this.data,
  197. time,
  198. adults,
  199. teenager,
  200. child,
  201. members,
  202. }
  203. this.$store.commit('setOrderInfo', orderInfo)
  204. uni.navigateTo({
  205. url: '/pages_order/order/orderConfirm/index'
  206. })
  207. } catch (err) {
  208. }
  209. },
  210. onPopupChange(e) {
  211. if (e.show) {
  212. return
  213. }
  214. this.$emit('timeChange', this.form.time)
  215. },
  216. },
  217. }
  218. </script>
  219. <style lang="scss" scoped>
  220. .popup__view {
  221. width: 100vw;
  222. display: flex;
  223. flex-direction: column;
  224. box-sizing: border-box;
  225. font-family: PingFang SC;
  226. font-weight: 400;
  227. line-height: 1.4;
  228. background: #FFFFFF;
  229. border-top-left-radius: 32rpx;
  230. border-top-right-radius: 32rpx;
  231. }
  232. .header {
  233. position: relative;
  234. width: 100%;
  235. padding: 24rpx 0;
  236. box-sizing: border-box;
  237. font-family: PingFang SC;
  238. font-weight: 500;
  239. font-size: 34rpx;
  240. line-height: 1.4;
  241. color: #181818;
  242. border-bottom: 2rpx solid #EEEEEE;
  243. }
  244. .section {
  245. padding: 24rpx 40rpx;
  246. font-family: PingFang SC;
  247. font-weight: 400;
  248. &-header {
  249. justify-content: space-between;
  250. font-size: 32rpx;
  251. font-weight: 500;
  252. color: #181818;
  253. .btn {
  254. column-gap: 4rpx;
  255. font-size: 32rpx;
  256. font-weight: 400;
  257. color: #8B8B8B;
  258. .highlight {
  259. color: #181818;
  260. }
  261. .img {
  262. width: 32rpx;
  263. height: auto;
  264. }
  265. }
  266. }
  267. &-content {
  268. margin-top: 20rpx;
  269. }
  270. }
  271. .member {
  272. display: grid;
  273. grid-template-columns: repeat(3, 1fr);
  274. gap: 12rpx;
  275. &-item {
  276. padding: 16rpx;
  277. text-align: center;
  278. font-size: 28rpx;
  279. color: #181818;
  280. background: #F9F9F9;
  281. border-radius: 16rpx;
  282. }
  283. }
  284. .footer {
  285. width: 100%;
  286. // height: 214rpx;
  287. padding: 32rpx 40rpx;
  288. box-sizing: border-box;
  289. .btn {
  290. width: 100%;
  291. padding: 14rpx 0;
  292. font-family: PingFang SC;
  293. font-weight: 500;
  294. font-size: 36rpx;
  295. line-height: 1.4;
  296. color: #FFFFFF;
  297. background-image: linear-gradient(to right, #21FEEC, #019AF9);
  298. border: 2rpx solid #00A9FF;
  299. border-radius: 41rpx;
  300. }
  301. }
  302. </style>