run-errands.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. <template>
  2. <view class="flex-common-css iPhone-bottom box-border-box">
  3. <Nav title="跑腿代拿" leftIconColor="#333" titleColor="#333"></Nav>
  4. <scroll-view :scroll-y="true" class="scroll-view-css box-border-box p-24">
  5. <view class="box-border-box mb-20">
  6. <uni-swiper-dot :info="bannerList" mode="dot" :dotsStyles="dotStyle" :current="current">
  7. <swiper class="swiper" @change="change" :autoplay='true' :circular='true' interval="4000">
  8. <swiper-item v-for="(item,i) in bannerList" :key="i">
  9. <image class="swiper-item" :src="item.image" mode="aspectFill">
  10. </image>
  11. </swiper-item>
  12. </swiper>
  13. </uni-swiper-dot>
  14. </view>
  15. <view class="align-items-center p-16 box-border-box b-rad-20 bgc-f mb-20">
  16. <view class="flex-1 flex-direction align-items-center b-rad-20 ptb-15" v-for="(item,index) in serveData"
  17. :key="index" @click="serveClick(item)"
  18. :class="formData.errands_id === item.errands_id?'bgc-BCEAFF':'bgc-f'">
  19. <image class="item-img mb-8 b-rad-16" :src="item.img" mode=""></image>
  20. <view class="text-align-center fs-26">
  21. {{item.name}}
  22. </view>
  23. </view>
  24. </view>
  25. <view class="form-box bgc-f b-rad-16 p-20 box-border-box">
  26. <view class="align-items-flex-start mb-20">
  27. <view class="fs-26 fc-3 mr-20 flex-shrink0 pt-15">
  28. <text class="fc-ff3939">*</text>服务描述 :
  29. </view>
  30. <view class="textarea-box flex-1 bgc-F6F6F6 b-rad-16">
  31. <textarea class="textarea" v-model="formData.serve_desc" maxlength='50'
  32. :placeholder="errandsData.desc" />
  33. <view class="fs-24 fc-6 tj-num">
  34. {{formData.serve_desc.length}}/50
  35. </view>
  36. </view>
  37. </view>
  38. <view class="align-items-flex-start mb-20">
  39. <view class="fs-26 fc-3 mr-20 flex-shrink0 pt-15">
  40. 图片介绍 :
  41. </view>
  42. <view class="up-img-box align-items-center flex-wrap-wrap">
  43. <view class="up-img" v-for="(item, index) in formData.images" :key="index">
  44. <image class="img" :src="item" mode="aspectFill"></image>
  45. <uni-icons @click="deleteImg(index)" class="delete-icons" color="#000" type="clear"
  46. size="20"></uni-icons>
  47. </view>
  48. <view @click="upImg" v-if="formData.images.length<= 5"
  49. class="add-img-box flex-direction flex-justify-center align-items-center bgc-F6F6F6">
  50. <image class="add-img" src="@/static/img/my/sc.png" mode=""></image>
  51. <view class="fs-20 fc-9">
  52. 上传照片
  53. </view>
  54. <view class="fs-20 fc-9">
  55. (最多6张)
  56. </view>
  57. </view>
  58. </view>
  59. </view>
  60. <view class="align-items-flex-start mb-20">
  61. <view class="fs-26 fc-3 mr-20 flex-shrink0 pt-15">
  62. <text class="fc-ff3939">*</text>服务地址 :
  63. </view>
  64. <view class="textarea-box flex-1 bgc-F6F6F6 b-rad-16">
  65. <textarea class="textarea" cursor-spacing='30' v-model="formData.serve_address" maxlength='50'
  66. :placeholder="errandsData.add_str" />
  67. <view class="fs-24 fc-6 tj-num">
  68. {{formData.serve_address.length}}/50
  69. </view>
  70. </view>
  71. </view>
  72. <view class="align-items-center ptb-24 b-b-EFF2F5 mb-20" @click="openAddress">
  73. <view class="fs-26 fc-3 mr-20 flex-shrink0">
  74. <text class="fc-ff3939" v-if="errandsData.errands_id != 4">*</text>送达地址 :
  75. </view>
  76. <view class="text-overflow-ellipsis-one fs-26" :class="formData.address_id?'fc-0':'fc-9'">
  77. {{formData.address_id?addressData.school_name + addressData.dorm_name + addressData.address :'选择地址'}}
  78. </view>
  79. </view>
  80. <view class="align-items-center ptb-24 b-b-EFF2F5 mb-20" @click="openTime">
  81. <view class="fs-26 fc-3 mr-20 flex-shrink0">
  82. <text class="fc-ff3939">*</text>{{errandsData.time_str}} :
  83. </view>
  84. <view class="text-overflow-ellipsis-one fs-26" :class="deliverText?'fc-0':'fc-9'">
  85. {{deliverText?'预计'+ deliverText+ '送达':'选择'+ errandsData.time_str}}
  86. </view>
  87. </view>
  88. <view class="align-items-center ptb-24 b-b-EFF2F5 mb-20" @click="openItem"
  89. v-if="errandsData.item && errandsData.item.length>0">
  90. <view class="fs-26 fc-3 mr-20 flex-shrink0">
  91. <text class="fc-ff3939">*</text>{{errandsData.item_name}} :
  92. </view>
  93. <view class="text-overflow-ellipsis-one fs-26" :class="itemData.item_id?'fc-0':'fc-9'">
  94. {{itemData.item_id?itemData.name:'选择'+ errandsData.item_name}}
  95. </view>
  96. </view>
  97. <view class="align-items-center ptb-24 b-b-EFF2F5 mb-20">
  98. <view class="fs-26 fc-3 mr-20 flex-shrink0">
  99. <text class="fc-ff3939">*</text>是否上楼 :
  100. </view>
  101. <view class="fs-26 align-items-center">
  102. <view class="align-items-center mr-50" @click="isUpClick(0)">
  103. <uni-icons class="mr-10" v-if="formData.is_up === 0" type="checkbox-filled" size="22"
  104. color="#4495FD"></uni-icons>
  105. <uni-icons class="mr-10" v-else type="circle" size="22" color="#000"></uni-icons>
  106. <view class="fs-28 mr-10">
  107. 否
  108. </view>
  109. <view class="fs-28">
  110. (0元)
  111. </view>
  112. </view>
  113. <view class="align-items-center" @click="isUpClick(1)">
  114. <uni-icons class="mr-10" v-if="formData.is_up === 1" type="checkbox-filled" size="22"
  115. color="#4495FD"></uni-icons>
  116. <uni-icons class="mr-10" v-else type="circle" size="22" color="#000"></uni-icons>
  117. <view class="fs-28 mr-10">
  118. 是
  119. </view>
  120. <view class="fs-28 mr-10 fc-FB3D03">
  121. ¥{{errandsData.up_price}}
  122. </view>
  123. </view>
  124. </view>
  125. </view>
  126. <view class="align-items-center ptb-24 b-b-EFF2F5 mb-20">
  127. <view class="fs-26 fc-3 mr-20 flex-shrink0">
  128. 打赏金额 :
  129. </view>
  130. <view class="">
  131. <input v-model="formData.reward_money" type="digit" cursor-spacing='20' class="uni-input"
  132. placeholder="自定义金额 0~1000" @blur="amountNum" @input="handleInput" />
  133. </view>
  134. </view>
  135. </view>
  136. </scroll-view>
  137. <view class="bgc-f b-t-F9F9F9 plr-24 box-border-box">
  138. <view class="but-box align-items-center box-border-box flex-justify-space b-rad-200">
  139. <view class="but-left align-items-center flex-1 bgc-3 pl-24 box-border-box" @click="openPop">
  140. <view class="fs-32 fc-FB4845 mr-20 fw-600">
  141. ¥ {{amount}}
  142. </view>
  143. <view class="fs-24 fc-f">
  144. (点击展开明细)
  145. </view>
  146. </view>
  147. <view class="but bgc-5CC2FF fs-26 fc-f text-align-center" @click.stop="goToUrl">
  148. 去下单
  149. </view>
  150. </view>
  151. </view>
  152. <uni-popup ref="popup" type="bottom" border-radius="10px 10px 0 0" background-color="#fff"
  153. :is-mask-click="false">
  154. <view class="pop-box box-border-box">
  155. <uni-icons @click="closePop" class="box-close" type="closeempty" size="20"></uni-icons>
  156. <view class="bgc-f p-24 box-border-box">
  157. <view class="fs-34 fw-600 mb-20">
  158. 费用明细
  159. </view>
  160. <view class="align-items-center flex-justify-space mb-20">
  161. <view class="fs-28 fc-6">
  162. 基础服务费
  163. </view>
  164. <view class="fs-28 fc-0">
  165. ¥ {{errandsData.price}}
  166. </view>
  167. </view>
  168. <view class="align-items-center flex-justify-space mb-20">
  169. <view class="fs-28 fc-6">
  170. {{errandsData.item_name}}加价
  171. </view>
  172. <view class="fs-28 fc-0">
  173. ¥ {{itemData.price?itemData.price:'0.00'}}
  174. </view>
  175. </view>
  176. <view class="align-items-center flex-justify-space mb-20">
  177. <view class="fs-28 fc-6">
  178. 上楼加价
  179. </view>
  180. <view class="fs-28 fc-0">
  181. ¥ {{formData.is_up === 1?errandsData.up_price:'0.00'}}
  182. </view>
  183. </view>
  184. <view class="align-items-center flex-justify-space">
  185. <view class="fs-28 fc-6">
  186. 打赏金额
  187. </view>
  188. <view class="fs-28 fc-0">
  189. ¥ {{formData.reward_money?(Number(formData.reward_money)).toFixed(2):'0.00'}}
  190. </view>
  191. </view>
  192. </view>
  193. <view class="p-20 box-border-box bgc-f b-t-F9F9F9">
  194. <view class="but-box align-items-center box-border-box flex-justify-space b-rad-200">
  195. <view class="but-left align-items-center flex-1 bgc-3 pl-24 box-border-box" @click="closePop">
  196. <view class="fs-32 fc-FB4845 mr-20 fw-600">
  197. ¥ {{amount}}
  198. </view>
  199. </view>
  200. <view class="but bgc-5CC2FF fs-26 fc-f text-align-center" @click.stop="goToUrl">
  201. 去下单
  202. </view>
  203. </view>
  204. </view>
  205. </view>
  206. </uni-popup>
  207. <selectAddressVue ref="selectAddress" @giveAddress='giveAddress'></selectAddressVue>
  208. <timeVue ref="selectTime" @giveTime='giveTime' @timeAndType='timeAndType'></timeVue>
  209. <selectModVue ref="selectModRef" @selectData='selectData' :popTitleName='errandsData.item_name'></selectModVue>
  210. </view>
  211. </template>
  212. <script>
  213. import selectAddressVue from './confirm-mod/select-address.vue';
  214. import timeVue from './confirm-mod/time.vue';
  215. import selectModVue from './run-errands-mod/select-mod.vue';
  216. import {
  217. getAddressDefault
  218. } from '@/api/member.js'
  219. import {
  220. errandsBanner,
  221. errandsServe,
  222. errandsCreate
  223. } from "@/api/errands.js"
  224. import tools from '@/service/tools';
  225. import {
  226. upLoadingFileOss
  227. } from "@/service/upLoadingFile.js"
  228. export default {
  229. components: {
  230. selectAddressVue,
  231. timeVue,
  232. selectModVue
  233. },
  234. data() {
  235. return {
  236. bannerList: [],
  237. dotStyle: {
  238. width: 6,
  239. bottom: 6,
  240. backgroundColor: 'rgba(51, 51, 51,0.3)',
  241. border: '1px rgba(0,0,0,0) solid',
  242. color: '#fff',
  243. selectedBackgroundColor: 'rgba(79, 179, 239,0.9)',
  244. selectedBorder: '1px rgba(0,0,0,0) solid'
  245. },
  246. current: 0,
  247. serveData: [], // 类型
  248. errandsData: {}, // 类型对应的数据
  249. formData: {
  250. errands_id: 0,
  251. serve_desc: '',
  252. serve_address: '',
  253. images: [],
  254. address_id: '',
  255. time_type: '',
  256. serve_time: '',
  257. item_id: 0,
  258. is_up: 0,
  259. reward_money: ''
  260. },
  261. deliverText: '',
  262. addressData: {},
  263. itemData: {},
  264. amount: '0.00',
  265. };
  266. },
  267. onShow() {
  268. let isConfirmAddressFlag = uni.getStorageSync('isConfirmAddressFlag');
  269. if (isConfirmAddressFlag.flag) {
  270. this.openAddress(isConfirmAddressFlag);
  271. }
  272. },
  273. onHide() {
  274. uni.removeStorageSync('isConfirmAddressFlag');
  275. },
  276. onUnload() {
  277. uni.removeStorageSync('isConfirmAddressFlag');
  278. },
  279. onLoad(data) {
  280. this.getAddressDefault();
  281. },
  282. mounted() {
  283. this.getBanner()
  284. this.getErrandsServe()
  285. },
  286. methods: {
  287. goToUrl() {
  288. errandsCreate(this.formData).then((res) => {
  289. if (res.code === 1) {
  290. uni.redirectTo({
  291. url: `/pages-takeout/run-errands-mod/pay-page?orderId=${res.data.order_id}`
  292. });
  293. }
  294. })
  295. },
  296. isUpClick(num) {
  297. this.formData.is_up = num
  298. this.amountNum()
  299. },
  300. handleInput(event) {
  301. let value = event.detail.value;
  302. // 只允许数字和小数点
  303. value = value.replace(/[^0-9.]/g, '');
  304. // 禁止输入多个小数点
  305. const parts = value.split('.'); // 按小数点分割字符串
  306. if (parts.length > 2) { // 如果小数点数量超过1个
  307. value = parts[0] + '.' + parts.slice(1).join(''); // 只保留第一个小数点
  308. }
  309. // 限制小数位数最多为1位
  310. if (parts.length === 2 && parts[1].length > 1) {
  311. value = parts[0] + '.' + parts[1].charAt(0); // 只保留第一位小数
  312. tools.error('金额只会保留一位小数')
  313. }
  314. // 禁止以0开头的多个0(例如:001 -> 1)
  315. value = value.replace(/^0+(\d+)/, '$1');
  316. // 更新输入框的值
  317. this.formData.reward_money = value;
  318. // 手动触发 input 事件,确保 v-model 更新
  319. this.$nextTick(() => {
  320. event.target.value = value; // 更新输入框的实际值
  321. });
  322. },
  323. amountNum() {
  324. this.amount = 0
  325. if (this.errandsData.price) {
  326. this.amount = (Number(this.amount) + Number(this.errandsData.price)).toFixed(2)
  327. }
  328. if (this.itemData.price) {
  329. this.amount = (Number(this.amount) + Number(this.itemData.price)).toFixed(2)
  330. }
  331. if (this.formData.is_up === 1) {
  332. this.amount = (Number(this.amount) + Number(this.errandsData.up_price)).toFixed(2)
  333. }
  334. if (this.formData.reward_money) {
  335. this.amount = (Number(this.amount) + Number(this.formData.reward_money)).toFixed(2)
  336. }
  337. },
  338. openPop() {
  339. this.$refs.popup.open()
  340. },
  341. closePop() {
  342. this.$refs.popup.close()
  343. },
  344. selectData(item) {
  345. if (item) {
  346. this.itemData = item
  347. this.formData.item_id = item.item_id
  348. this.amountNum()
  349. }
  350. },
  351. openItem() {
  352. this.$refs.selectModRef.getList(this.formData.item_id, this.errandsData.item);
  353. },
  354. openTime() {
  355. // 选择时间
  356. this.$refs.selectTime.getList(0, this.errandsData.time);
  357. },
  358. timeAndType(type, time) {
  359. // 时间选择
  360. this.formData.time_type = type
  361. this.formData.serve_time = time
  362. if (type == 1) {
  363. this.deliverText = '今天' + time
  364. } else {
  365. this.deliverText = '明天' + time
  366. }
  367. console.log(type, time, '&&&&&&&&&&&&&&&&&&&&&&&&');
  368. },
  369. giveTime(e) {
  370. console.log(e);
  371. },
  372. openAddress(addressData) {
  373. // 选择地址
  374. if (this.addressData.id) {
  375. this.$refs.selectAddress.id = this.addressData.id;
  376. }
  377. if (addressData && addressData.is_default == 1) {
  378. this.$refs.selectAddress.id = addressData.id;
  379. this.$refs.selectAddress.getList(addressData.id);
  380. } else {
  381. this.$refs.selectAddress.getList();
  382. }
  383. },
  384. // 获取默认地址
  385. getAddressDefault() {
  386. getAddressDefault().then((res) => {
  387. if (res.code === 1) {
  388. this.addressData = res.data
  389. if (this.addressData.id) {
  390. this.formData.address_id = this.addressData.id;
  391. }
  392. }
  393. })
  394. },
  395. giveAddress(e) {
  396. this.addressData = e;
  397. this.formData.address_id = this.addressData.id;
  398. },
  399. deleteImg(index) {
  400. this.formData.images.splice(index, 1)
  401. },
  402. upImg() {
  403. uni.chooseImage({
  404. count: 6,
  405. sizeType: ["compressed", "camera"],
  406. sourceType: ["album", "camera"],
  407. success: (res) => {
  408. res.tempFiles.forEach((file) => {
  409. this.uploadingFile(file);
  410. })
  411. }
  412. })
  413. },
  414. uploadingFile(file) {
  415. upLoadingFileOss(file.path).then((res) => {
  416. if (res) {
  417. this.formData.images.push(res)
  418. } else {
  419. tools.error('图片上传失败')
  420. }
  421. })
  422. },
  423. serveClick(item) {
  424. if (this.formData.errands_id === item.errands_id) {
  425. return
  426. }
  427. this.formData.errands_id = item.errands_id
  428. this.errandsData = item
  429. this.itemData = {}
  430. this.deliverText = ''
  431. this.amountNum()
  432. },
  433. getBanner() {
  434. errandsBanner().then((res) => {
  435. if (res.code === 1) {
  436. this.bannerList = res.data;
  437. }
  438. })
  439. },
  440. getErrandsServe() {
  441. errandsServe().then((res) => {
  442. if (res.code === 1) {
  443. this.serveData = res.data
  444. this.formData.errands_id = this.serveData[0].errands_id
  445. this.errandsData = this.serveData[0]
  446. this.amountNum()
  447. }
  448. })
  449. },
  450. change(e) {
  451. this.current = e.detail.current
  452. },
  453. },
  454. }
  455. </script>
  456. <style lang="scss" scoped>
  457. .pop-box {
  458. position: relative;
  459. }
  460. .box-close {
  461. position: absolute;
  462. top: 24rpx;
  463. right: 22rpx;
  464. }
  465. .swiper {
  466. width: 702rpx;
  467. height: 280rpx;
  468. border-radius: 20rpx;
  469. .swiper-item {
  470. width: 702rpx;
  471. height: 280rpx;
  472. border-radius: 20rpx;
  473. }
  474. }
  475. .item-img {
  476. width: 60rpx;
  477. height: 60rpx;
  478. }
  479. .up-img {
  480. width: 150rpx;
  481. height: 150rpx;
  482. margin: 0 8rpx 8rpx 0;
  483. position: relative;
  484. .img {
  485. width: 100%;
  486. height: 100%;
  487. border-radius: 10rpx;
  488. }
  489. .delete-icons {
  490. position: absolute;
  491. right: 0;
  492. top: 0;
  493. }
  494. }
  495. .textarea-box {
  496. height: 150rpx;
  497. position: relative;
  498. }
  499. .tj-num {
  500. position: absolute;
  501. right: 10rpx;
  502. bottom: 10rpx;
  503. }
  504. .textarea {
  505. width: 100%;
  506. height: 100%;
  507. padding: 20rpx;
  508. box-sizing: border-box;
  509. }
  510. .add-img-box {
  511. width: 150rpx;
  512. height: 150rpx;
  513. border: 1px dashed #666;
  514. .add-img {
  515. width: 36rpx;
  516. height: 36rpx;
  517. }
  518. }
  519. .but-box {
  520. height: 80rpx;
  521. }
  522. .but-left {
  523. height: 100%;
  524. line-height: 80rpx;
  525. border-radius: 200rpx 0 0 200rpx;
  526. }
  527. .but {
  528. width: 180rpx;
  529. height: 100%;
  530. line-height: 80rpx;
  531. border-radius: 0 200rpx 200rpx 0;
  532. }
  533. </style>