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

422 lines
12 KiB

  1. <template>
  2. <view class="page__view">
  3. <navbar title="填写订单" leftClick @leftClick="$utils.navigateBack" />
  4. <view class="main">
  5. <productCard :data="orderInfo"></productCard>
  6. <uv-form
  7. ref="form"
  8. :model="form"
  9. errorType="toast"
  10. >
  11. <view class="card">
  12. <view class="card-header">联系人信息</view>
  13. <view class="form-item">
  14. <uv-form-item prop="name" :customStyle="formItemStyle">
  15. <view class="form-item-label">
  16. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  17. 真实姓名
  18. </view>
  19. <view class="form-item-content">
  20. <formInput v-model="form.name"></formInput>
  21. </view>
  22. </uv-form-item>
  23. </view>
  24. <view class="form-item">
  25. <uv-form-item prop="phone" :customStyle="formItemStyle">
  26. <view class="form-item-label">
  27. <image class="icon" src="@/pages_order/static/icon-require.png" mode="widthFix"></image>
  28. 手机号码
  29. </view>
  30. <view class="form-item-content">
  31. <formInput v-model="form.phone"></formInput>
  32. </view>
  33. </uv-form-item>
  34. </view>
  35. </view>
  36. <view class="card">
  37. <view class="card-header">订单详情</view>
  38. <view style="margin-top: 16rpx;">
  39. <uv-form-item prop="members" :customStyle="formItemStyle">
  40. <!-- style="width: calc(100vw - 40rpx*2);" -->
  41. <view>
  42. <peopleNumberInput
  43. :adults.sync="form.adults"
  44. :teenager.sync="form.teenager"
  45. :child.sync="form.child"
  46. :adultsPrice="productPackage.adultsPrice"
  47. :teenagerPrice="productPackage.teenagerPrice"
  48. :childPrice="productPackage.childPrice"
  49. ></peopleNumberInput>
  50. <memberChooseView
  51. :members.sync="form.members"
  52. ></memberChooseView>
  53. </view>
  54. </uv-form-item>
  55. </view>
  56. </view>
  57. <view class="card">
  58. <view class="card-header">其他</view>
  59. <view class="form-item">
  60. <uv-form-item prop="couponId" :customStyle="formItemStyle">
  61. <view class="form-item-label">选择优惠券</view>
  62. <view class="form-item-content">
  63. <view class="flex row" @click="jumpToSelectCoupon">
  64. <view v-if="form.couponId" class="text">{{ couponInfo.label }}</view>
  65. <view v-else class="text placeholder">请选择</view>
  66. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  67. </view>
  68. </view>
  69. </uv-form-item>
  70. </view>
  71. <view class="form-item">
  72. <uv-form-item prop="receiptId" :customStyle="formItemStyle">
  73. <view class="form-item-label">选择发票类型</view>
  74. <view class="form-item-content">
  75. <view class="flex row" @click="jumpToSelectInvoice">
  76. <view v-if="form.receiptId" class="text">{{ getInvoiceDesc(form.receiptId) }}</view>
  77. <view v-else class="text placeholder">请选择</view>
  78. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  79. </view>
  80. </view>
  81. </uv-form-item>
  82. </view>
  83. </view>
  84. </uv-form>
  85. <view class="notice">
  86. <!-- <view class="notice-header">下单须知</view> -->
  87. <view class="notice-content">
  88. <!-- todo: check key -->
  89. <!-- <uv-parse :content="configList['order_instructions']"></uv-parse> -->
  90. 如有特殊病史或有不宜参加的旅程项目男女报名如无法同住分开报名需安排同住同车等请备注
  91. </view>
  92. </view>
  93. </view>
  94. <view class="bottom">
  95. <view class="agreement">
  96. <uv-checkbox-group
  97. v-model="checkboxValue"
  98. shape="circle"
  99. >
  100. <uv-checkbox
  101. size="40rpx"
  102. icon-size="40rpx"
  103. activeColor="#00A9FF"
  104. :name="1"
  105. ></uv-checkbox>
  106. </uv-checkbox-group>
  107. <view class="desc">
  108. 我已阅读并同意
  109. <!-- todo: 替换配置项key -->
  110. <text class="highlight" @click="$refs.modal.open('config_agreement', '退订政策')">退订政策</text>
  111. <!-- todo: 替换配置项key -->
  112. <text class="highlight" @click="$refs.modal.open('config_privacy', '合同范本')">合同范本</text>
  113. <!-- todo: 替换配置项key -->
  114. <text class="highlight" @click="$refs.modal.open('config_privacy', '预订须知')">预订须知</text>
  115. <!-- todo: 替换配置项key -->
  116. <text class="highlight" @click="$refs.modal.open('config_privacy', '安全提示')">安全提示</text>
  117. </view>
  118. </view>
  119. <view class="flex bar">
  120. <view class="col price">
  121. <view class="flex price-label">
  122. 已选<view class="highlight">{{ `${totolPeople}` }}</view>总额
  123. </view>
  124. <view class="flex price-value">
  125. ¥<view class="highlight">{{ totalPrice }}</view>
  126. </view>
  127. </view>
  128. <button class="col btn btn-primary btn-pay" @click="onPay">立即支付</button>
  129. </view>
  130. </view>
  131. <agreementModal ref="modal" @confirm="onConfirmAgreement"></agreementModal>
  132. </view>
  133. </template>
  134. <script>
  135. import { mapState } from 'vuex'
  136. import productCard from '@/pages_order/order/components/productCard.vue'
  137. import peopleNumberInput from './peopleNumberInput.vue'
  138. import memberChooseView from './memberChooseView.vue'
  139. import formInput from '@/pages_order/components/formInput.vue'
  140. import agreementModal from '@/pages_order/components/agreementModal.vue'
  141. export default {
  142. components: {
  143. productCard,
  144. peopleNumberInput,
  145. memberChooseView,
  146. formInput,
  147. agreementModal,
  148. },
  149. data() {
  150. return {
  151. form: {
  152. name: null,
  153. phone: null,
  154. adults: 0,
  155. teenager: 0,
  156. child: 0,
  157. members: [],
  158. couponId: null,
  159. receiptId: null,
  160. },
  161. checkboxValue: [],
  162. formItemStyle: { padding: 0 },
  163. }
  164. },
  165. computed: {
  166. ...mapState(['configList', 'userInfo', 'orderInfo', 'couponInfo']),
  167. productPackage() {
  168. const { time, product } = this.orderInfo
  169. const { timeOptions } = product || {}
  170. return timeOptions?.find?.(item => item.id === time) || {}
  171. },
  172. totolPeople() {
  173. const { adults, teenager, child } = this.form
  174. return (adults || 0) + (teenager || 0) + (child || 0)
  175. },
  176. totalPrice() {
  177. const { adults, teenager, child, couponId } = this.form
  178. const { adultsPrice, teenagerPrice, childPrice } = this.productPackage
  179. let total = 0
  180. adults && (total += adults * (adultsPrice || 0))
  181. teenager && (total += teenager * (teenagerPrice || 0))
  182. child && (total += child * (childPrice || 0))
  183. couponId && (total -= (this.couponInfo?.price || 0))
  184. return total
  185. },
  186. },
  187. watch: {
  188. form: {
  189. handler(val) {
  190. this.$refs.form.setRules(this.getRules())
  191. },
  192. deep: true
  193. }
  194. },
  195. onShow() {
  196. if (this.couponInfo) {
  197. this.form.couponId = this.couponInfo.id
  198. }
  199. },
  200. onLoad(arg) {
  201. console.log('onLoad')
  202. console.log('orderInfo', this.orderInfo)
  203. this.initData()
  204. },
  205. onReady() {
  206. this.$refs.form.setRules(this.getRules())
  207. },
  208. onUnload() {
  209. this.$store.commit('setCouponInfo', null)
  210. },
  211. methods: {
  212. getRules() {
  213. const { adults, teenager, child } = this.form
  214. return {
  215. 'name': {
  216. type: 'string',
  217. required: true,
  218. message: '请输入真实姓名',
  219. },
  220. 'phone': {
  221. type: 'string',
  222. required: true,
  223. message: '请输入手机号码',
  224. },
  225. 'adults': {
  226. type: 'number',
  227. required: true,
  228. message: '请选择人数',
  229. validator: (rule, value, callback) => {
  230. if (adults || teenager || child) {
  231. return true
  232. }
  233. return false
  234. },
  235. },
  236. 'members': {
  237. type: 'array',
  238. required: true,
  239. message: '请选择出行人',
  240. },
  241. }
  242. },
  243. initData() {
  244. const {
  245. time,
  246. adults,
  247. teenager,
  248. child,
  249. members,
  250. } = this.orderInfo
  251. this.form = {
  252. name: null,
  253. phone: null,
  254. adults,
  255. teenager,
  256. child,
  257. members: members.map(item => ({ ...item, isSelected: true })),
  258. couponId: null,
  259. receiptId: null,
  260. }
  261. },
  262. jumpToSelectCoupon() {
  263. uni.navigateTo({
  264. url: `/pages_order/coupon/couponSelect/index`
  265. })
  266. },
  267. getCouponDesc() {
  268. // todo
  269. },
  270. jumpToSelectInvoice() {
  271. // todo
  272. },
  273. getInvoiceDesc() {
  274. // todo
  275. },
  276. onConfirmAgreement(confirm) {
  277. if (confirm) {
  278. this.checkboxValue = [1]
  279. } else {
  280. this.checkboxValue = []
  281. }
  282. },
  283. async onPay() {
  284. if(!this.checkboxValue.length){
  285. return uni.showToast({
  286. title: '请先同意《用户协议》《隐私协议》《消费者告知》',
  287. icon:'none'
  288. })
  289. }
  290. try {
  291. await this.$refs.form.validate()
  292. // todo: fetch create order and save wx pay data
  293. const {
  294. product,
  295. time,
  296. } = this.orderInfo
  297. const {
  298. name,
  299. phone,
  300. adults,
  301. teenager,
  302. child,
  303. members,
  304. couponId,
  305. receiptId,
  306. } = this.form
  307. const { startDate, endDate } = time
  308. let params = {
  309. activityId: product.id,
  310. startDate,
  311. endDate,
  312. couponId,
  313. receiptId,
  314. name,
  315. phone,
  316. }
  317. const result = await this.$fetch('createOrder', params)
  318. // todo: check result includes order id?
  319. const orderInfo = {
  320. product,
  321. couponInfo: this.couponInfo,
  322. time,
  323. adults,
  324. teenager,
  325. child,
  326. members,
  327. couponId,
  328. receiptId,
  329. name,
  330. phone,
  331. }
  332. this.$store.commit('setOrderInfo', orderInfo)
  333. // todo: get id?
  334. uni.navigateTo({
  335. url: `/pages_order/order/orderPay/index?id=${result.id}`
  336. })
  337. } catch (err) {
  338. }
  339. },
  340. },
  341. }
  342. </script>
  343. <style scoped lang="scss">
  344. @import '../styles/style.scss';
  345. .price {
  346. justify-content: flex-start;
  347. &-label {
  348. justify-content: flex-start;
  349. column-gap: 12rpx;
  350. font-family: PingFang SC;
  351. font-weight: 400;
  352. font-size: 24rpx;
  353. line-height: 1.4;
  354. color: #626262;
  355. .highlight {
  356. font-size: 24rpx;
  357. font-weight: 500;
  358. color: $uni-color;
  359. }
  360. }
  361. &-value {
  362. justify-content: flex-start;
  363. column-gap: 8rpx;
  364. font-family: PingFang SC;
  365. font-weight: 500;
  366. font-size: 24rpx;
  367. line-height: 1.4;
  368. color: $uni-color;
  369. .highlight {
  370. font-size: 40rpx;
  371. }
  372. }
  373. }
  374. .btn-pay {
  375. flex: none;
  376. width: auto;
  377. padding: 14rpx 74rpx;
  378. }
  379. </style>