普兆健康管家前端代码仓库
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.

420 lines
10 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. <template>
  2. <view class="page__view">
  3. <navbar title="确认订单" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#F3F2F7" />
  4. <view class="main">
  5. <view class="flex address" @click="jumpToSelectAddress">
  6. <addressView :data="addressData" :showIcon="true"></addressView>
  7. <uv-icon name="arrow-right" color="#C6C6C6" size="32rpx"></uv-icon>
  8. </view>
  9. <view class="card" v-for="(item, index) in productList" :key="item.id">
  10. <productCard :data="item" @change="onChange(index, $event)"></productCard>
  11. </view>
  12. <view class="order" v-if="orderData">
  13. <view class="order-header">
  14. 订单信息
  15. </view>
  16. <view class="flex row">
  17. <view class="row-label">订单编号</view>
  18. <view class="row-content">{{ orderData.number }}</view>
  19. </view>
  20. <view class="flex row">
  21. <view class="row-label">下单时间</view>
  22. <view class="row-content">{{ $dayjs(orderData.createTime).format('YYYY-MM-DD HH:mm') }}</view>
  23. </view>
  24. </view>
  25. <view class="notice">
  26. <view class="notice-header">下单须知</view>
  27. <view class="notice-content">
  28. <uv-parse :content="configList['order_instructions']"></uv-parse>
  29. </view>
  30. </view>
  31. </view>
  32. <view class="bottom">
  33. <view class="agreement">
  34. <uv-checkbox-group
  35. v-model="checkboxValue"
  36. shape="circle"
  37. >
  38. <uv-checkbox
  39. size="40rpx"
  40. icon-size="40rpx"
  41. activeColor="#7451DE"
  42. :name="1"
  43. ></uv-checkbox>
  44. </uv-checkbox-group>
  45. <view class="desc">
  46. 我已阅读并同意
  47. <!-- todo: 替换配置项key -->
  48. <text class="highlight" @click="$refs.modal.open('config_agreement', '用户协议')">用户协议</text>
  49. <!-- todo: 替换配置项key -->
  50. <text class="highlight" @click="$refs.modal.open('config_privacy', '隐私协议')">隐私协议</text>
  51. <!-- todo: 替换配置项key -->
  52. <text class="highlight" @click="$refs.modal.open('config_privacy', '消费者告知')">消费者告知</text>
  53. </view>
  54. </view>
  55. <view class="flex bar">
  56. <view class="flex col price">
  57. <view class="price-label">合计</view>
  58. <view class="price-unit">¥</view><view class="price-value">{{ totalPrice }}</view>
  59. </view>
  60. <button class="col btn" @click="onPay">立即支付</button>
  61. </view>
  62. </view>
  63. <agreementModal ref="modal" @confirm="onConfirmAgreement"></agreementModal>
  64. <payPopup ref="payPopup" @paySuccess="onPaySuccess" @payCancel="onPayCancel"></payPopup>
  65. </view>
  66. </template>
  67. <script>
  68. import { mapState } from 'vuex'
  69. import addressView from '@/pages_order/address/addressView.vue'
  70. import productCard from './productCard.vue'
  71. import agreementModal from '@/pages_order/components/agreementModal.vue'
  72. import payPopup from '@/pages_order/order/payPopup.vue'
  73. export default {
  74. components: {
  75. addressView,
  76. productCard,
  77. agreementModal,
  78. payPopup,
  79. },
  80. data() {
  81. return {
  82. id: null,
  83. defaultAddressInfo: null,
  84. orderData: null,
  85. productList: [],
  86. checkboxValue: [],
  87. }
  88. },
  89. computed: {
  90. ...mapState(['configList', 'userInfo', 'payOrderProduct', 'addressInfo']),
  91. totalPrice() {
  92. return this.productList.reduce((price, item) => {
  93. // return price + item.price * (item.count || 1)
  94. return price + item.currentPrice
  95. }, 0)
  96. },
  97. addressData() {
  98. return this.addressInfo || this.defaultAddressInfo || null
  99. },
  100. },
  101. async onShow() {
  102. console.log('onShow')
  103. console.log('address', this.addressInfo)
  104. this.fetchDefaultAddress()
  105. },
  106. onLoad(arg) {
  107. console.log('onLoad')
  108. console.log('payOrderProduct', this.payOrderProduct)
  109. this.productList = JSON.parse(JSON.stringify(this.payOrderProduct))
  110. // todo: check include Overseas Product ?
  111. // this.$utils.navigateTo('/pages_order/order/userInfo/infoFill')
  112. return
  113. this.orderData = {
  114. id: '001',
  115. number: 'BH872381728321983929',
  116. createTime: '2025-04-28 08:14',
  117. }
  118. console.log('orderData', this.orderData)
  119. },
  120. onUnload() {
  121. this.$store.commit('setAddressInfo', null)
  122. },
  123. methods: {
  124. async fetchDefaultAddress() {
  125. try {
  126. const params = {
  127. pageNo: 1,
  128. pageSize: 1,
  129. userId: this.userInfo.id,
  130. defaultFlag: 1,
  131. }
  132. let { records, total } = await this.$fetch('getAddressList', params)
  133. this.defaultAddressInfo = total ? records[0] : null
  134. } catch (err) {
  135. this.defaultAddressInfo = null
  136. }
  137. },
  138. jumpToSelectAddress() {
  139. this.$utils.navigateTo('/pages_order/address/addressList')
  140. },
  141. onChange(index, obj) {
  142. console.log('onChange', index, obj)
  143. const target = this.productList[index]
  144. target.skuId = obj.id
  145. target.specName = obj.specName
  146. target.currentPrice = obj.price
  147. this.productList[index] = target
  148. console.log('after set', this.productList)
  149. },
  150. onConfirmAgreement(confirm) {
  151. if (confirm) {
  152. this.checkboxValue = [1]
  153. } else {
  154. this.checkboxValue = []
  155. }
  156. },
  157. async onPay() {
  158. if(!this.checkboxValue.length){
  159. return uni.showToast({
  160. title: '请先同意《用户协议》《隐私协议》《消费者告知》',
  161. icon:'none'
  162. })
  163. }
  164. if (!this.addressData?.id) {
  165. return uni.showToast({
  166. title: '请选择地址',
  167. icon:'none'
  168. })
  169. }
  170. // const { id } = this.orderData
  171. const obj = {
  172. // // todo: check title
  173. // title: '营养套餐消费',
  174. // orderId: id,
  175. list: this.productList,
  176. addressId: this.addressData.id,
  177. amount: this.totalPrice,
  178. }
  179. this.$refs.payPopup.open(obj)
  180. },
  181. isDetectProduct() {
  182. // type: 产品类型(0营养剂,1预约,2课程)
  183. // return this.productList.length == 1 && this.productList[0].type == 1
  184. return this.productList.every(item => item.type == '1')
  185. },
  186. onPaySuccess() {
  187. // todo: check → jump?
  188. setTimeout(() => {
  189. if (this.isDetectProduct()) {
  190. // uni.reLaunch({
  191. // url: `/pages_order/order/orderDetail/index?id=${id}`
  192. // });
  193. uni.reLaunch({
  194. url: `/pages_order/checkup/checkupRecords`
  195. });
  196. return
  197. }
  198. // todo: check → jump to order list page ?
  199. uni.reLaunch({
  200. url: `/pages_order/order/orderList/index`
  201. });
  202. }, 700)
  203. // uni.reLaunch({
  204. // url: '/pages_order/order/orderList/index'
  205. // })
  206. },
  207. onPayCancel() {
  208. // todo: check → jump to order list page?
  209. uni.redirectTo({
  210. url: `/pages_order/order/orderList/index?index=1`
  211. });
  212. },
  213. },
  214. }
  215. </script>
  216. <style scoped lang="scss">
  217. .page__view {
  218. width: 100vw;
  219. min-height: 100vh;
  220. background-color: $uni-bg-color;
  221. position: relative;
  222. /deep/ .nav-bar__view {
  223. position: fixed;
  224. top: 0;
  225. left: 0;
  226. }
  227. }
  228. .main {
  229. padding: calc(var(--status-bar-height) + 144rpx) 32rpx 310rpx 32rpx;
  230. }
  231. .address {
  232. margin-bottom: 40rpx;
  233. justify-content: space-between;
  234. padding: 24rpx 32rpx;
  235. background: #FFFFFF;
  236. border-radius: 24rpx;
  237. }
  238. .card {
  239. & + & {
  240. margin-top: 32rpx;
  241. }
  242. }
  243. .order {
  244. margin-top: 40rpx;
  245. padding: 32rpx;
  246. background: #FAFAFF;
  247. border: 2rpx solid #FFFFFF;
  248. border-radius: 32rpx;
  249. &-header {
  250. font-family: PingFang SC;
  251. font-weight: 500;
  252. font-size: 36rpx;
  253. line-height: 1.4;
  254. color: #252545;
  255. }
  256. .row {
  257. margin-top: 32rpx;
  258. justify-content: space-between;
  259. font-family: PingFang SC;
  260. font-weight: 400;
  261. line-height: 1.4;
  262. &-label {
  263. font-size: 26rpx;
  264. color: #8B8B8B;
  265. }
  266. &-content {
  267. font-size: 28rpx;
  268. color: #393939;
  269. }
  270. }
  271. }
  272. .notice {
  273. margin-top: 40rpx;
  274. font-family: PingFang SC;
  275. font-weight: 400;
  276. &-header {
  277. font-size: 28rpx;
  278. line-height: 1.4;
  279. color: #393939;
  280. }
  281. &-content {
  282. margin-top: 24rpx;
  283. font-size: 24rpx;
  284. line-height: 1.4;
  285. color: #BABABA;
  286. }
  287. }
  288. .bottom {
  289. position: fixed;
  290. left: 0;
  291. bottom: 0;
  292. width: 100vw;
  293. // height: 270rpx;
  294. background: #FFFFFF;
  295. box-sizing: border-box;
  296. .agreement {
  297. display: flex;
  298. padding: 16rpx 40rpx;
  299. background: #EFEAFF;
  300. box-sizing: border-box;
  301. /deep/ .uv-checkbox-group {
  302. flex: none;
  303. }
  304. .desc {
  305. flex: 1;
  306. font-family: PingFang SC;
  307. font-size: 24rpx;
  308. font-weight: 400;
  309. line-height: 40rpx;
  310. color: #8B8B8B;
  311. }
  312. .highlight {
  313. color: $uni-color;
  314. }
  315. }
  316. .bar {
  317. padding: 24rpx 40rpx;
  318. padding-bottom: calc(env(safe-area-inset-bottom) + 24rpx);
  319. box-sizing: border-box;
  320. column-gap: 30rpx;
  321. .col {
  322. flex: 1;
  323. }
  324. .price {
  325. justify-content: flex-start;
  326. &-label {
  327. font-family: PingFang SC;
  328. font-weight: 400;
  329. font-size: 24rpx;
  330. line-height: 1.4;
  331. color: #626262;
  332. }
  333. &-unit {
  334. margin: 0 8rpx;
  335. font-family: PingFang SC;
  336. font-weight: 500;
  337. font-size: 24rpx;
  338. line-height: 1.4;
  339. color: #7451DE;
  340. }
  341. &-value {
  342. font-family: PingFang SC;
  343. font-weight: 500;
  344. font-size: 40rpx;
  345. line-height: 1.4;
  346. color: #7451DE;
  347. }
  348. }
  349. .btn {
  350. width: 100%;
  351. padding: 16rpx 0;
  352. box-sizing: border-box;
  353. font-family: PingFang SC;
  354. font-weight: 500;
  355. font-size: 36rpx;
  356. line-height: 1;
  357. color: #FFFFFF;
  358. background-image: linear-gradient(to right, #4B348F, #845CFA);
  359. border-radius: 41rpx;
  360. }
  361. }
  362. }
  363. </style>