建材商城系统20241014
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.

585 lines
17 KiB

7 months ago
  1. <template>
  2. <view class="hand-firm">
  3. <navbar
  4. title="确定下单"
  5. leftClick
  6. @leftClick="$utils.navigateBack"
  7. />
  8. <!-- 商品信息 -->
  9. <view class="content-wrapper">
  10. <uv-checkbox-group
  11. shape="circle"
  12. v-model="checkboxValue">
  13. <view class="section-wrapper">
  14. <view class="section-title">
  15. <view class="title-bar"></view>
  16. <text>商品信息</text>
  17. </view>
  18. <view class="product-card" v-for="(info, index) in productInfo.commonShop">
  19. <view class="checkbox">
  20. <uv-checkbox
  21. :name="info.id"
  22. activeColor="#eb3300"
  23. size="40rpx"
  24. icon-size="35rpx"
  25. ></uv-checkbox>
  26. </view>
  27. <image class="product-image" :src="info.image &&
  28. info.image.split(',')[0]" mode="aspectFill"></image>
  29. <view class="product-details">
  30. <view class="product-name">{{info.name || ''}}</view>
  31. <view class="product-tags">
  32. <text class="tag">快速下单</text>
  33. </view>
  34. <view class="product-price">
  35. <text class="price-value">¥{{info.price || ''}}</text>
  36. <text class="price-unit">/{{info.unit || ''}}</text>
  37. </view>
  38. <view class="selectNum">
  39. <uv-number-box v-model="info.selectNum"></uv-number-box>
  40. </view>
  41. </view>
  42. </view>
  43. </view>
  44. </uv-checkbox-group>
  45. <!-- 个人信息 -->
  46. <view class="section-wrapper">
  47. <view class="section-title">
  48. <view class="title-bar"></view>
  49. <text>填写个人信息</text>
  50. </view>
  51. <view class="address-section">
  52. <view v-if="addressTotal > 0" class="has-address">
  53. <view class="address-info" @click="openAddress">
  54. <view class="address-user">
  55. <text class="name">{{address.name}}</text>
  56. <text class="phone">{{address.phone}}</text>
  57. </view>
  58. <view class="address-detail">
  59. {{address.address}} {{address.addressDetails}}
  60. </view>
  61. <view class="address-action">
  62. <text class="address-tip">选择其他已添加过的地址</text>
  63. <view class="arrow-right"></view>
  64. </view>
  65. </view>
  66. <view class="address-tag">
  67. <text>已添加过的地址</text>
  68. </view>
  69. </view>
  70. <!-- 地址表单 -->
  71. <view v-else class="no-address">
  72. <redact-address-form
  73. ref="addressForm"
  74. @saveOrUpdate="handleAddressSave"
  75. ></redact-address-form>
  76. </view>
  77. </view>
  78. </view>
  79. </view>
  80. <!-- 底部按钮 -->
  81. <view class="order-submit">
  82. <button class="submit-btn-close" @click="cancelOrderFast">取消</button>
  83. <button class="submit-btn" @click="submitOrder">快捷下单{{ totalPrice }}</button>
  84. </view>
  85. <!-- 地址选择弹窗 -->
  86. <uv-popup ref="addressPopup" :round="30" style="padding-bottom: 90rpx;">
  87. <addressList ref="addressList" height="60vh" @select="selectAddress" />
  88. <view class="add-btn">
  89. <view @click="$utils.navigateTo('/pages_order/mine/address?type=back')" class="button-submit">新增地址</view>
  90. </view>
  91. </uv-popup>
  92. <kefu/>
  93. </view>
  94. </template>
  95. <script>
  96. import redactAddressForm from '../components/address/redactAddressForm.vue';
  97. import addressList from '../components/address/addressList.vue';
  98. export default {
  99. components: {
  100. redactAddressForm,
  101. addressList
  102. },
  103. data() {
  104. return {
  105. checkboxValue : [],
  106. productInfo: {
  107. commonShop : [],
  108. }, // 信息
  109. orderId: '', // 订单ID
  110. address: {
  111. name: '请选择地址',
  112. address: '',
  113. phone: ''
  114. },
  115. addressTotal: 0,
  116. orderInfo: [],
  117. isLoading: false, // 加载状态
  118. shouldSubmitOrder: false // 标记是否应该在获取地址后自动提交订单
  119. };
  120. },
  121. computed: {
  122. totalPrice(){
  123. if (!this.checkboxValue.length || !this.productInfo.commonShop) {
  124. return 0
  125. }
  126. let price = 0
  127. this.productInfo.commonShop.forEach(n => {
  128. if(this.checkboxValue.includes(n.id)){
  129. price += n.price * (n.selectNum || 1)
  130. }
  131. })
  132. return Number(price).toFixed(2)
  133. },
  134. },
  135. onLoad(options) {
  136. // 获取订单ID
  137. if (options.orderId) {
  138. this.orderId = options.orderId;
  139. this.getOrderInfo();
  140. }
  141. },
  142. onShow() {
  143. // 获取地址列表
  144. this.getAddressList();
  145. },
  146. methods: {
  147. // 获取订单信息
  148. getOrderInfo() {
  149. // this.$api('getOrderInfo', res => {
  150. // if (res.code === 200 && res.result[0]) {
  151. // // 如果返回商品信息,则设置商品信息 commonShop
  152. // this.productInfo = res.result[0];
  153. // this.productInfo.commonShop.forEach(n => {
  154. // this.checkboxValue.push(n.id)
  155. // })
  156. // }
  157. // });
  158. this.$api('getAddOrderInfo', {
  159. orderId : this.orderId
  160. }, res => {
  161. if (res.code === 200 && res.result) {
  162. // 如果返回商品信息,则设置商品信息 commonShop
  163. this.productInfo = res.result;
  164. this.productInfo.commonShop.forEach(n => {
  165. this.checkboxValue.push(n.id)
  166. })
  167. }
  168. });
  169. },
  170. // 获取地址列表
  171. getAddressList() {
  172. // 调用地址列表组件获取地址
  173. this.$refs.addressList.getAddressList().then(res => {
  174. if (res && res.total) {
  175. this.addressTotal = res.total;
  176. // 查找默认地址
  177. if (res.records && res.records.length > 0) {
  178. const defaultAddress = res.records.find(addr => addr.defaultFlag === '1');
  179. if (defaultAddress) {
  180. this.address = defaultAddress;
  181. } else {
  182. // 如果没有默认地址,使用第一个地址
  183. this.address = res.records[0];
  184. }
  185. // 如果标记为需要自动提交订单
  186. if (this.shouldSubmitOrder) {
  187. this.shouldSubmitOrder = false; // 重置标记
  188. this.submitOrder(true); // 执行提交订单,传入true表示不再验证地址
  189. }
  190. }
  191. } else {
  192. this.addressTotal = 0;
  193. }
  194. // 完成加载
  195. this.isLoading = false;
  196. }).catch(err => {
  197. console.error('获取地址列表失败', err);
  198. this.addressTotal = 0;
  199. this.isLoading = false;
  200. });
  201. },
  202. // 打开地址选择弹窗
  203. openAddress() {
  204. this.$refs.addressPopup.open('bottom');
  205. },
  206. // 选择地址
  207. selectAddress(address) {
  208. this.address = address;
  209. this.$refs.addressPopup.close();
  210. },
  211. // 处理地址保存
  212. handleAddressSave(address) {
  213. // 显示加载状态
  214. this.isLoading = true;
  215. // 保存地址
  216. this.$api('saveOrUpdateAddress', address, res => {
  217. if (res.code === 200) {
  218. uni.showToast({
  219. title: '地址保存成功',
  220. icon: 'success'
  221. });
  222. // 标记需要在获取地址后自动提交订单
  223. this.shouldSubmitOrder = true;
  224. // 重新获取地址列表
  225. this.getAddressList();
  226. } else {
  227. uni.showToast({
  228. title: res.message || '保存失败',
  229. icon: 'none'
  230. });
  231. this.isLoading = false;
  232. }
  233. });
  234. },
  235. // 提交订单
  236. submitOrder(skipAddressCheck = false) {
  237. if (!skipAddressCheck && this.addressTotal === 0) {
  238. const addressForm = this.$refs.addressForm;
  239. // 验证地址表单
  240. const isValid = addressForm.parameterVerification(addressForm.addressDetail);
  241. if (!isValid.auth) {
  242. uni.showToast({
  243. title: isValid.title,
  244. icon: 'none'
  245. });
  246. return;
  247. }
  248. // 显示加载状态
  249. this.isLoading = true;
  250. // 保存地址并继续
  251. addressForm.onSubmit();
  252. return;
  253. }
  254. // 显示加载中
  255. uni.showLoading({
  256. title: '提交订单中...'
  257. });
  258. let list = []
  259. this.productInfo.commonShop.forEach(n => {
  260. if(this.checkboxValue.includes(n.id)){
  261. list.push({
  262. num: n.selectNum || 1,
  263. shopId: n.id,
  264. })
  265. }
  266. })
  267. let data = {
  268. addressId: this.address.id,
  269. payType: 1, // 默认微信支付
  270. orderId: this.orderId,
  271. list: JSON.stringify(list),
  272. }
  273. /*
  274. {
  275. addressId: this.address.id,
  276. productId: this.productInfo.id,
  277. num : 1,
  278. payType: 1, // 默认微信支付
  279. orderId: this.orderId
  280. }
  281. */
  282. // 创建订单
  283. this.$api('createSumOrder', data, res => {
  284. uni.hideLoading();
  285. if (res.code === 200) {
  286. uni.requestPaymentWxPay(res)
  287. .then(e => {
  288. uni.showToast({
  289. title: '下单成功',
  290. icon: 'none'
  291. })
  292. this.paySuccess(res)
  293. }).catch(n => {
  294. setTimeout(uni.redirectTo, 700, {
  295. url: '/pages/index/order'
  296. })
  297. })
  298. } else {
  299. uni.showToast({
  300. title: res.message || '下单失败',
  301. icon: 'none'
  302. });
  303. }
  304. });
  305. },
  306. paySuccess(res){
  307. setTimeout(uni.redirectTo, 700, {
  308. url: '/pages/index/order'
  309. })
  310. },
  311. cancelOrderFast(){
  312. uni.showModal({
  313. title: '确认取消吗?',
  314. success : res => {
  315. if(res.confirm){
  316. this.$api('cancelOrderFast', {
  317. orderId : this.orderId
  318. }).then(res => {
  319. uni.showToast({
  320. title: '取消成功',
  321. icon: 'none',
  322. success() {
  323. uni.navigateBack(-1)
  324. }
  325. })
  326. })
  327. }
  328. }
  329. })
  330. },
  331. }
  332. }
  333. </script>
  334. <style scoped lang="scss">
  335. .hand-firm {
  336. min-height: 100vh;
  337. background-color: #f6f6f6;
  338. padding-bottom: 120rpx;
  339. .content-wrapper {
  340. padding: 20rpx;
  341. }
  342. .section-wrapper {
  343. margin-bottom: 20rpx;
  344. border-radius: 12rpx;
  345. overflow: hidden;
  346. width: 100%;
  347. background-color: #fff;
  348. }
  349. .section-title {
  350. display: flex;
  351. align-items: center;
  352. padding: 30rpx;
  353. border-bottom: 1rpx solid #f0f0f0;
  354. .title-bar {
  355. width: 6rpx;
  356. height: 30rpx;
  357. background-color: #D03F25;
  358. margin-right: 15rpx;
  359. border-radius: 3rpx;
  360. }
  361. text {
  362. font-size: 32rpx;
  363. font-weight: 500;
  364. color: #333;
  365. }
  366. }
  367. .product-card {
  368. display: flex;
  369. padding: 30rpx;
  370. .checkbox{
  371. display: flex;
  372. justify-content: center;
  373. align-items: center;
  374. }
  375. .product-image {
  376. width: 200rpx;
  377. height: 200rpx;
  378. margin-right: 30rpx;
  379. background-color: #f9f9f9;
  380. border-radius: 8rpx;
  381. }
  382. .product-details {
  383. flex: 1;
  384. display: flex;
  385. flex-direction: column;
  386. justify-content: space-between;
  387. gap: 6rpx;
  388. .product-name {
  389. font-size: 28rpx;
  390. font-weight: 500;
  391. color: #333;
  392. }
  393. .product-tags {
  394. display: flex;
  395. .tag {
  396. font-size: 24rpx;
  397. color: #D03F25;
  398. padding: 4rpx 12rpx;
  399. background-color: rgba(208, 63, 37, 0.1);
  400. border-radius: 14rpx;
  401. margin-right: 15rpx;
  402. }
  403. }
  404. .product-price {
  405. font-size: 26rpx;
  406. color: #666;
  407. .price-value {
  408. font-size: 30rpx;
  409. font-weight: 600;
  410. color: #D03F25;
  411. }
  412. }
  413. }
  414. }
  415. .address-section {
  416. .has-address {
  417. position: relative;
  418. .address-info {
  419. padding: 30rpx;
  420. .address-user {
  421. margin-bottom: 15rpx;
  422. .name {
  423. font-size: 32rpx;
  424. font-weight: 500;
  425. color: #333;
  426. margin-right: 30rpx;
  427. }
  428. .phone {
  429. font-size: 28rpx;
  430. color: #666;
  431. }
  432. }
  433. .address-detail {
  434. font-size: 28rpx;
  435. color: #666;
  436. line-height: 1.5;
  437. margin-bottom: 20rpx;
  438. }
  439. .address-action {
  440. display: flex;
  441. justify-content: space-between;
  442. align-items: center;
  443. padding-top: 20rpx;
  444. border-top: 1px solid #eee;
  445. .address-tip {
  446. font-size: 28rpx;
  447. color: #D03F25;
  448. }
  449. .arrow-right {
  450. width: 16rpx;
  451. height: 16rpx;
  452. border-top: 2rpx solid #D03F25;
  453. border-right: 2rpx solid #D03F25;
  454. transform: rotate(45deg);
  455. }
  456. }
  457. }
  458. .address-tag {
  459. position: absolute;
  460. top: 20rpx;
  461. right: 0;
  462. background-color: #D03F25;
  463. color: #fff;
  464. font-size: 24rpx;
  465. padding: 8rpx 20rpx;
  466. border-radius: 30rpx 0 0 30rpx;
  467. }
  468. }
  469. .no-address {
  470. padding: 20rpx;
  471. }
  472. }
  473. .order-submit {
  474. display: flex;
  475. padding: 20rpx 30rpx;
  476. background-color: #fff;
  477. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  478. gap: 20rpx;
  479. .submit-btn-close{
  480. height: 90rpx;
  481. line-height: 90rpx;
  482. text-align: center;
  483. background-color: rgba($uni-color, 0.2);
  484. border: 1rpx solid $uni-color;
  485. color: $uni-color;
  486. font-size: 32rpx;
  487. border-radius: 45rpx;
  488. border: none;
  489. flex: 1;
  490. }
  491. .submit-btn {
  492. height: 90rpx;
  493. line-height: 90rpx;
  494. text-align: center;
  495. background-color: $uni-color;
  496. color: #fff;
  497. font-size: 32rpx;
  498. border-radius: 45rpx;
  499. border: none;
  500. flex: 4;
  501. }
  502. }
  503. .add-btn {
  504. padding: 30rpx;
  505. .button-submit {
  506. display: flex;
  507. align-items: center;
  508. justify-content: center;
  509. width: 100%;
  510. height: 90rpx;
  511. background-color: #D03F25;
  512. color: #fff;
  513. font-size: 32rpx;
  514. border-radius: 45rpx;
  515. }
  516. }
  517. }
  518. </style>