app-delivery.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. <template>
  2. <div class="app-delivery-module">
  3. <!-- 收花人 -->
  4. <div class="module-com">
  5. <tui-list-cell class="line-cell" :hover="false">
  6. <div class="tui-title">收花人姓名</div>
  7. <input v-model="form.receiveUserName" placeholder-class="phcolor" placeholder-style="color:#ccc" @blur="onInputBlur" class="tui-input" name="receiveUserName" placeholder="请填写姓名" />
  8. </tui-list-cell>
  9. <tui-list-cell class="line-cell" :hover="false">
  10. <div class="tui-title">收花人电话</div>
  11. <input v-model="form.receiveMobile" placeholder-class="phcolor" placeholder-style="color:#ccc" @blur="onInputBlur" class="tui-input" name="receiveMobile" placeholder="请填写电话" type="number" />
  12. </tui-list-cell>
  13. <tui-list-cell class="line-cell" :hover="false" :arrow="true" @click="openAddres">
  14. <div class="tui-title">所在城市</div>
  15. <div class="tui-input" v-if="form.province || form.city" >{{ form.province + '-' + form.city }}</div>
  16. <div class="tui-placeholder" v-else>请选择</div>
  17. </tui-list-cell>
  18. <tui-list-cell v-if="hasMap == 0" class="line-cell" :hover="false">
  19. <view class="tui-title">详细地址</view>
  20. <input v-model="form.address" placeholder-class="phcolor" class="tui-input" name="address" @input="modifyShow" placeholder="请填写地址" />
  21. </tui-list-cell>
  22. <tui-list-cell v-else class="line-cell" :hover="false" :arrow="true" @click="selRegionFn">
  23. <div class="tui-title">详细地址</div>
  24. <div class="tui-input" v-if="form.address">{{ form.address }}</div>
  25. <div class="tui-placeholder" v-else>请选择地址</div>
  26. </tui-list-cell>
  27. <tui-list-cell class="line-cell" :hover="false">
  28. <div class="tui-title">门牌号</div>
  29. <input v-model="form.floor" placeholder-class="phcolor" placeholder-style="color:#ccc" class="tui-input" name="floor" placeholder="楼号门牌号(选填)" @input="modifyShow" />
  30. </tui-list-cell>
  31. </div>
  32. <!-- 跑腿费 -->
  33. <div class="module-com" v-if="displaySendCost">
  34. <!-- 跑腿平台报价列表 -->
  35. <view v-if="deliveryQuotes.length > 0" class="delivery-quotes-container">
  36. <view class="delivery-quotes-grid">
  37. <view
  38. v-for="(item, index) in deliveryQuotes"
  39. :key="index"
  40. class="delivery-quote-item"
  41. :class="{
  42. 'active': selectedDeliveryIndex === index,
  43. 'disabled': !item.isAble,
  44. 'fixed-width': deliveryQuotes.length <= 2
  45. }"
  46. @click="selectDelivery(item, index)"
  47. >
  48. <view class="platform-name">{{ item.displayName || item.name }}</view>
  49. <view class="platform-price">{{ item.priceText }}元</view>
  50. <view v-if="item.type" class="platform-type">{{ item.type }}</view>
  51. </view>
  52. </view>
  53. </view>
  54. <view v-else-if="deliveryQuotesLoading" style="text-align: center; padding: 20upx 0; color: #999;">
  55. 正在获取报价...
  56. </view>
  57. <tui-list-cell class="line-cell" :hover="false" v-if="Number(showDistance)>0">
  58. <view class="tui-title">距离</view>
  59. <text v-if="showDistance > 100" style="color: #3385FF">{{ (showDistance/1000).toFixed(1) }}公里</text>
  60. <text v-else style="color: #3385FF">{{ showDistance }} 米</text>
  61. </tui-list-cell>
  62. <tui-list-cell class="line-cell" :hover="false">
  63. <view class="tui-title">跑腿费</view>
  64. <view>
  65. <text :class="isFreeDelivery ? 'delivery-price-free' : 'delivery-price-normal'">¥{{ isFreeDelivery ? 0 : (selectedDeliveryData ? selectedDeliveryData.priceNumber : 0) }}</text>
  66. <text v-if="isFreeDelivery" class="free-delivery-tag">¥0【免跑腿费】</text>
  67. </view>
  68. </tui-list-cell>
  69. </div>
  70. <!-- 订花人 -->
  71. <div class="module-com">
  72. <tui-list-cell class="line-cell" :hover="false" :arrow="true">
  73. <div class="tui-title">配送日期</div>
  74. <picker mode="date" :value="form.reachDate" @change="selTimeFn" class="tui-input">
  75. <div v-if="form.reachDate">{{ form.reachDate }}</div>
  76. <div v-else>今天</div>
  77. </picker>
  78. </tui-list-cell>
  79. <tui-list-cell class="line-cell" :hover="false" :arrow="false">
  80. <div class="tui-title">配送时间</div>
  81. <picker mode="multiSelector" :value="form.reachPeriod" :range="timeOptions" @change="bindTimeChange">
  82. <view class="uni-input" style="width:450upx;">{{form.reachPeriod}}<text style="margin-left:10upx;">前</text></view>
  83. </picker>
  84. </tui-list-cell>
  85. </div>
  86. <div class="module-com">
  87. <tui-list-cell class="line-cell" :hover="false">
  88. <div class="tui-title">贺卡内容</div>
  89. <textarea v-model="form.cardInfo" placeholder-class="phcolor" placeholder-style="color:#ccc" class="tui-input textarea-input" name="cardInfo" placeholder="请填写" :adjust-position="false" :cursor-spacing="0" :auto-height="false" :disable-default-padding="true" :show-confirm-bar="false" />
  90. </tui-list-cell>
  91. <tui-list-cell class="line-cell" :hover="false">
  92. <div class="tui-title">派送方式</div>
  93. <div class="delivery-mode-wrapper">
  94. <switch :checked="form.anonymity == 1" @change="anonymityChange" color="#ff6b35" />
  95. <span v-if="form.anonymity == 0" class="mode-text normal">不匿名</span>
  96. <span v-else class="mode-text anonymous">要匿名</span>
  97. </div>
  98. </tui-list-cell>
  99. <tui-list-cell class="line-cell" :hover="false">
  100. <div class="tui-title">备注内容</div>
  101. <textarea v-model="form.remark" placeholder-class="phcolor" placeholder-style="color:#ccc" class="tui-input textarea-input" name="remark" placeholder="选填,不要贺卡这里备注" :adjust-position="false" :cursor-spacing="0" :auto-height="false" :disable-default-padding="true" :show-confirm-bar="false" />
  102. </tui-list-cell>
  103. </div>
  104. <!-- 省市联动 -->
  105. <simple-address ref="simpleAddress" :region="orderShop.region" :pickerValueDefault="cityPickerValueDefault" @onConfirm="onCityConfirm" >
  106. </simple-address>
  107. <!-- 选择地区 -->
  108. <app-area-sel :isFocus="false" :show.sync="showRegion" :city="form.city" @change="changeAreaFn" />
  109. </div>
  110. </template>
  111. <script>
  112. import { mapGetters } from "vuex";
  113. import TuiListCell from "@/components/plugin/list-cell";
  114. import AppDatePicker from "@/components/app-date-picker";
  115. import SimpleAddress from "@/components/plugin/simple-address";
  116. import AppAreaSel from "@/components/app-area-sel";
  117. import { allDeliveryQuotes } from "@/api/express/delivery";
  118. export default {
  119. name: "app-delivery-module",
  120. components: {
  121. AppAreaSel,
  122. TuiListCell,
  123. AppDatePicker,
  124. SimpleAddress
  125. },
  126. props: {
  127. hasMap: {
  128. type: Number,
  129. default: 0
  130. },
  131. openIntraCity: {
  132. type: Number,
  133. default: 0
  134. },
  135. hcFreeKm: {
  136. type: Number,
  137. default: 0
  138. },
  139. hcMap: {
  140. type: Array,
  141. default: () => []
  142. },
  143. allPriceFun: {
  144. type: Number,
  145. default: 0
  146. },
  147. allCountFun: {
  148. type: Object,
  149. default: () => ({
  150. bigLength: 0,
  151. weight: 0
  152. })
  153. },
  154. },
  155. data() {
  156. return {
  157. region: [],
  158. showRegion: false,
  159. form: {
  160. receiveUserName: "", // 收花人姓名;到店自提的姓名
  161. receiveMobile: "", // 收花人手机;到店自提的电话;
  162. province: "", // 收花人省份
  163. city: "", // 收花人城市
  164. address: "", // 收花人城市
  165. floor: "", // 收花人门楼号和牌号
  166. showAddress: "", // 收花人完整地址,从腾讯地图接口获取
  167. reachDate: "", // 配送时间
  168. reachPeriod: "12:00", // 配送时段,0上午1下午2晚上
  169. cardInfo: "", // 贺卡信息
  170. remark: "", // 备注
  171. anonymity:0
  172. },
  173. region: {
  174. latitude: 0,
  175. longitude: 0
  176. },
  177. showDistance: 0,
  178. deliveryQuotes: [], // 跑腿平台报价列表
  179. selectedDeliveryIndex: -1, // 选中的报价索引
  180. deliveryQuotesLoading: false, // 报价加载状态
  181. selectedDeliveryData: null, // 选中的报价数据
  182. // 省市联动
  183. cityPickerValueDefault: [0, 0],
  184. timeOptions: [
  185. ['00', '01', '02','03','04','05','06','07','08','09','10','11','12','13', '14','15','16','17','18','19','20','21','22','23'],
  186. ['00', '05', '10','15','20','25','30','35','40','45','50','55']
  187. ]
  188. };
  189. },
  190. computed: {
  191. ...mapGetters({ orderShop: "getOrderShop" }),
  192. ...mapGetters({ shopUser: "getShopUser" }),
  193. displaySendCost() {
  194. if (this.openIntraCity == 1) {
  195. return true;
  196. }
  197. return false;
  198. },
  199. isFreeDelivery() {
  200. // 不显示跑腿费时,不需要判断
  201. if (this.displaySendCost == false) {
  202. return false;
  203. }
  204. // 条件1: 比较距离与免费公里数
  205. // showDistance单位是米,hcFreeKm单位是千米
  206. const distanceInMeters = Number(this.showDistance) || 0;
  207. const freeKmInMeters = (Number(this.hcFreeKm) || 0) * 1000;
  208. // 如果距离小于免费公里数,则免运费
  209. if (distanceInMeters < freeKmInMeters) {
  210. return true;
  211. }
  212. // 条件2: 如果距离大于免费公里数,判断hcMap
  213. if (distanceInMeters > freeKmInMeters) {
  214. // 判断hcMap是否为空
  215. if (this.hcMap && Array.isArray(this.hcMap) && this.hcMap.length > 0) {
  216. // 获取花材数量和总价
  217. const flowerCount = this.allCountFun.bigLength || 0;
  218. const flowerPrice = this.allPriceFun || 0;
  219. // 遍历hcMap,检查是否满足免运费条件
  220. for (const rule of this.hcMap) {
  221. const ruleNum = Number(rule.num) || 0;
  222. const rulePrice = Number(rule.price) || 0;
  223. const ruleDistance = (Number(rule.distance) || 0) * 1000; // 转换为米
  224. // 如果花材数量大于num,花材总价大于price,距离小于distance,则免运费
  225. if (flowerCount >= ruleNum && flowerPrice >= rulePrice && distanceInMeters <= ruleDistance) {
  226. return true;
  227. }
  228. }
  229. }
  230. }
  231. return false;
  232. }
  233. },
  234. watch: {
  235. orderShop(val) {
  236. console.log("val", val);
  237. this.form.province = val.shop.province ? val.shop.province : "";
  238. this.form.city = val.shop.city ? val.shop.city : "";
  239. }
  240. },
  241. mounted() {
  242. if (!this.$util.isEmpty(this.orderShop)) {
  243. this.form.province = this.orderShop.shop.province ? this.orderShop.shop.province : ""
  244. this.form.city = this.orderShop.shop.city ? this.orderShop.shop.city : ""
  245. }
  246. },
  247. methods: {
  248. bindTimeChange(event){
  249. let val = event.detail.value
  250. let before = Number(val[0])
  251. let after = Number(val[1])
  252. let timeOptions = this.timeOptions
  253. this.form.reachPeriod = timeOptions[0][before]+':'+timeOptions[1][after]
  254. },
  255. selTimeFn(e) {
  256. this.form.reachDate = e.detail.value;
  257. },
  258. // 选择地址
  259. selRegionFn() {
  260. if (!this.form.city) {
  261. this.$msg("请先选择地址!");
  262. return false;
  263. }
  264. this.showRegion = true;
  265. },
  266. changeAreaFn(e) {
  267. this.form.address = e.name;
  268. this.form.showAddress = e.address;
  269. let locationStr = e.location
  270. let fruits = locationStr.split(",").map(fruit => fruit.trim());
  271. this.region.latitude = fruits[1];
  272. this.region.longitude = fruits[0];
  273. this.triggerGetDeliveryQuotes();
  274. },
  275. onInputBlur(){
  276. this.triggerGetDeliveryQuotes();
  277. },
  278. triggerGetDeliveryQuotes() {
  279. if (this.form.receiveUserName && this.form.receiveMobile && this.form.address) {
  280. this.getDeliveryQuotes();
  281. }
  282. },
  283. getDeliveryQuotes() {
  284. this.deliveryQuotesLoading = true;
  285. this.deliveryQuotes = [];
  286. this.selectedDeliveryIndex = -1;
  287. this.selectedDeliveryData = null;
  288. allDeliveryQuotes({
  289. weight: this.allCountFun.weight,
  290. selectList: [], // 根据需要传递
  291. remark: this.form.remark,
  292. }).then(res => {
  293. this.deliveryQuotesLoading = false;
  294. if (res.code === 1 && res.data && res.data.deliveryList) {
  295. this.deliveryQuotes = res.data.deliveryList
  296. .filter(item => item.isAble !== false)
  297. .map(item => {
  298. const price = item.price ? (item.price / 100).toFixed(1) : '0.0';
  299. let displayName = item.name;
  300. if (displayName && displayName.includes('(')) {
  301. displayName = displayName.split('(')[0].trim();
  302. }
  303. return {
  304. ...item,
  305. displayName: displayName,
  306. priceText: price,
  307. priceNumber: parseFloat(price)
  308. };
  309. });
  310. const firstAvailableIndex = this.deliveryQuotes.findIndex(item => item.isAble);
  311. if (firstAvailableIndex !== -1) {
  312. this.selectDelivery(this.deliveryQuotes[firstAvailableIndex], firstAvailableIndex);
  313. }
  314. } else {
  315. uni.showToast({
  316. title: '暂无可用的配送平台',
  317. icon: 'none'
  318. });
  319. }
  320. }).catch(err => {
  321. this.deliveryQuotesLoading = false;
  322. console.error('获取跑腿报价失败:', err);
  323. uni.showToast({
  324. title: '获取报价失败',
  325. icon: 'none'
  326. });
  327. });
  328. },
  329. selectDelivery(item, index) {
  330. if (!item.isAble) return;
  331. this.selectedDeliveryIndex = index;
  332. this.selectedDeliveryData = item;
  333. this.showDistance = item.distance || 0;
  334. const freight = this.isFreeDelivery ? 0 : item.priceNumber;
  335. this.$emit("countFreight", {
  336. sendCost: freight,
  337. deliveryPlatform: item.en_name,
  338. deliveryPrice: item.priceNumber,
  339. deliveryDistance: item.distance || 0,
  340. });
  341. },
  342. anonymityChange(e) {
  343. this.form.anonymity = e.detail.value ? 1 : 0;
  344. },
  345. // 省市联动
  346. openAddres() {
  347. //城市暂时不可修改 shish 2020.5.11
  348. this.$refs.simpleAddress.open();
  349. },
  350. onCityConfirm(e) {
  351. this.form.address = e.label;
  352. this.form.province = e.provinceName;
  353. this.form.city = e.cityName;
  354. },
  355. modifyShow(){
  356. this.form.showAddress = this.form.province+this.form.city+this.form.address+this.form.floor
  357. },
  358. }
  359. };
  360. </script>
  361. <style lang="scss" scoped>
  362. .app-delivery-module {
  363. .module-com {
  364. margin-bottom: 20upx;
  365. background-color: #fff;
  366. .module-tit {
  367. padding: 20upx 18upx;
  368. font-size: 28upx;
  369. font-weight: 600;
  370. border-bottom: 1upx solid $borderColor;
  371. }
  372. .module-det {
  373. padding: 0 30upx;
  374. }
  375. }
  376. // 公共
  377. .line-cell {
  378. .tui-title {
  379. width: 210upx;
  380. color: $fontColor2;
  381. }
  382. .tui-input {
  383. width: calc(100% - 210upx);
  384. font-size: 28upx;
  385. }
  386. .tui-placeholder {
  387. color: #ccc;
  388. }
  389. .phcolor {
  390. color: green;
  391. }
  392. }
  393. // 今天时间选择
  394. .delivery-time {
  395. @include disFlex(center, center);
  396. .button-com {
  397. padding-top: 14upx;
  398. padding-bottom: 10upx;
  399. margin: 0 10upx;
  400. }
  401. }
  402. // 贺卡
  403. .gift-wrap {
  404. .gift-content {
  405. width: 100%;
  406. }
  407. .need-gift {
  408. margin-left: 14upx;
  409. color: $fontColor2;
  410. }
  411. .gitf-textarea {
  412. margin-top: 40upx;
  413. }
  414. }
  415. // 派送方式选择器样式
  416. .delivery-mode-wrapper {
  417. display: flex;
  418. align-items: center;
  419. justify-content: flex-end;
  420. .mode-text {
  421. font-size: 40upx;
  422. margin-left: 10upx;
  423. transition: color 0.3s ease;
  424. &.normal {
  425. // 不匿名 - 使用页面常规颜色
  426. color: #666666;
  427. }
  428. &.anonymous {
  429. // 匿名 - 使用显眼颜色
  430. color: #ff6b35;
  431. font-weight: bold;
  432. }
  433. }
  434. switch {
  435. transform: scale(0.8);
  436. }
  437. }
  438. // textarea样式 - 彻底解决input文字浮动问题
  439. .textarea-input {
  440. /* 重置textarea默认样式,使其表现像input */
  441. resize: none !important;
  442. overflow: hidden !important;
  443. border: none !important;
  444. outline: none !important;
  445. background: transparent !important;
  446. padding: 0 !important;
  447. margin: 0 !important;
  448. /* 固定高度和行高,防止浮动 */
  449. height: 44upx !important;
  450. max-height: 44upx !important;
  451. min-height: 44upx !important;
  452. line-height: 44upx !important;
  453. /* 字体样式保持一致 */
  454. font-size: 28upx !important;
  455. font-family: inherit !important;
  456. color: inherit !important;
  457. /* 文字单行显示,模拟input行为 */
  458. white-space: nowrap !important;
  459. text-overflow: ellipsis !important;
  460. word-wrap: normal !important;
  461. word-break: keep-all !important;
  462. /* 垂直对齐 */
  463. vertical-align: middle !important;
  464. text-align: left !important;
  465. /* 微信小程序特殊处理 */
  466. /* #ifdef MP-WEIXIN */
  467. /* 禁用textarea的多行特性 */
  468. overflow-y: hidden !important;
  469. scrollbar-width: none !important;
  470. /* 固定位置,防止键盘影响 */
  471. position: relative !important;
  472. z-index: 1 !important;
  473. /* 禁用文字大小自动调整 */
  474. -webkit-text-size-adjust: none !important;
  475. text-size-adjust: none !important;
  476. /* 强制使用系统字体 */
  477. font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, sans-serif !important;
  478. /* #endif */
  479. /* H5环境优化 */
  480. /* #ifdef H5 */
  481. box-sizing: border-box !important;
  482. -webkit-appearance: none !important;
  483. appearance: none !important;
  484. /* #endif */
  485. /* APP环境优化 */
  486. /* #ifdef APP-PLUS */
  487. box-sizing: border-box !important;
  488. /* #endif */
  489. }
  490. }
  491. /* 跑腿平台报价样式 */
  492. .delivery-quotes-container {
  493. width: 100%;
  494. margin-top: 20upx;
  495. padding: 0 20upx;
  496. }
  497. .delivery-quotes-grid {
  498. display: flex;
  499. flex-wrap: wrap;
  500. gap: 10upx;
  501. padding: 0;
  502. width: 100%;
  503. }
  504. .delivery-quote-item {
  505. display: flex;
  506. flex-direction: column;
  507. align-items: center;
  508. justify-content: center;
  509. flex: 0 0 calc(33% - 7upx);
  510. width: calc(33% - 7upx);
  511. min-height: 140upx;
  512. padding: 16upx 10upx;
  513. margin-bottom: 5upx;
  514. background: #FFFFFF;
  515. border: 2upx solid #DDDDDD;
  516. border-radius: 8upx;
  517. box-sizing: border-box;
  518. transition: all 0.3s;
  519. // 当只有1-2项时使用固定宽度
  520. &.fixed-width {
  521. flex: 0 0 220rpx;
  522. width: 220rpx;
  523. }
  524. .platform-name {
  525. font-size: 26upx;
  526. color: #333;
  527. font-weight: bold;
  528. margin-bottom: 8upx;
  529. text-align: center;
  530. line-height: 1.3;
  531. max-width: 100%;
  532. overflow: hidden;
  533. text-overflow: ellipsis;
  534. display: -webkit-box;
  535. -webkit-line-clamp: 2;
  536. line-clamp: 2;
  537. -webkit-box-orient: vertical;
  538. word-break: break-all;
  539. }
  540. .platform-price {
  541. font-size: 30upx;
  542. color: #333;
  543. font-weight: bold;
  544. margin-bottom: 4upx;
  545. }
  546. .platform-type {
  547. font-size: 20upx;
  548. color: #868686;
  549. margin-top: 4upx;
  550. text-align: center;
  551. line-height: 1.3;
  552. max-width: 100%;
  553. overflow: hidden;
  554. text-overflow: ellipsis;
  555. display: -webkit-box;
  556. -webkit-line-clamp: 2;
  557. line-clamp: 2;
  558. -webkit-box-orient: vertical;
  559. word-break: break-all;
  560. }
  561. // 选中状态
  562. &.active {
  563. background: #09C567;
  564. border-color: #09C567;
  565. .platform-name,
  566. .platform-price {
  567. color: #FFFFFF;
  568. }
  569. .platform-type {
  570. color: rgba(255, 255, 255, 0.85);
  571. }
  572. }
  573. // 不可用状态
  574. &.disabled {
  575. opacity: 0.5;
  576. background: #F5F5F5;
  577. .platform-name,
  578. .platform-price,
  579. .platform-type {
  580. color: #CCCCCC;
  581. }
  582. }
  583. }
  584. /* 跑腿费价格样式 */
  585. .delivery-price-normal {
  586. color: #3385FF;
  587. }
  588. .delivery-price-free {
  589. color: #999;
  590. text-decoration: line-through;
  591. }
  592. .free-delivery-tag {
  593. color: #3385FF;
  594. margin-left: 10upx;
  595. }
  596. </style>