run-errands.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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. } else {
  294. tools.error(res.msg)
  295. }
  296. })
  297. },
  298. isUpClick(num) {
  299. this.formData.is_up = num
  300. this.amountNum()
  301. },
  302. handleInput(event) {
  303. let value = event.detail.value;
  304. // 只允许数字和小数点
  305. value = value.replace(/[^0-9.]/g, '');
  306. // 禁止输入多个小数点
  307. const parts = value.split('.'); // 按小数点分割字符串
  308. if (parts.length > 2) { // 如果小数点数量超过1个
  309. value = parts[0] + '.' + parts.slice(1).join(''); // 只保留第一个小数点
  310. }
  311. // 限制小数位数最多为1位
  312. if (parts.length === 2 && parts[1].length > 1) {
  313. value = parts[0] + '.' + parts[1].charAt(0); // 只保留第一位小数
  314. tools.error('金额只会保留一位小数')
  315. }
  316. // 禁止以0开头的多个0(例如:001 -> 1)
  317. value = value.replace(/^0+(\d+)/, '$1');
  318. // 更新输入框的值
  319. this.formData.reward_money = value;
  320. // 手动触发 input 事件,确保 v-model 更新
  321. this.$nextTick(() => {
  322. event.target.value = value; // 更新输入框的实际值
  323. });
  324. },
  325. amountNum() {
  326. this.amount = 0
  327. if (this.errandsData.price) {
  328. this.amount = (Number(this.amount) + Number(this.errandsData.price)).toFixed(2)
  329. }
  330. if (this.itemData.price) {
  331. this.amount = (Number(this.amount) + Number(this.itemData.price)).toFixed(2)
  332. }
  333. if (this.formData.is_up === 1) {
  334. this.amount = (Number(this.amount) + Number(this.errandsData.up_price)).toFixed(2)
  335. }
  336. if (this.formData.reward_money) {
  337. this.amount = (Number(this.amount) + Number(this.formData.reward_money)).toFixed(2)
  338. }
  339. },
  340. openPop() {
  341. this.$refs.popup.open()
  342. },
  343. closePop() {
  344. this.$refs.popup.close()
  345. },
  346. selectData(item) {
  347. if (item) {
  348. this.itemData = item
  349. this.formData.item_id = item.item_id
  350. this.amountNum()
  351. }
  352. },
  353. openItem() {
  354. this.$refs.selectModRef.getList(this.formData.item_id, this.errandsData.item);
  355. },
  356. openTime() {
  357. // 选择时间
  358. this.$refs.selectTime.getList(0, this.errandsData.time);
  359. },
  360. timeAndType(type, time) {
  361. // 时间选择
  362. this.formData.time_type = type
  363. this.formData.serve_time = time
  364. if (type == 1) {
  365. this.deliverText = '今天' + time
  366. } else {
  367. this.deliverText = '明天' + time
  368. }
  369. console.log(type, time, '&&&&&&&&&&&&&&&&&&&&&&&&');
  370. },
  371. giveTime(e) {
  372. console.log(e);
  373. },
  374. openAddress(addressData) {
  375. // 选择地址
  376. if (this.addressData.id) {
  377. this.$refs.selectAddress.id = this.addressData.id;
  378. }
  379. if (addressData && addressData.is_default == 1) {
  380. this.$refs.selectAddress.id = addressData.id;
  381. this.$refs.selectAddress.getList(addressData.id);
  382. } else {
  383. this.$refs.selectAddress.getList();
  384. }
  385. },
  386. // 获取默认地址
  387. getAddressDefault() {
  388. getAddressDefault().then((res) => {
  389. if (res.code === 1) {
  390. this.addressData = res.data
  391. if (this.addressData.id) {
  392. this.formData.address_id = this.addressData.id;
  393. }
  394. }
  395. })
  396. },
  397. giveAddress(e) {
  398. this.addressData = e;
  399. this.formData.address_id = this.addressData.id;
  400. },
  401. deleteImg(index) {
  402. this.formData.images.splice(index, 1)
  403. },
  404. upImg() {
  405. uni.chooseImage({
  406. count: 6,
  407. sizeType: ["compressed", "camera"],
  408. sourceType: ["album", "camera"],
  409. success: (res) => {
  410. res.tempFiles.forEach((file) => {
  411. this.uploadingFile(file);
  412. })
  413. }
  414. })
  415. },
  416. uploadingFile(file) {
  417. upLoadingFileOss(file.path).then((res) => {
  418. if (res) {
  419. this.formData.images.push(res)
  420. } else {
  421. tools.error('图片上传失败')
  422. }
  423. })
  424. },
  425. serveClick(item) {
  426. if (this.formData.errands_id === item.errands_id) {
  427. return
  428. }
  429. this.formData.errands_id = item.errands_id
  430. this.errandsData = item
  431. this.itemData = {}
  432. this.deliverText = ''
  433. this.amountNum()
  434. },
  435. getBanner() {
  436. errandsBanner().then((res) => {
  437. if (res.code === 1) {
  438. this.bannerList = res.data;
  439. }
  440. })
  441. },
  442. getErrandsServe() {
  443. errandsServe().then((res) => {
  444. if (res.code === 1) {
  445. this.serveData = res.data
  446. this.formData.errands_id = this.serveData[0].errands_id
  447. this.errandsData = this.serveData[0]
  448. this.amountNum()
  449. }
  450. })
  451. },
  452. change(e) {
  453. this.current = e.detail.current
  454. },
  455. },
  456. }
  457. </script>
  458. <style lang="scss" scoped>
  459. .pop-box {
  460. position: relative;
  461. }
  462. .box-close {
  463. position: absolute;
  464. top: 24rpx;
  465. right: 22rpx;
  466. }
  467. .swiper {
  468. width: 702rpx;
  469. height: 280rpx;
  470. border-radius: 20rpx;
  471. .swiper-item {
  472. width: 702rpx;
  473. height: 280rpx;
  474. border-radius: 20rpx;
  475. }
  476. }
  477. .item-img {
  478. width: 60rpx;
  479. height: 60rpx;
  480. }
  481. .up-img {
  482. width: 150rpx;
  483. height: 150rpx;
  484. margin: 0 8rpx 8rpx 0;
  485. position: relative;
  486. .img {
  487. width: 100%;
  488. height: 100%;
  489. border-radius: 10rpx;
  490. }
  491. .delete-icons {
  492. position: absolute;
  493. right: 0;
  494. top: 0;
  495. }
  496. }
  497. .textarea-box {
  498. height: 150rpx;
  499. position: relative;
  500. }
  501. .tj-num {
  502. position: absolute;
  503. right: 10rpx;
  504. bottom: 10rpx;
  505. }
  506. .textarea {
  507. width: 100%;
  508. height: 100%;
  509. padding: 20rpx;
  510. box-sizing: border-box;
  511. }
  512. .add-img-box {
  513. width: 150rpx;
  514. height: 150rpx;
  515. border: 1px dashed #666;
  516. .add-img {
  517. width: 36rpx;
  518. height: 36rpx;
  519. }
  520. }
  521. .but-box {
  522. height: 80rpx;
  523. }
  524. .but-left {
  525. height: 100%;
  526. line-height: 80rpx;
  527. border-radius: 200rpx 0 0 200rpx;
  528. }
  529. .but {
  530. width: 180rpx;
  531. height: 100%;
  532. line-height: 80rpx;
  533. border-radius: 0 200rpx 200rpx 0;
  534. }
  535. </style>