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

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