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

602 lines
18 KiB

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