wip: tiered deliver price
This commit is contained in:
1 parent
8423007350
commit
a0c13b91da
22 files changed
+414
-27
No files matched your search
@@ -0,0 +1,14 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 门店配送规则(阶梯配送费)升级脚本
|
||||||
|
-- 将固定的"配送价格"扩展为基于距离区间的"配送规则"
|
||||||
|
-- 配送规则存为 JSON 数组: [{minKm,maxKm,price}],区间左闭右开
|
||||||
|
-- 无规则匹配时回退 delivery_price(默认配送费)
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
ALTER TABLE `yshop_store_shop`
|
||||||
|
ADD COLUMN `delivery_rules` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '配送规则JSON:[{minKm,maxKm,price}]' AFTER `delivery_price`;
|
||||||
|
|
||||||
|
-- 可选:为已有支持外卖的门店初始化一条示例规则(请按需调整)
|
||||||
|
-- UPDATE `yshop_store_shop`
|
||||||
|
-- SET `delivery_rules` = '[{"minKm":0,"maxKm":3,"price":8},{"minKm":3,"maxKm":5,"price":12}]'
|
||||||
|
-- WHERE `distance` > 0 AND `delivery_rules` IS NULL;
|
||||||
@@ -13698,6 +13698,7 @@ CREATE TABLE `yshop_store_shop` (
|
|||||||
`distance` int NOT NULL DEFAULT '0' COMMENT '外卖配送距离,单位为千米。0表示不送外卖',
|
`distance` int NOT NULL DEFAULT '0' COMMENT '外卖配送距离,单位为千米。0表示不送外卖',
|
||||||
`min_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '起送价钱',
|
`min_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '起送价钱',
|
||||||
`delivery_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '配送价格',
|
`delivery_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '配送价格',
|
||||||
|
`delivery_rules` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '配送规则JSON:[{minKm,maxKm,price}]',
|
||||||
`notice` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '公告',
|
`notice` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '公告',
|
||||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否营业:0=否,1=是',
|
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否营业:0=否,1=是',
|
||||||
`admin_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '管理员id',
|
`admin_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '管理员id',
|
||||||
@@ -13714,8 +13715,8 @@ CREATE TABLE `yshop_store_shop` (
|
|||||||
-- Records of yshop_store_shop
|
-- Records of yshop_store_shop
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO `yshop_store_shop` VALUES (2, '意向餐饮店', '18000000000', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', '源汇区人民东路', '河南省漯河市源汇区团结路50号-20', '2023-08-19 09:00:00', '2023-08-19 23:00:00', '114.04877877439665', '33.57093831826829', 10, 10.00, 20.00, '新店开张', 1, '126', '1', '2023-08-06 11:48:43', '2023-09-14 17:30:59', '1', '1', b'0');
|
INSERT INTO `yshop_store_shop` VALUES (2, '意向餐饮店', '18000000000', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', '源汇区人民东路', '河南省漯河市源汇区团结路50号-20', '2023-08-19 09:00:00', '2023-08-19 23:00:00', '114.04877877439665', '33.57093831826829', 10, 10.00, 20.00, '[{"minKm":0,"maxKm":3,"price":8},{"minKm":3,"maxKm":5,"price":12},{"minKm":5,"maxKm":10,"price":18}]', '新店开张', 1, '126', '1', '2023-08-06 11:48:43', '2023-09-14 17:30:59', '1', '1', b'0');
|
||||||
INSERT INTO `yshop_store_shop` VALUES (3, 'yshop', '18000000000', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', '67号', '北京市东城区', '2023-08-06 13:35:02', '2023-08-06 15:35:02', '116.40403593222032', '39.91643892263258', 0, 1.00, 2.00, '2222', 1, '100', '2', '2023-08-06 13:14:53', '2024-03-26 15:42:20', '1', '1', b'0');
|
INSERT INTO `yshop_store_shop` VALUES (3, 'yshop', '18000000000', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', 'https://apidc.yixiang.co/admin-api/infra/file/4/get/44723283500f656cdd5906e13f2997cb292d17f516065ff4b3b38f9810bce4cb.jpg', '67号', '北京市东城区', '2023-08-06 13:35:02', '2023-08-06 15:35:02', '116.40403593222032', '39.91643892263258', 0, 1.00, 2.00, NULL, '2222', 1, '100', '2', '2023-08-06 13:14:53', '2024-03-26 15:42:20', '1', '1', b'0');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
|
|||||||
+1
@@ -36,6 +36,7 @@ public interface ErrorCodeConstants {
|
|||||||
|
|
||||||
|
|
||||||
ErrorCode STORE_ORDER_DESK_NOT = new ErrorCode(1008007023, "当前桌号不存在");
|
ErrorCode STORE_ORDER_DESK_NOT = new ErrorCode(1008007023, "当前桌号不存在");
|
||||||
|
ErrorCode STORE_ORDER_OUT_OF_DELIVERY_RANGE = new ErrorCode(1008007024, "当前收货地址不在配送范围内");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+22
-10
@@ -49,6 +49,7 @@ import co.yixiang.yshop.module.product.service.storeproductattrvalue.StoreProduc
|
|||||||
import co.yixiang.yshop.module.store.convert.storeshop.StoreShopConvert;
|
import co.yixiang.yshop.module.store.convert.storeshop.StoreShopConvert;
|
||||||
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
||||||
import co.yixiang.yshop.module.store.service.storeshop.AppStoreShopService;
|
import co.yixiang.yshop.module.store.service.storeshop.AppStoreShopService;
|
||||||
|
import co.yixiang.yshop.module.store.service.storeshop.DeliveryCalculator;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
@@ -180,8 +181,26 @@ public class AppStoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper,Store
|
|||||||
|
|
||||||
BigDecimal sumPrice = BigDecimal.ZERO;
|
BigDecimal sumPrice = BigDecimal.ZERO;
|
||||||
BigDecimal couponPrice = BigDecimal.ZERO;
|
BigDecimal couponPrice = BigDecimal.ZERO;
|
||||||
BigDecimal postagePrice = storeShopDO.getDeliveryPrice();
|
|
||||||
BigDecimal deductionPrice = BigDecimal.ZERO;
|
BigDecimal deductionPrice = BigDecimal.ZERO;
|
||||||
|
// 计算配送费:外卖按收货地址距离匹配配送规则,自取不收配送费
|
||||||
|
BigDecimal postagePrice = BigDecimal.ZERO;
|
||||||
|
UserAddressDO userAddress = null;
|
||||||
|
if (OrderLogEnum.ORDER_TAKE_OUT.getValue().equals(param.getOrderType())) {
|
||||||
|
if (StrUtil.isEmpty(param.getAddressId())) {
|
||||||
|
throw exception(SELECT_ADDRESS);
|
||||||
|
}
|
||||||
|
userAddress = appUserAddressService.getById(param.getAddressId());
|
||||||
|
if (ObjectUtil.isNull(userAddress)) {
|
||||||
|
throw exception(USER_ADDRESS_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
double distanceKm = DeliveryCalculator.calcDistanceKm(
|
||||||
|
storeShopDO.getLng(), storeShopDO.getLat(),
|
||||||
|
userAddress.getLongitude(), userAddress.getLatitude());
|
||||||
|
postagePrice = DeliveryCalculator.calcDeliveryPrice(storeShopDO, distanceKm);
|
||||||
|
if (postagePrice == null) {
|
||||||
|
throw exception(STORE_ORDER_OUT_OF_DELIVERY_RANGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//对库存检查加锁
|
//对库存检查加锁
|
||||||
RLock lock = redissonClient.getLock(LOCK_KEY);
|
RLock lock = redissonClient.getLock(LOCK_KEY);
|
||||||
@@ -264,13 +283,6 @@ public class AppStoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper,Store
|
|||||||
storeOrder.setOrderId(orderSn);
|
storeOrder.setOrderId(orderSn);
|
||||||
//处理如果是外卖 地址
|
//处理如果是外卖 地址
|
||||||
if(OrderLogEnum.ORDER_TAKE_OUT.getValue().equals(param.getOrderType())){
|
if(OrderLogEnum.ORDER_TAKE_OUT.getValue().equals(param.getOrderType())){
|
||||||
if (StrUtil.isEmpty(param.getAddressId())) {
|
|
||||||
throw exception(SELECT_ADDRESS);
|
|
||||||
}
|
|
||||||
UserAddressDO userAddress = appUserAddressService.getById(param.getAddressId());
|
|
||||||
if (ObjectUtil.isNull(userAddress)) {
|
|
||||||
throw exception(USER_ADDRESS_NOT_EXISTS);
|
|
||||||
}
|
|
||||||
storeOrder.setRealName(userAddress.getRealName());
|
storeOrder.setRealName(userAddress.getRealName());
|
||||||
storeOrder.setUserPhone(userAddress.getPhone());
|
storeOrder.setUserPhone(userAddress.getPhone());
|
||||||
storeOrder.setUserAddress(userAddress.getAddress() + " " + userAddress.getDetail());
|
storeOrder.setUserAddress(userAddress.getAddress() + " " + userAddress.getDetail());
|
||||||
@@ -278,12 +290,12 @@ public class AppStoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper,Store
|
|||||||
storeOrder.setCartId(StrUtil.join(",", cartIds));
|
storeOrder.setCartId(StrUtil.join(",", cartIds));
|
||||||
storeOrder.setTotalNum(totalNum);
|
storeOrder.setTotalNum(totalNum);
|
||||||
storeOrder.setTotalPrice(sumPrice);
|
storeOrder.setTotalPrice(sumPrice);
|
||||||
storeOrder.setTotalPostage(storeShopDO.getDeliveryPrice());
|
storeOrder.setTotalPostage(postagePrice);
|
||||||
|
|
||||||
storeOrder.setCouponId(StrUtil.isBlank(param.getCouponId()) ? 0 : Integer.valueOf(param.getCouponId()));
|
storeOrder.setCouponId(StrUtil.isBlank(param.getCouponId()) ? 0 : Integer.valueOf(param.getCouponId()));
|
||||||
storeOrder.setCouponPrice(couponPrice);
|
storeOrder.setCouponPrice(couponPrice);
|
||||||
storeOrder.setPayPrice(payPrice);
|
storeOrder.setPayPrice(payPrice);
|
||||||
storeOrder.setPayPostage(storeShopDO.getDeliveryPrice());
|
storeOrder.setPayPostage(postagePrice);
|
||||||
storeOrder.setDeductionPrice(deductionPrice);
|
storeOrder.setDeductionPrice(deductionPrice);
|
||||||
storeOrder.setPaid(OrderInfoEnum.PAY_STATUS_0.getValue());
|
storeOrder.setPaid(OrderInfoEnum.PAY_STATUS_0.getValue());
|
||||||
storeOrder.setPayType(param.getPayType());
|
storeOrder.setPayType(param.getPayType());
|
||||||
|
|||||||
+5
-1
@@ -1,5 +1,6 @@
|
|||||||
package co.yixiang.yshop.module.store.controller.admin.storeshop.vo;
|
package co.yixiang.yshop.module.store.controller.admin.storeshop.vo;
|
||||||
|
|
||||||
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.DeliveryRule;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -60,9 +61,12 @@ public class StoreShopBaseVO {
|
|||||||
@Schema(description = "起送价钱", required = true, example = "15157")
|
@Schema(description = "起送价钱", required = true, example = "15157")
|
||||||
private BigDecimal minPrice;
|
private BigDecimal minPrice;
|
||||||
|
|
||||||
@Schema(description = "配送价格", required = true, example = "11771")
|
@Schema(description = "默认配送费(未配置规则或无匹配时兜底)", example = "8")
|
||||||
private BigDecimal deliveryPrice;
|
private BigDecimal deliveryPrice;
|
||||||
|
|
||||||
|
@Schema(description = "配送规则(按距离区间阶梯计费)", example = "[{\"minKm\":1,\"maxKm\":3,\"price\":8}]")
|
||||||
|
private List<DeliveryRule> deliveryRules;
|
||||||
|
|
||||||
@Schema(description = "公告", required = true)
|
@Schema(description = "公告", required = true)
|
||||||
@NotNull(message = "公告不能为空")
|
@NotNull(message = "公告不能为空")
|
||||||
private String notice;
|
private String notice;
|
||||||
|
|||||||
+9
@@ -10,6 +10,7 @@ package co.yixiang.yshop.module.store.controller.app.storeshop;
|
|||||||
|
|
||||||
import co.yixiang.yshop.framework.common.pojo.CommonResult;
|
import co.yixiang.yshop.framework.common.pojo.CommonResult;
|
||||||
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.AppStoreShopVO;
|
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.AppStoreShopVO;
|
||||||
|
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.DeliveryFeeRespVO;
|
||||||
import co.yixiang.yshop.module.store.convert.storeshop.StoreShopConvert;
|
import co.yixiang.yshop.module.store.convert.storeshop.StoreShopConvert;
|
||||||
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
||||||
import co.yixiang.yshop.module.store.service.storeshop.AppStoreShopService;
|
import co.yixiang.yshop.module.store.service.storeshop.AppStoreShopService;
|
||||||
@@ -75,6 +76,14 @@ public class AppStoreController {
|
|||||||
return success(StoreShopConvert.INSTANCE.convert02(storeShopDO));
|
return success(StoreShopConvert.INSTANCE.convert02(storeShopDO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/delivery-fee")
|
||||||
|
@Operation(summary = "预估配送费")
|
||||||
|
public CommonResult<DeliveryFeeRespVO> getDeliveryFee(@RequestParam("shopId") Long shopId,
|
||||||
|
@RequestParam("lng") double lng,
|
||||||
|
@RequestParam("lat") double lat) {
|
||||||
|
return success(appStoreShopService.getDeliveryFee(shopId, lng, lat));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -54,6 +54,9 @@ public class AppStoreShopVO {
|
|||||||
@Schema(description = "配送价格", required = true, example = "11771")
|
@Schema(description = "配送价格", required = true, example = "11771")
|
||||||
private BigDecimal deliveryPrice;
|
private BigDecimal deliveryPrice;
|
||||||
|
|
||||||
|
@Schema(description = "配送规则JSON文本", example = "[{\"minKm\":1,\"maxKm\":3,\"price\":8}]")
|
||||||
|
private String deliveryRules;
|
||||||
|
|
||||||
@Schema(description = "公告", required = true)
|
@Schema(description = "公告", required = true)
|
||||||
private String notice;
|
private String notice;
|
||||||
|
|
||||||
|
|||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package co.yixiang.yshop.module.store.controller.app.storeshop.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预估配送费 Response VO
|
||||||
|
*
|
||||||
|
* @author yshop
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DeliveryFeeRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "收货地址到门店的距离(千米)")
|
||||||
|
private Double distanceKm;
|
||||||
|
|
||||||
|
@Schema(description = "配送费(元),超出配送范围时为 0")
|
||||||
|
private BigDecimal deliveryPrice;
|
||||||
|
|
||||||
|
@Schema(description = "是否超出配送范围")
|
||||||
|
private Boolean outOfRange;
|
||||||
|
|
||||||
|
}
|
||||||
+13
@@ -3,11 +3,15 @@ package co.yixiang.yshop.module.store.convert.storeshop;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import co.yixiang.yshop.framework.common.pojo.PageResult;
|
import co.yixiang.yshop.framework.common.pojo.PageResult;
|
||||||
|
import co.yixiang.yshop.framework.common.util.json.JsonUtils;
|
||||||
|
|
||||||
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.AppStoreShopVO;
|
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.AppStoreShopVO;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Named;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
import co.yixiang.yshop.module.store.controller.admin.storeshop.vo.*;
|
import co.yixiang.yshop.module.store.controller.admin.storeshop.vo.*;
|
||||||
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.DeliveryRule;
|
||||||
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +30,7 @@ public interface StoreShopConvert {
|
|||||||
|
|
||||||
StoreShopRespVO convert(StoreShopDO bean);
|
StoreShopRespVO convert(StoreShopDO bean);
|
||||||
|
|
||||||
|
@Mapping(source = "deliveryRules", target = "deliveryRules", qualifiedByName = "rulesToJson")
|
||||||
AppStoreShopVO convert02(StoreShopDO bean);
|
AppStoreShopVO convert02(StoreShopDO bean);
|
||||||
|
|
||||||
List<StoreShopRespVO> convertList(List<StoreShopDO> list);
|
List<StoreShopRespVO> convertList(List<StoreShopDO> list);
|
||||||
@@ -34,4 +39,12 @@ public interface StoreShopConvert {
|
|||||||
|
|
||||||
List<StoreShopExcelVO> convertList02(List<StoreShopDO> list);
|
List<StoreShopExcelVO> convertList02(List<StoreShopDO> list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将配送规则 List 序列化为 JSON 文本,供小程序端解析展示
|
||||||
|
*/
|
||||||
|
@Named("rulesToJson")
|
||||||
|
default String rulesToJson(List<DeliveryRule> rules) {
|
||||||
|
return rules == null ? null : JsonUtils.toJsonString(rules);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package co.yixiang.yshop.module.store.dal.dataobject.storeshop;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配送规则 DO
|
||||||
|
* <p>
|
||||||
|
* 描述一段距离区间对应的配送费,区间为左闭右开 [minKm, maxKm)。
|
||||||
|
* 例如:minKm=1, maxKm=3, price=8 表示 1km(含) ~ 3km(不含) 配送费 8 元。
|
||||||
|
*
|
||||||
|
* @author yshop
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class DeliveryRule {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最小距离(单位:千米,含)
|
||||||
|
*/
|
||||||
|
private BigDecimal minKm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大距离(单位:千米,不含)
|
||||||
|
*/
|
||||||
|
private BigDecimal maxKm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配送费(单位:元)
|
||||||
|
*/
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
}
|
||||||
+8
-1
@@ -1,6 +1,7 @@
|
|||||||
package co.yixiang.yshop.module.store.dal.dataobject.storeshop;
|
package co.yixiang.yshop.module.store.dal.dataobject.storeshop;
|
||||||
|
|
||||||
import co.yixiang.yshop.framework.mybatis.core.type.StringListTypeHandler;
|
import co.yixiang.yshop.framework.mybatis.core.type.StringListTypeHandler;
|
||||||
|
import co.yixiang.yshop.module.store.dal.type.DeliveryRuleListTypeHandler;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -74,9 +75,15 @@ public class StoreShopDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private BigDecimal minPrice;
|
private BigDecimal minPrice;
|
||||||
/**
|
/**
|
||||||
* 配送价格
|
* 配送价格(默认/兜底配送费)
|
||||||
*/
|
*/
|
||||||
private BigDecimal deliveryPrice;
|
private BigDecimal deliveryPrice;
|
||||||
|
/**
|
||||||
|
* 配送规则(按距离区间阶梯计费),JSON 数组:[{minKm,maxKm,price}]
|
||||||
|
* 区间左闭右开,无匹配时回退 deliveryPrice
|
||||||
|
*/
|
||||||
|
@TableField(typeHandler = DeliveryRuleListTypeHandler.class)
|
||||||
|
private List<DeliveryRule> deliveryRules;
|
||||||
/**
|
/**
|
||||||
* 公告
|
* 公告
|
||||||
*/
|
*/
|
||||||
|
|||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
package co.yixiang.yshop.module.store.dal.type;
|
||||||
|
|
||||||
|
import co.yixiang.yshop.framework.common.util.json.JsonUtils;
|
||||||
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.DeliveryRule;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List<DeliveryRule> 的类型转换器实现类,对应数据库的 text 类型
|
||||||
|
* <p>
|
||||||
|
* 参考 {@link com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler} 实现,
|
||||||
|
* 使用 TypeReference 保证反序列化为 List<DeliveryRule> 时泛型不丢失。
|
||||||
|
* 放置于 store 模块以避免框架层反向依赖业务模块。
|
||||||
|
*
|
||||||
|
* @author yshop
|
||||||
|
*/
|
||||||
|
public class DeliveryRuleListTypeHandler extends AbstractJsonTypeHandler<Object> {
|
||||||
|
|
||||||
|
private static final TypeReference<List<DeliveryRule>> TYPE_REFERENCE = new TypeReference<List<DeliveryRule>>(){};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object parse(String json) {
|
||||||
|
return JsonUtils.parseObject(json, TYPE_REFERENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String toJson(Object obj) {
|
||||||
|
return JsonUtils.toJsonString(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
@@ -1,6 +1,7 @@
|
|||||||
package co.yixiang.yshop.module.store.service.storeshop;
|
package co.yixiang.yshop.module.store.service.storeshop;
|
||||||
|
|
||||||
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.AppStoreShopVO;
|
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.AppStoreShopVO;
|
||||||
|
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.DeliveryFeeRespVO;
|
||||||
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
@@ -22,4 +23,13 @@ public interface AppStoreShopService extends IService<StoreShopDO> {
|
|||||||
*/
|
*/
|
||||||
List<AppStoreShopVO> getStoreList(double lon,double lat,String name,Integer shopId);
|
List<AppStoreShopVO> getStoreList(double lon,double lat,String name,Integer shopId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预估配送费
|
||||||
|
* @param shopId 门店id
|
||||||
|
* @param lng 收货地址经度
|
||||||
|
* @param lat 收货地址纬度
|
||||||
|
* @return 配送费及是否超出配送范围
|
||||||
|
*/
|
||||||
|
DeliveryFeeRespVO getDeliveryFee(Long shopId, double lng, double lat);
|
||||||
|
|
||||||
}
|
}
|
||||||
+22
@@ -1,6 +1,7 @@
|
|||||||
package co.yixiang.yshop.module.store.service.storeshop;
|
package co.yixiang.yshop.module.store.service.storeshop;
|
||||||
|
|
||||||
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.AppStoreShopVO;
|
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.AppStoreShopVO;
|
||||||
|
import co.yixiang.yshop.module.store.controller.app.storeshop.vo.DeliveryFeeRespVO;
|
||||||
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
||||||
import co.yixiang.yshop.module.store.dal.mysql.storeshop.StoreShopMapper;
|
import co.yixiang.yshop.module.store.dal.mysql.storeshop.StoreShopMapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
@@ -8,6 +9,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,4 +36,24 @@ public class AppStoreShopServiceImpl extends ServiceImpl<StoreShopMapper, StoreS
|
|||||||
public List<AppStoreShopVO> getStoreList(double lon, double lat,String name,Integer shopId) {
|
public List<AppStoreShopVO> getStoreList(double lon, double lat,String name,Integer shopId) {
|
||||||
return shopMapper.getStoreList(lon,lat,name,shopId);
|
return shopMapper.getStoreList(lon,lat,name,shopId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DeliveryFeeRespVO getDeliveryFee(Long shopId, double lng, double lat) {
|
||||||
|
StoreShopDO shop = getById(shopId);
|
||||||
|
DeliveryFeeRespVO vo = new DeliveryFeeRespVO();
|
||||||
|
vo.setOutOfRange(true);
|
||||||
|
vo.setDeliveryPrice(BigDecimal.ZERO);
|
||||||
|
if (shop == null) {
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
double distanceKm = DeliveryCalculator.calcDistanceKm(
|
||||||
|
shop.getLng(), shop.getLat(), String.valueOf(lng), String.valueOf(lat));
|
||||||
|
BigDecimal price = DeliveryCalculator.calcDeliveryPrice(shop, distanceKm);
|
||||||
|
vo.setDistanceKm(distanceKm);
|
||||||
|
if (price != null) {
|
||||||
|
vo.setOutOfRange(false);
|
||||||
|
vo.setDeliveryPrice(price);
|
||||||
|
}
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
package co.yixiang.yshop.module.store.service.storeshop;
|
||||||
|
|
||||||
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.DeliveryRule;
|
||||||
|
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配送费计算工具
|
||||||
|
* <p>
|
||||||
|
* 1. {@link #calcDistanceKm} 使用 Haversine 公式计算两个经纬度之间的球面距离(千米),
|
||||||
|
* 与 StoreShopMapper.getStoreList 中的 SQL 计算口径一致(地球半径 6378.138km)。
|
||||||
|
* 2. {@link #calcDeliveryPrice} 根据距离匹配门店的配送规则(左闭右开区间),
|
||||||
|
* 无匹配时回退 deliveryPrice(兜底);超出配送范围返回 null。
|
||||||
|
*
|
||||||
|
* @author yshop
|
||||||
|
*/
|
||||||
|
public class DeliveryCalculator {
|
||||||
|
|
||||||
|
/** 地球半径(千米),与 Mapper SQL 保持一致 */
|
||||||
|
private static final double EARTH_RADIUS_KM = 6378.138;
|
||||||
|
|
||||||
|
private DeliveryCalculator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算两个经纬度之间的球面距离(千米)
|
||||||
|
*
|
||||||
|
* @param lng1 起点-经度
|
||||||
|
* @param lat1 起点-纬度
|
||||||
|
* @param lng2 终点-经度
|
||||||
|
* @param lat2 终点-纬度
|
||||||
|
* @return 距离(千米)
|
||||||
|
*/
|
||||||
|
public static double calcDistanceKm(String lng1, String lat1, String lng2, String lat2) {
|
||||||
|
double lon1 = Double.parseDouble(lng1);
|
||||||
|
double latt1 = Double.parseDouble(lat1);
|
||||||
|
double lon2 = Double.parseDouble(lng2);
|
||||||
|
double latt2 = Double.parseDouble(lat2);
|
||||||
|
double radLat1 = Math.toRadians(latt1);
|
||||||
|
double radLat2 = Math.toRadians(latt2);
|
||||||
|
double a = radLat1 - radLat2;
|
||||||
|
double b = Math.toRadians(lon1) - Math.toRadians(lon2);
|
||||||
|
double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
|
||||||
|
+ Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
|
||||||
|
distance = distance * EARTH_RADIUS_KM;
|
||||||
|
return Math.round(distance * 1000.0) / 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据距离匹配配送规则计算配送费
|
||||||
|
*
|
||||||
|
* @param shop 门店
|
||||||
|
* @param distanceKm 收货地址到门店的距离(千米)
|
||||||
|
* @return 配送费;返回 null 表示不在配送范围内(不可配送)
|
||||||
|
*/
|
||||||
|
public static BigDecimal calcDeliveryPrice(StoreShopDO shop, double distanceKm) {
|
||||||
|
// distance<=0 表示不送外卖
|
||||||
|
Integer maxDistance = shop.getDistance();
|
||||||
|
if (maxDistance == null || maxDistance <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 超出配送范围
|
||||||
|
if (distanceKm > maxDistance) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 匹配阶梯规则(左闭右开)
|
||||||
|
List<DeliveryRule> rules = shop.getDeliveryRules();
|
||||||
|
if (rules != null && !rules.isEmpty()) {
|
||||||
|
for (DeliveryRule rule : rules) {
|
||||||
|
if (rule.getMinKm() == null || rule.getMaxKm() == null || rule.getPrice() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double min = rule.getMinKm().doubleValue();
|
||||||
|
double max = rule.getMaxKm().doubleValue();
|
||||||
|
if (distanceKm >= min && distanceKm < max) {
|
||||||
|
return rule.getPrice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 无规则匹配,回退默认配送费
|
||||||
|
return shop.getDeliveryPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,3 +13,10 @@ export function menuGoods(data) {
|
|||||||
return api.get('/product/products', data, { login: false })
|
return api.get('/product/products', data, { login: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预估配送费(根据收货地址距离匹配配送规则)
|
||||||
|
*/
|
||||||
|
export function getDeliveryFee(data) {
|
||||||
|
return api.get('/store/delivery-fee', data, { login: false })
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const DEV_API_URL = 'http://localhost:48081/app-api'
|
const DEV_API_URL = 'http://maitre.imomoe.cn/api/app-api'
|
||||||
const PROD_API_URL = 'https://apidc.yixiang.co/app-api'
|
const PROD_API_URL = 'https://maitre.imomoe.cn/api/app-api'
|
||||||
|
|
||||||
// #ifdef APP-PLUS || H5 || MP-WEIXIN
|
// #ifdef APP-PLUS || H5 || MP-WEIXIN
|
||||||
export const VUE_APP_API_URL =
|
export const VUE_APP_API_URL =
|
||||||
@@ -8,7 +8,7 @@ export const VUE_APP_API_URL =
|
|||||||
|
|
||||||
export const VUE_APP_RESOURCES_URL = 'https://h5.yixiang.co/static'
|
export const VUE_APP_RESOURCES_URL = 'https://h5.yixiang.co/static'
|
||||||
export const VUE_APP_UPLOAD_URL = VUE_APP_API_URL + '/infra/file/upload'
|
export const VUE_APP_UPLOAD_URL = VUE_APP_API_URL + '/infra/file/upload'
|
||||||
export const APP_ID = 'wxdbdbc123c8c30b45'
|
export const APP_ID = 'wx3548436813df3f60'
|
||||||
|
|
||||||
const orderListStatus = {}
|
const orderListStatus = {}
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
<view class="flex-fill d-flex justify-content-end align-items-center">
|
<view class="flex-fill d-flex justify-content-end align-items-center">
|
||||||
<view>
|
<view>
|
||||||
总计¥{{ total }}
|
总计¥{{ total }}
|
||||||
<text v-if="orderType == 'takeout'">,配送费¥{{ store.deliveryPrice }}</text>
|
<text v-if="orderType == 'takeout'">,配送费¥{{ deliveryFee }}</text>
|
||||||
<text v-if="coupon.value">,¥-{{ coupon.value }}</text>
|
<text v-if="coupon.value">,¥-{{ coupon.value }}</text>
|
||||||
,实付
|
,实付
|
||||||
</view>
|
</view>
|
||||||
@@ -219,6 +219,7 @@
|
|||||||
import {
|
import {
|
||||||
ref,
|
ref,
|
||||||
computed,
|
computed,
|
||||||
|
watch,
|
||||||
nextTick
|
nextTick
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { useMainStore } from '@/store/store'
|
import { useMainStore } from '@/store/store'
|
||||||
@@ -235,12 +236,15 @@ import {
|
|||||||
import {
|
import {
|
||||||
couponCount
|
couponCount
|
||||||
} from '@/api/coupon'
|
} from '@/api/coupon'
|
||||||
|
import { getDeliveryFee } from '@/api/goods'
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
import * as jweixin from 'weixin-js-sdk'
|
import * as jweixin from 'weixin-js-sdk'
|
||||||
// #endif
|
// #endif
|
||||||
const main = useMainStore()
|
const main = useMainStore()
|
||||||
const { orderType,address, store,location,isLogin,member,mycoupon } = storeToRefs(main)
|
const { orderType,address, store,location,isLogin,member,mycoupon } = storeToRefs(main)
|
||||||
const active = ref(false)
|
const active = ref(false)
|
||||||
|
const deliveryFee = ref(0)
|
||||||
|
const outOfRange = ref(false)
|
||||||
const title = ref('支付')
|
const title = ref('支付')
|
||||||
const jsStr = ref('')
|
const jsStr = ref('')
|
||||||
const cart = ref([])
|
const cart = ref([])
|
||||||
@@ -302,8 +306,8 @@ const total = computed(() =>{
|
|||||||
const amount = computed(() =>{
|
const amount = computed(() =>{
|
||||||
let amount = cart.value.reduce((acc, cur) => acc + cur.number * cur.price, 0);
|
let amount = cart.value.reduce((acc, cur) => acc + cur.number * cur.price, 0);
|
||||||
// 加配送费
|
// 加配送费
|
||||||
if (store.value.distance > 0 && orderType.value == 'takeout') {
|
if (orderType.value == 'takeout') {
|
||||||
amount += parseFloat(store.value.deliveryPrice);
|
amount += deliveryFee.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -317,6 +321,33 @@ const amount = computed(() =>{
|
|||||||
return amount.toFixed(2);
|
return amount.toFixed(2);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 选地址/切换外卖后预估配送费(基于收货地址距离匹配配送规则)
|
||||||
|
watch([address, store, orderType], async () => {
|
||||||
|
if (orderType.value !== 'takeout' || !store.value.id) {
|
||||||
|
deliveryFee.value = 0
|
||||||
|
outOfRange.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 未选地址时显示默认配送费
|
||||||
|
if (!address.value || !address.value.longitude) {
|
||||||
|
deliveryFee.value = parseFloat(store.value.deliveryPrice) || 0
|
||||||
|
outOfRange.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await getDeliveryFee({
|
||||||
|
shopId: store.value.id,
|
||||||
|
lng: address.value.longitude,
|
||||||
|
lat: address.value.latitude
|
||||||
|
})
|
||||||
|
outOfRange.value = !!res.outOfRange
|
||||||
|
deliveryFee.value = parseFloat(res.deliveryPrice) || 0
|
||||||
|
} catch (e) {
|
||||||
|
deliveryFee.value = parseFloat(store.value.deliveryPrice) || 0
|
||||||
|
outOfRange.value = false
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
coupon.value = main.mycoupon
|
coupon.value = main.mycoupon
|
||||||
let date = new Date(new Date().getTime() + 3600000); // 一个小时后
|
let date = new Date(new Date().getTime() + 3600000); // 一个小时后
|
||||||
@@ -494,6 +525,11 @@ const submit = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const pay = async() => {
|
const pay = async() => {
|
||||||
|
// 外卖时校验收货地址是否在配送范围
|
||||||
|
if (orderType.value == 'takeout' && outOfRange.value) {
|
||||||
|
uToast.value.show({message:'当前收货地址不在配送范围内',type: 'error'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
let that = this;
|
let that = this;
|
||||||
// // #ifdef MP-WEIXIN
|
// // #ifdef MP-WEIXIN
|
||||||
// await new Promise(function(revolve) {
|
// await new Promise(function(revolve) {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<view class="shop-page__body">
|
<view class="shop-page__body">
|
||||||
<view class="shop-page__info">
|
<view class="shop-page__info">
|
||||||
<view>距离您 {{kmUnit(item.dis)}}</view>
|
<view>距离您 {{kmUnit(item.dis)}}</view>
|
||||||
<view v-if="item.distance > 0">配送距离:{{item.distance + 'km '}} & 配送费:{{item.delivery_price}}</view>
|
<view v-if="item.distance > 0">配送距离:{{item.distance + 'km '}} & 配送费:{{deliveryRuleText(item)}}</view>
|
||||||
<view v-else>外卖不配送</view>
|
<view v-else>外卖不配送</view>
|
||||||
<view>{{item.addressMap + ' ' + item.address}}</view>
|
<view>{{item.addressMap + ' ' + item.address}}</view>
|
||||||
<view>营业时间 {{formatDateTime(item.startTime,'hh:mm')}} - {{formatDateTime(item.endTime,'hh:mm')}}</view>
|
<view>营业时间 {{formatDateTime(item.startTime,'hh:mm')}} - {{formatDateTime(item.endTime,'hh:mm')}}</view>
|
||||||
@@ -74,6 +74,20 @@ const getShop = async(keywork = '') => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 配送规则摘要文本
|
||||||
|
const deliveryRuleText = (item) => {
|
||||||
|
let rules = []
|
||||||
|
try {
|
||||||
|
rules = typeof item.deliveryRules === 'string' && item.deliveryRules
|
||||||
|
? JSON.parse(item.deliveryRules) : (item.deliveryRules || [])
|
||||||
|
} catch (e) {
|
||||||
|
rules = []
|
||||||
|
}
|
||||||
|
if (rules && rules.length) {
|
||||||
|
return rules.map(r => r.minKm + '-' + r.maxKm + 'km ¥' + r.price).join(' / ')
|
||||||
|
}
|
||||||
|
return '¥' + (item.deliveryPrice || 0)
|
||||||
|
}
|
||||||
//打开定位
|
//打开定位
|
||||||
const openLocation = (shop) => {
|
const openLocation = (shop) => {
|
||||||
//console.log(shop);
|
//console.log(shop);
|
||||||
|
|||||||
@@ -366,12 +366,12 @@ const getCartGoodsPrice = computed(() =>{ //计算购物车总价
|
|||||||
return parseFloat(price).toFixed(2);
|
return parseFloat(price).toFixed(2);
|
||||||
})
|
})
|
||||||
const disabledPay = computed(() => { //是否达到起送价
|
const disabledPay = computed(() => { //是否达到起送价
|
||||||
return orderType.value == 'takeout' && (getCartGoodsPrice < parseFloat(store.value.min_price)) ? true :
|
return orderType.value == 'takeout' && (getCartGoodsPrice < parseFloat(store.value.minPrice)) ? true :
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
const spread = computed(() => { //差多少元起送
|
const spread = computed(() => { //差多少元起送
|
||||||
if (orderType.value != 'takeout') return
|
if (orderType.value != 'takeout') return
|
||||||
return parseFloat((store.value.min_price - getCartGoodsPrice).toFixed(2))
|
return parseFloat((store.value.minPrice - getCartGoodsPrice).toFixed(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听自定义事件
|
// 监听自定义事件
|
||||||
@@ -750,8 +750,8 @@ const toPay = () => {
|
|||||||
uToast.value.show({message:'本店不支持外卖',type: 'error'});
|
uToast.value.show({message:'本店不支持外卖',type: 'error'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 判断当前是否在配送范围内
|
// 判断当前是否在配送范围内(基于用户定位距离粗略判断,最终以下单时收货地址校验为准)
|
||||||
if (orderType.value == 'takeout' && store.value.distance < store.value.far) {
|
if (orderType.value == 'takeout' && store.value.dis && store.value.distance < (store.value.dis / 1000)) {
|
||||||
uToast.value.show({message:'选中的地址不在配送范围',type: 'error'});
|
uToast.value.show({message:'选中的地址不在配送范围',type: 'error'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface DeliveryRule {
|
||||||
|
minKm: number
|
||||||
|
maxKm: number
|
||||||
|
price: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface ShopVO {
|
export interface ShopVO {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
@@ -13,6 +19,7 @@ export interface ShopVO {
|
|||||||
distance: number
|
distance: number
|
||||||
minPrice: number
|
minPrice: number
|
||||||
deliveryPrice: number
|
deliveryPrice: number
|
||||||
|
deliveryRules: DeliveryRule[]
|
||||||
notice: string
|
notice: string
|
||||||
status: boolean
|
status: boolean
|
||||||
adminId: string
|
adminId: string
|
||||||
|
|||||||
@@ -46,8 +46,23 @@
|
|||||||
<el-form-item label="起送价钱" prop="minPrice">
|
<el-form-item label="起送价钱" prop="minPrice">
|
||||||
<el-input v-model="formData.minPrice" placeholder="请输入起送价钱" />
|
<el-input v-model="formData.minPrice" placeholder="请输入起送价钱" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="配送价格" prop="deliveryPrice">
|
<el-form-item label="默认配送费" prop="deliveryPrice">
|
||||||
<el-input v-model="formData.deliveryPrice" placeholder="请输入配送价格" />
|
<el-input v-model="formData.deliveryPrice" placeholder="未配置规则或无匹配时的兜底配送费" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="配送规则">
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<div v-for="(rule, index) in formData.deliveryRules" :key="index" class="delivery-rule-row">
|
||||||
|
<el-input v-model="rule.minKm" placeholder="最小km" style="width: 110px;" />
|
||||||
|
<span class="delivery-rule-sep">~</span>
|
||||||
|
<el-input v-model="rule.maxKm" placeholder="最大km" style="width: 110px;" />
|
||||||
|
<span class="delivery-rule-unit">km</span>
|
||||||
|
<el-input v-model="rule.price" placeholder="配送费" style="width: 110px;" />
|
||||||
|
<span class="delivery-rule-unit">元</span>
|
||||||
|
<el-button type="danger" link @click="removeRule(index)">删除</el-button>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" link @click="addRule">+ 添加规则</el-button>
|
||||||
|
<div class="delivery-rule-tip">区间左闭右开,如 1~3km 含 1 不含 3;不配置规则时使用默认配送费</div>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="公告" prop="notice">
|
<el-form-item label="公告" prop="notice">
|
||||||
<el-input type="textarea" rows="5" v-model="formData.notice" placeholder="请输入公告" />
|
<el-input type="textarea" rows="5" v-model="formData.notice" placeholder="请输入公告" />
|
||||||
@@ -111,6 +126,7 @@ const formData = ref({
|
|||||||
distance: undefined,
|
distance: undefined,
|
||||||
minPrice: undefined,
|
minPrice: undefined,
|
||||||
deliveryPrice: undefined,
|
deliveryPrice: undefined,
|
||||||
|
deliveryRules: [],
|
||||||
notice: undefined,
|
notice: undefined,
|
||||||
status: undefined,
|
status: undefined,
|
||||||
adminId: undefined,
|
adminId: undefined,
|
||||||
@@ -159,6 +175,7 @@ const open = async (type: string, id?: number) => {
|
|||||||
try {
|
try {
|
||||||
formData.value = await ShopApi.getShop(id)
|
formData.value = await ShopApi.getShop(id)
|
||||||
formData.value.adminId = formData.value.adminId.map(Number)
|
formData.value.adminId = formData.value.adminId.map(Number)
|
||||||
|
formData.value.deliveryRules = formData.value.deliveryRules || []
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -197,6 +214,19 @@ const openForm = () => {
|
|||||||
formRef1.value.open()
|
formRef1.value.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 新增一条配送规则 */
|
||||||
|
const addRule = () => {
|
||||||
|
if (!formData.value.deliveryRules) {
|
||||||
|
formData.value.deliveryRules = []
|
||||||
|
}
|
||||||
|
formData.value.deliveryRules.push({ minKm: undefined, maxKm: undefined, price: undefined })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除一条配送规则 */
|
||||||
|
const removeRule = (index: number) => {
|
||||||
|
formData.value.deliveryRules.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
const mapConfirm = (mapResult) => {
|
const mapConfirm = (mapResult) => {
|
||||||
console.log('mapResult:',mapResult)
|
console.log('mapResult:',mapResult)
|
||||||
formData.value.addressMap = mapResult.address
|
formData.value.addressMap = mapResult.address
|
||||||
@@ -233,6 +263,7 @@ const resetForm = () => {
|
|||||||
distance: undefined,
|
distance: undefined,
|
||||||
minPrice: undefined,
|
minPrice: undefined,
|
||||||
deliveryPrice: undefined,
|
deliveryPrice: undefined,
|
||||||
|
deliveryRules: [],
|
||||||
notice: undefined,
|
notice: undefined,
|
||||||
status: 1,
|
status: 1,
|
||||||
adminId: undefined,
|
adminId: undefined,
|
||||||
@@ -248,4 +279,19 @@ const resetForm = () => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 300px;
|
height: 300px;
|
||||||
}
|
}
|
||||||
|
.delivery-rule-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.delivery-rule-sep,
|
||||||
|
.delivery-rule-unit {
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
.delivery-rule-tip {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in new issue
Block a user