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

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