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

571 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. },
  159. // 获取地址列表
  160. getAddressList() {
  161. // 调用地址列表组件获取地址
  162. this.$refs.addressList.getAddressList().then(res => {
  163. if (res && res.total) {
  164. this.addressTotal = res.total;
  165. // 查找默认地址
  166. if (res.records && res.records.length > 0) {
  167. const defaultAddress = res.records.find(addr => addr.defaultFlag === '1');
  168. if (defaultAddress) {
  169. this.address = defaultAddress;
  170. } else {
  171. // 如果没有默认地址,使用第一个地址
  172. this.address = res.records[0];
  173. }
  174. // 如果标记为需要自动提交订单
  175. if (this.shouldSubmitOrder) {
  176. this.shouldSubmitOrder = false; // 重置标记
  177. this.submitOrder(true); // 执行提交订单,传入true表示不再验证地址
  178. }
  179. }
  180. } else {
  181. this.addressTotal = 0;
  182. }
  183. // 完成加载
  184. this.isLoading = false;
  185. }).catch(err => {
  186. console.error('获取地址列表失败', err);
  187. this.addressTotal = 0;
  188. this.isLoading = false;
  189. });
  190. },
  191. // 打开地址选择弹窗
  192. openAddress() {
  193. this.$refs.addressPopup.open('bottom');
  194. },
  195. // 选择地址
  196. selectAddress(address) {
  197. this.address = address;
  198. this.$refs.addressPopup.close();
  199. },
  200. // 处理地址保存
  201. handleAddressSave(address) {
  202. // 显示加载状态
  203. this.isLoading = true;
  204. // 保存地址
  205. this.$api('saveOrUpdateAddress', address, res => {
  206. if (res.code === 200) {
  207. uni.showToast({
  208. title: '地址保存成功',
  209. icon: 'success'
  210. });
  211. // 标记需要在获取地址后自动提交订单
  212. this.shouldSubmitOrder = true;
  213. // 重新获取地址列表
  214. this.getAddressList();
  215. } else {
  216. uni.showToast({
  217. title: res.message || '保存失败',
  218. icon: 'none'
  219. });
  220. this.isLoading = false;
  221. }
  222. });
  223. },
  224. // 提交订单
  225. submitOrder(skipAddressCheck = false) {
  226. if (!skipAddressCheck && this.addressTotal === 0) {
  227. const addressForm = this.$refs.addressForm;
  228. // 验证地址表单
  229. const isValid = addressForm.parameterVerification(addressForm.addressDetail);
  230. if (!isValid.auth) {
  231. uni.showToast({
  232. title: isValid.title,
  233. icon: 'none'
  234. });
  235. return;
  236. }
  237. // 显示加载状态
  238. this.isLoading = true;
  239. // 保存地址并继续
  240. addressForm.onSubmit();
  241. return;
  242. }
  243. // 显示加载中
  244. uni.showLoading({
  245. title: '提交订单中...'
  246. });
  247. let list = []
  248. this.productInfo.commonShop.forEach(n => {
  249. if(this.checkboxValue.includes(n.id)){
  250. list.push({
  251. num: n.selectNum || 1,
  252. shopId: n.id,
  253. })
  254. }
  255. })
  256. let data = {
  257. addressId: this.address.id,
  258. payType: 1, // 默认微信支付
  259. orderId: this.orderId,
  260. list: JSON.stringify(list),
  261. }
  262. /*
  263. {
  264. addressId: this.address.id,
  265. productId: this.productInfo.id,
  266. num : 1,
  267. payType: 1, // 默认微信支付
  268. orderId: this.orderId
  269. }
  270. */
  271. // 创建订单
  272. this.$api('createSumOrder', data, res => {
  273. uni.hideLoading();
  274. if (res.code === 200) {
  275. uni.requestPaymentWxPay(res)
  276. .then(e => {
  277. uni.showToast({
  278. title: '下单成功',
  279. icon: 'none'
  280. })
  281. this.paySuccess(res)
  282. }).catch(n => {
  283. setTimeout(uni.redirectTo, 700, {
  284. url: '/pages/index/order'
  285. })
  286. })
  287. } else {
  288. uni.showToast({
  289. title: res.message || '下单失败',
  290. icon: 'none'
  291. });
  292. }
  293. });
  294. },
  295. paySuccess(res){
  296. setTimeout(uni.redirectTo, 700, {
  297. url: '/pages/index/order'
  298. })
  299. },
  300. cancelOrderFast(){
  301. uni.showModal({
  302. title: '确认取消吗?',
  303. success : res => {
  304. if(res.confirm){
  305. this.$api('cancelOrderFast', {
  306. orderId : this.orderId
  307. }).then(res => {
  308. uni.showToast({
  309. title: '取消成功',
  310. icon: 'none',
  311. success() {
  312. uni.navigateBack(-1)
  313. }
  314. })
  315. })
  316. }
  317. }
  318. })
  319. },
  320. }
  321. }
  322. </script>
  323. <style scoped lang="scss">
  324. .hand-firm {
  325. min-height: 100vh;
  326. background-color: #f6f6f6;
  327. padding-bottom: 120rpx;
  328. .content-wrapper {
  329. padding: 20rpx;
  330. }
  331. .section-wrapper {
  332. margin-bottom: 20rpx;
  333. border-radius: 12rpx;
  334. overflow: hidden;
  335. width: 100%;
  336. background-color: #fff;
  337. }
  338. .section-title {
  339. display: flex;
  340. align-items: center;
  341. padding: 30rpx;
  342. border-bottom: 1rpx solid #f0f0f0;
  343. .title-bar {
  344. width: 6rpx;
  345. height: 30rpx;
  346. background-color: #D03F25;
  347. margin-right: 15rpx;
  348. border-radius: 3rpx;
  349. }
  350. text {
  351. font-size: 32rpx;
  352. font-weight: 500;
  353. color: #333;
  354. }
  355. }
  356. .product-card {
  357. display: flex;
  358. padding: 30rpx;
  359. .checkbox{
  360. display: flex;
  361. justify-content: center;
  362. align-items: center;
  363. }
  364. .product-image {
  365. width: 200rpx;
  366. height: 200rpx;
  367. margin-right: 30rpx;
  368. background-color: #f9f9f9;
  369. border-radius: 8rpx;
  370. }
  371. .product-details {
  372. flex: 1;
  373. display: flex;
  374. flex-direction: column;
  375. justify-content: space-between;
  376. gap: 6rpx;
  377. .product-name {
  378. font-size: 28rpx;
  379. font-weight: 500;
  380. color: #333;
  381. }
  382. .product-tags {
  383. display: flex;
  384. .tag {
  385. font-size: 24rpx;
  386. color: #D03F25;
  387. padding: 4rpx 12rpx;
  388. background-color: rgba(208, 63, 37, 0.1);
  389. border-radius: 14rpx;
  390. margin-right: 15rpx;
  391. }
  392. }
  393. .product-price {
  394. font-size: 26rpx;
  395. color: #666;
  396. .price-value {
  397. font-size: 30rpx;
  398. font-weight: 600;
  399. color: #D03F25;
  400. }
  401. }
  402. }
  403. }
  404. .address-section {
  405. .has-address {
  406. position: relative;
  407. .address-info {
  408. padding: 30rpx;
  409. .address-user {
  410. margin-bottom: 15rpx;
  411. .name {
  412. font-size: 32rpx;
  413. font-weight: 500;
  414. color: #333;
  415. margin-right: 30rpx;
  416. }
  417. .phone {
  418. font-size: 28rpx;
  419. color: #666;
  420. }
  421. }
  422. .address-detail {
  423. font-size: 28rpx;
  424. color: #666;
  425. line-height: 1.5;
  426. margin-bottom: 20rpx;
  427. }
  428. .address-action {
  429. display: flex;
  430. justify-content: space-between;
  431. align-items: center;
  432. padding-top: 20rpx;
  433. border-top: 1px solid #eee;
  434. .address-tip {
  435. font-size: 28rpx;
  436. color: #D03F25;
  437. }
  438. .arrow-right {
  439. width: 16rpx;
  440. height: 16rpx;
  441. border-top: 2rpx solid #D03F25;
  442. border-right: 2rpx solid #D03F25;
  443. transform: rotate(45deg);
  444. }
  445. }
  446. }
  447. .address-tag {
  448. position: absolute;
  449. top: 20rpx;
  450. right: 0;
  451. background-color: #D03F25;
  452. color: #fff;
  453. font-size: 24rpx;
  454. padding: 8rpx 20rpx;
  455. border-radius: 30rpx 0 0 30rpx;
  456. }
  457. }
  458. .no-address {
  459. padding: 20rpx;
  460. }
  461. }
  462. .order-submit {
  463. display: flex;
  464. padding: 20rpx 30rpx;
  465. background-color: #fff;
  466. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  467. gap: 20rpx;
  468. .submit-btn-close{
  469. height: 90rpx;
  470. line-height: 90rpx;
  471. text-align: center;
  472. background-color: rgba($uni-color, 0.2);
  473. border: 1rpx solid $uni-color;
  474. color: $uni-color;
  475. font-size: 32rpx;
  476. border-radius: 45rpx;
  477. border: none;
  478. flex: 1;
  479. }
  480. .submit-btn {
  481. height: 90rpx;
  482. line-height: 90rpx;
  483. text-align: center;
  484. background-color: $uni-color;
  485. color: #fff;
  486. font-size: 32rpx;
  487. border-radius: 45rpx;
  488. border: none;
  489. flex: 4;
  490. }
  491. }
  492. .add-btn {
  493. padding: 30rpx;
  494. .button-submit {
  495. display: flex;
  496. align-items: center;
  497. justify-content: center;
  498. width: 100%;
  499. height: 90rpx;
  500. background-color: #D03F25;
  501. color: #fff;
  502. font-size: 32rpx;
  503. border-radius: 45rpx;
  504. }
  505. }
  506. }
  507. </style>