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

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