From 7679a7d09125681644851ecf7a158d4a083d5cae Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Tue, 15 Sep 2026 14:10:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A5=97=E9=A4=90=E5=8A=A0=E5=88=86=E5=8C=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/i18n/messages.properties | 1 + .../ruoyi/price/domain/BmsProductCountry.java | 6 + .../ruoyi/price/domain/PrcPriceSetDetail.java | 6 + .../price/mapper/PrcPriceSetDetailMapper.java | 3 +- .../service/impl/BmsProductServiceImpl.java | 135 ++++-- .../service/impl/PrcPriceSetServiceImpl.java | 142 +++++- .../service/impl/PrcZoneServiceImpl.java | 79 +-- .../impl/TmsPriceQuoteServiceImpl.java | 3 +- .../com/ruoyi/price/util/ZoneCodeAbbrevs.java | 144 ++++++ .../mapper/price/BmsProductCountryMapper.xml | 24 +- .../mapper/price/PrcPriceSetDetailMapper.xml | 21 + bms-back/sql/price/price_set.sql | 9 +- .../sql/price/price_set_detail_zone_alter.sql | 55 +++ bms-back/sql/price/product.sql | 7 +- .../sql/price/product_country_zone_alter.sql | 74 +++ .../components/ProductCountryDrawer.vue | 32 +- .../basic/zone/components/PostcodeDrawer.vue | 135 ++++++ .../zone/components/PostcodeFormDialog.vue | 63 +++ .../basic/zone/components/ZoneDrawer.vue | 222 +++++++++ .../basic/zone/components/ZoneFormDialog.vue | 100 ++++ .../src/views/price/basic/zone/index.vue | 457 +----------------- .../set/components/create/StepDetails.vue | 29 +- .../set/components/detail/SetDetailDialog.vue | 23 +- .../set/components/detail/SetDetailTable.vue | 5 +- .../set/composables/usePriceSetRefData.js | 44 +- .../src/views/price/set/utils/priceSet.js | 57 ++- 26 files changed, 1279 insertions(+), 597 deletions(-) create mode 100644 bms-back/ruoyi-system/src/main/java/com/ruoyi/price/util/ZoneCodeAbbrevs.java create mode 100644 bms-back/sql/price/price_set_detail_zone_alter.sql create mode 100644 bms-back/sql/price/product_country_zone_alter.sql create mode 100644 bms-front/src/views/price/basic/zone/components/PostcodeDrawer.vue create mode 100644 bms-front/src/views/price/basic/zone/components/PostcodeFormDialog.vue create mode 100644 bms-front/src/views/price/basic/zone/components/ZoneDrawer.vue create mode 100644 bms-front/src/views/price/basic/zone/components/ZoneFormDialog.vue diff --git a/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties b/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties index 19d48034..693f561e 100644 --- a/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/bms-back/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -70,6 +70,7 @@ price.set.detail.country.empty=固定价明细必须填写国家 price.set.detail.weight.empty=固定价明细必须填写重量段 price.set.detail.freight.empty=固定价明细必须填写运费单价 price.set.detail.registration.empty=固定价明细必须填写挂号费 +price.set.detail.zone.empty=固定价明细必须选择分区 price.set.detail.roundMode.invalid=取值方式仅支持 UP / DOWN / HALF_UP price.set.customer.notExists=客户绑定不存在 price.set.customer.unbindAudit=审核中的套餐不允许解绑客户 diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/BmsProductCountry.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/BmsProductCountry.java index e034774d..551abc29 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/BmsProductCountry.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/BmsProductCountry.java @@ -18,6 +18,12 @@ public class BmsProductCountry implements Serializable private String productCode; private String countryName; private String countryCode; + /** prc_zone.zone_id; null when product country has no zone */ + private Long zoneId; + /** e.g. Z1; null when no zone */ + private String zoneCode; + /** e.g. Australia(Z1); null when no zone */ + private String zoneName; private BigDecimal weightMin; private BigDecimal weightMax; private BigDecimal freightPrice; diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/PrcPriceSetDetail.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/PrcPriceSetDetail.java index dc1e877a..e0b4e68a 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/PrcPriceSetDetail.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/PrcPriceSetDetail.java @@ -18,6 +18,12 @@ public class PrcPriceSetDetail implements Serializable private String productName; private String countryCode; private String countryName; + /** prc_zone.zone_id; null when country has no zone */ + private Long zoneId; + /** e.g. Z1; null when no zone */ + private String zoneCode; + /** zone display name; null when no zone */ + private String zoneName; private Long zdId; private Long wsdId; private String weightLabel; diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcPriceSetDetailMapper.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcPriceSetDetailMapper.java index 180dfaea..d1667dc0 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcPriceSetDetailMapper.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcPriceSetDetailMapper.java @@ -27,12 +27,13 @@ public interface PrcPriceSetDetailMapper public int countByPriceSetId(Long priceSetId); - /** FIXED:按产品+国家+关联产品国家明细/重量段命中 */ + /** FIXED: product + country + zone + productCountryId / weight */ public PrcPriceSetDetail selectFixedMatch( @Param("priceSetId") Long priceSetId, @Param("productCode") String productCode, @Param("countryCode") String countryCode, @Param("countryName") String countryName, + @Param("zoneCode") String zoneCode, @Param("productCountryId") Long productCountryId, @Param("weightLabel") String weightLabel); diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/BmsProductServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/BmsProductServiceImpl.java index a8b3b55b..8a5c4bb8 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/BmsProductServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/BmsProductServiceImpl.java @@ -34,10 +34,13 @@ import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.price.domain.BmsProduct; import com.ruoyi.price.domain.BmsProductCountry; +import com.ruoyi.price.domain.PrcZone; import com.ruoyi.price.mapper.BmsProductCountryMapper; import com.ruoyi.price.mapper.BmsProductMapper; +import com.ruoyi.price.mapper.PrcZoneMapper; import com.ruoyi.price.service.IBmsProductService; import com.ruoyi.price.service.IPrcSurchargeRelService; +import com.ruoyi.price.util.ZoneCodeAbbrevs; import com.ruoyi.system.domain.BmsRegion; import com.ruoyi.system.mapper.BmsRegionMapper; @@ -74,6 +77,9 @@ public class BmsProductServiceImpl implements IBmsProductService @Autowired private BmsRegionMapper regionMapper; + @Autowired + private PrcZoneMapper zoneMapper; + @Autowired private IPrcSurchargeRelService surchargeRelService; @@ -248,6 +254,7 @@ public class BmsProductServiceImpl implements IBmsProductService throw new ServiceException(MessageUtils.message("price.product.import.regionEmpty")); } List euRegions = loadEuRegions(regionByNameCn); + Map zoneByCountryAndCode = loadZoneByCountryAndCode(); int successNum = 0; int skipNum = 0; @@ -263,7 +270,7 @@ public class BmsProductServiceImpl implements IBmsProductService try { int[] nums = importOneProduct(workbook, product, operName, regionByNameCn, euRegions, - unmatchedCountries); + zoneByCountryAndCode, unmatchedCountries); transactionManager.commit(tx); successNum += nums[0]; skipNum += nums[1]; @@ -300,7 +307,8 @@ public class BmsProductServiceImpl implements IBmsProductService /** @return int[]{added, skipped, countryRows} */ private int[] importOneProduct(Workbook workbook, BmsProduct product, String operName, - Map regionByNameCn, List euRegions, Set unmatchedCountries) + Map regionByNameCn, List euRegions, + Map zoneByCountryAndCode, Set unmatchedCountries) { product.setSourceSheet(product.getProductName()); product.setCreateBy(operName); @@ -314,7 +322,7 @@ public class BmsProductServiceImpl implements IBmsProductService } product.setSourceSheet(productSheet.getSheetName()); List countries = parseProductSheet(productSheet, product, - regionByNameCn, euRegions, unmatchedCountries); + regionByNameCn, euRegions, zoneByCountryAndCode, unmatchedCountries); if (countries == null || countries.isEmpty()) { throw new ServiceException(MessageUtils.message("price.product.import.noCountry", @@ -412,7 +420,8 @@ public class BmsProductServiceImpl implements IBmsProductService { throw new ServiceException(MessageUtils.message("price.product.import.needFeeAlter")); } - if (tip.contains("expire_date") || tip.contains("uk_product_country_weight")) + if (tip.contains("expire_date") || tip.contains("uk_product_country_weight") + || tip.contains("uk_product_country_zone_weight")) { throw new ServiceException(MessageUtils.message("price.product.import.needExpireAlter")); } @@ -716,7 +725,8 @@ public class BmsProductServiceImpl implements IBmsProductService } private List parseProductSheet(Sheet sheet, BmsProduct product, - Map regionByNameCn, List euRegions, Set unmatchedCountries) + Map regionByNameCn, List euRegions, + Map zoneByCountryAndCode, Set unmatchedCountries) { if (sheet == null) { @@ -739,7 +749,6 @@ public class BmsProductServiceImpl implements IBmsProductService int colSize = findColByHeader(headerRow, "\u5c3a\u5bf8", colCountry + 1); int colLastMile = findColByHeader(headerRow, "\u5c3e\u7a0b", colCountry + 1); int colLeadTime = findColByHeader(headerRow, "\u65f6\u6548", colCountry + 1); - // Fallback offsets when header text missing (Weishi layout: B=country ... H=lead time) if (colWeight < 0) { colWeight = colCountry + 1; @@ -765,7 +774,7 @@ public class BmsProductServiceImpl implements IBmsProductService colLeadTime = colCountry + 6; } - String currentCountry = null; + String currentCountryRaw = null; String currentSize = null; String currentLastMile = null; String currentLeadTime = null; @@ -785,12 +794,12 @@ public class BmsProductServiceImpl implements IBmsProductService } if (StringUtils.isNotEmpty(countryCell)) { - currentCountry = normalizeCountryName(countryCell); + currentCountryRaw = countryCell; currentSize = emptyToNull(cellString(row.getCell(colSize))); currentLastMile = emptyToNull(cellString(row.getCell(colLastMile))); currentLeadTime = emptyToNull(cellString(row.getCell(colLeadTime))); } - if (StringUtils.isEmpty(currentCountry)) + if (StringUtils.isEmpty(currentCountryRaw)) { continue; } @@ -803,14 +812,23 @@ public class BmsProductServiceImpl implements IBmsProductService BigDecimal registration = cellDecimal(row.getCell(colRegister)); Date eff = sheetEffective != null ? sheetEffective : product.getEffectiveDate(); - BmsRegion region = resolveRegion(currentCountry, regionByNameCn); + ZoneCodeAbbrevs.CountryZoneParts parts = ZoneCodeAbbrevs.splitCountryZone(currentCountryRaw); + if (parts == null || StringUtils.isEmpty(parts.countryName)) + { + continue; + } + String baseCountry = parts.countryName; + String zoneLabel = parts.zoneLabel; + + BmsRegion region = resolveRegion(baseCountry, regionByNameCn); if (region != null) { list.add(buildCountryRow(region.getCountryNameCn(), region.getCountryCode2(), + zoneLabel, zoneByCountryAndCode, range, freight, registration, currentSize, currentLastMile, currentLeadTime, eff)); continue; } - if (isEuZoneName(currentCountry)) + if (isEuZoneName(baseCountry) && StringUtils.isEmpty(zoneLabel)) { if (euRegions == null || euRegions.isEmpty()) { @@ -819,23 +837,26 @@ public class BmsProductServiceImpl implements IBmsProductService for (BmsRegion eu : euRegions) { list.add(buildCountryRow(eu.getCountryNameCn(), eu.getCountryCode2(), + null, zoneByCountryAndCode, range, freight, registration, currentSize, currentLastMile, currentLeadTime, eff)); } continue; } - unmatchedCountries.add(currentCountry); - list.add(buildCountryRow(currentCountry, null, + unmatchedCountries.add(currentCountryRaw.trim().replace("\n", " ").replace("\r", " ")); + list.add(buildCountryRow(baseCountry, null, zoneLabel, zoneByCountryAndCode, range, freight, registration, currentSize, currentLastMile, currentLeadTime, eff)); } return list; } - private BmsProductCountry buildCountryRow(String countryName, String countryCode, BigDecimal[] range, + private BmsProductCountry buildCountryRow(String countryName, String countryCode, String zoneLabel, + Map zoneByCountryAndCode, BigDecimal[] range, BigDecimal freight, BigDecimal registration, String sizeLimit, String lastMile, String leadTime, Date eff) { BmsProductCountry c = new BmsProductCountry(); c.setCountryName(countryName); c.setCountryCode(countryCode); + applyZoneFields(c, countryCode, zoneLabel, zoneByCountryAndCode); c.setWeightMin(range[0]); c.setWeightMax(range[1]); c.setFreightPrice(freight); @@ -847,6 +868,68 @@ public class BmsProductServiceImpl implements IBmsProductService return c; } + private void applyZoneFields(BmsProductCountry c, String countryCode, String zoneLabel, + Map zoneByCountryAndCode) + { + if (StringUtils.isEmpty(zoneLabel)) + { + c.setZoneId(null); + c.setZoneCode(null); + c.setZoneName(null); + return; + } + String zoneCode = ZoneCodeAbbrevs.toAbbrev(zoneLabel); + c.setZoneCode(zoneCode); + PrcZone zone = null; + if (StringUtils.isNotEmpty(countryCode) && zoneByCountryAndCode != null) + { + zone = zoneByCountryAndCode.get(zoneKey(countryCode, zoneCode)); + } + if (zone != null) + { + c.setZoneId(zone.getZoneId()); + c.setZoneCode(zone.getZoneCode()); + c.setZoneName(zone.getZoneName()); + } + else + { + c.setZoneId(null); + c.setZoneName(StringUtils.isNotEmpty(c.getCountryName()) + ? c.getCountryName() + "(" + zoneCode + ")" + : zoneCode); + } + } + + private Map loadZoneByCountryAndCode() + { + Map map = new HashMap<>(); + List list = zoneMapper.selectPrcZoneList(new PrcZone()); + if (list == null) + { + return map; + } + for (PrcZone zone : list) + { + if (zone == null || StringUtils.isEmpty(zone.getZoneCode())) + { + continue; + } + String code2 = zone.getCountryCode(); + if (StringUtils.isEmpty(code2)) + { + continue; + } + map.put(zoneKey(code2, zone.getZoneCode()), zone); + } + return map; + } + + private static String zoneKey(String countryCode, String zoneCode) + { + return String.valueOf(countryCode).trim().toUpperCase(Locale.ROOT) + + "|" + String.valueOf(zoneCode).trim().toUpperCase(Locale.ROOT); + } + /** * Load bms_region map: country_name_cn -> region (for country_code_2). */ @@ -907,7 +990,6 @@ public class BmsProductServiceImpl implements IBmsProductService { return candidates.get(0); } - // Prefer longest Chinese name match (e.g. 捷克共和国 over 捷克) BmsRegion best = candidates.get(0); for (int i = 1; i < candidates.size(); i++) { @@ -920,29 +1002,6 @@ public class BmsProductServiceImpl implements IBmsProductService return best; } - /** Strip Excel footnotes like "澳大利亚(1区)\\n分区点击" */ - private static String normalizeCountryName(String name) - { - if (name == null) - { - return null; - } - String n = name.trim(); - int cut = n.length(); - int nl = n.indexOf('\n'); - if (nl >= 0 && nl < cut) - { - cut = nl; - } - int cr = n.indexOf('\r'); - if (cr >= 0 && cr < cut) - { - cut = cr; - } - n = n.substring(0, cut).trim(); - return n.isEmpty() ? null : n; - } - private BigDecimal[] parseWeightRange(String label) { if (StringUtils.isEmpty(label)) diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java index b363d397..4f7193e4 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcPriceSetServiceImpl.java @@ -248,6 +248,9 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService nd.setProductName(d.getProductName()); nd.setCountryCode(d.getCountryCode()); nd.setCountryName(d.getCountryName()); + nd.setZoneId(d.getZoneId()); + nd.setZoneCode(d.getZoneCode()); + nd.setZoneName(d.getZoneName()); nd.setZdId(d.getZdId()); nd.setWsdId(d.getWsdId()); nd.setWeightLabel(d.getWeightLabel()); @@ -659,6 +662,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { throw new ServiceException(MessageUtils.message("price.set.detail.country.empty")); } + requireZoneIfNeeded(detail); if (detail.getWsdId() == null && StringUtils.isEmpty(detail.getWeightLabel())) { throw new ServiceException(MessageUtils.message("price.set.detail.weight.empty")); @@ -692,6 +696,29 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } } + /** + * When product country rows for this country have zones, zone is required. + */ + private void requireZoneIfNeeded(PrcPriceSetDetail detail) + { + if (detail.getZoneId() != null || StringUtils.isNotEmpty(detail.getZoneCode())) + { + return; + } + List rows = listOpenProductCountries(detail.getProductCode()); + for (BmsProductCountry row : rows) + { + if (!matchDetailCountryOnly(detail, row)) + { + continue; + } + if (row.getZoneId() != null || StringUtils.isNotEmpty(row.getZoneCode())) + { + throw new ServiceException(MessageUtils.message("price.set.detail.zone.empty")); + } + } + } + /** * Resolve / verify product_country_id from bms_product_country when missing. */ @@ -699,25 +726,16 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { if (detail.getProductCountryId() != null) { + syncZoneFromProductCountry(detail); return; } if (StringUtils.isEmpty(detail.getProductCode()) || StringUtils.isEmpty(detail.getWeightLabel())) { return; } - BmsProductCountry query = new BmsProductCountry(); - query.setProductCode(detail.getProductCode()); - List rows = productCountryMapper.selectBmsProductCountryList(query); - if (rows == null || rows.isEmpty()) - { - return; - } + List rows = listOpenProductCountries(detail.getProductCode()); for (BmsProductCountry row : rows) { - if (row.getExpireDate() != null) - { - continue; - } if (!matchDetailCountry(detail, row)) { continue; @@ -730,12 +748,80 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService && detail.getWeightLabel().contains(stripTrailingZeros(row.getWeightMax())))) { detail.setProductCountryId(row.getDetailId()); + applyZoneFromRow(detail, row); return; } } } - private static boolean matchDetailCountry(PrcPriceSetDetail detail, BmsProductCountry row) + private void syncZoneFromProductCountry(PrcPriceSetDetail detail) + { + if (detail.getProductCountryId() == null) + { + return; + } + if (detail.getZoneId() != null || StringUtils.isNotEmpty(detail.getZoneCode())) + { + return; + } + BmsProductCountry query = new BmsProductCountry(); + query.setProductCode(detail.getProductCode()); + List rows = productCountryMapper.selectBmsProductCountryList(query); + if (rows == null) + { + return; + } + for (BmsProductCountry row : rows) + { + if (detail.getProductCountryId().equals(row.getDetailId())) + { + applyZoneFromRow(detail, row); + return; + } + } + } + + private List listOpenProductCountries(String productCode) + { + if (StringUtils.isEmpty(productCode)) + { + return java.util.Collections.emptyList(); + } + BmsProductCountry query = new BmsProductCountry(); + query.setProductCode(productCode); + List rows = productCountryMapper.selectBmsProductCountryList(query); + if (rows == null || rows.isEmpty()) + { + return java.util.Collections.emptyList(); + } + List open = new ArrayList(); + for (BmsProductCountry row : rows) + { + if (row.getExpireDate() == null) + { + open.add(row); + } + } + return open; + } + + private static void applyZoneFromRow(PrcPriceSetDetail detail, BmsProductCountry row) + { + if (detail.getZoneId() == null) + { + detail.setZoneId(row.getZoneId()); + } + if (StringUtils.isEmpty(detail.getZoneCode())) + { + detail.setZoneCode(row.getZoneCode()); + } + if (StringUtils.isEmpty(detail.getZoneName())) + { + detail.setZoneName(row.getZoneName()); + } + } + + private static boolean matchDetailCountryOnly(PrcPriceSetDetail detail, BmsProductCountry row) { if (StringUtils.isNotEmpty(detail.getCountryCode()) && StringUtils.isNotEmpty(row.getCountryCode())) { @@ -745,6 +831,38 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService && detail.getCountryName().equals(row.getCountryName()); } + private static boolean matchDetailCountry(PrcPriceSetDetail detail, BmsProductCountry row) + { + if (!matchDetailCountryOnly(detail, row)) + { + return false; + } + return matchDetailZone(detail, row); + } + + private static boolean matchDetailZone(PrcPriceSetDetail detail, BmsProductCountry row) + { + boolean detailHas = detail.getZoneId() != null || StringUtils.isNotEmpty(detail.getZoneCode()); + boolean rowHas = row.getZoneId() != null || StringUtils.isNotEmpty(row.getZoneCode()); + if (!detailHas && !rowHas) + { + return true; + } + if (detailHas != rowHas) + { + return false; + } + if (detail.getZoneId() != null && row.getZoneId() != null) + { + return detail.getZoneId().equals(row.getZoneId()); + } + if (StringUtils.isNotEmpty(detail.getZoneCode()) && StringUtils.isNotEmpty(row.getZoneCode())) + { + return detail.getZoneCode().equalsIgnoreCase(row.getZoneCode()); + } + return false; + } + private static String formatWeightLabel(BigDecimal min, BigDecimal max) { return stripTrailingZeros(min) + "> entry : entries) { - String zoneCode = toZoneCodeAbbrev(entry.getKey()); + String zoneCode = ZoneCodeAbbrevs.toAbbrev(entry.getKey()); PrcZone zone = new PrcZone(); zone.setRegionId(regionId); zone.setCountryCode(region.getCountryCode2()); @@ -868,78 +869,6 @@ public class PrcZoneServiceImpl implements IPrcZoneService return (countryName == null ? "" : countryName) + "(" + zoneCode + ")"; } - /** - * Map Excel/local zone labels to English abbreviations for zone_code. - */ - private static String toZoneCodeAbbrev(String raw) - { - if (StringUtils.isEmpty(raw)) - { - return raw; - } - String key = raw.replace('\r', ' ').replace('\n', ' ').replaceAll("\\s+", " ").trim(); - switch (key) - { - case "1\u533a": - return "Z1"; - case "2\u533a": - return "Z2"; - case "3\u533a": - return "Z3"; - case "4\u533a": - return "Z4"; - case "5\u533a": - return "Z5"; - case "Mondial Relay": - return "MR"; - case "\u6c96\u7e04\u770c": - return "OKN"; - case "\u5317\u7231\u5c14\u5170": - return "NI"; - case "\u9ad8\u5730\u548c\u5c9b\u5c7f": - return "HI"; - case "\u6d77\u5ce1\u7fa4\u5c9b(\u542b\u9521\u5229\u5c9b)": - return "CI"; - case "\u9a6c\u6069\u5c9b": - return "IM"; - case "Beckton": - return "BEC"; - case "Bristol": - return "BRS"; - case "Cambridge": - return "CAM"; - case "Collier Wood": - return "CW"; - case "Guernsey": - return "GG"; - case "Isle of Man Bailiwick of Guernsey States of Jersey": - return "IM_GG_JE"; - case "Lakeside": - return "LKS"; - case "London Congestion Zone": - return "LCZ"; - case "London Congestion Zone - Gemini": - return "LCZ_GEM"; - case "London ULEZ - Wimbledon": - return "ULEZ_WIM"; - case "Northern Ireland Isles of Scilly": - return "NI_SCILLY"; - case "Old Kent Road Cargo Bikes": - return "OKR_CB"; - case "Oxford": - return "OXF"; - case "The Highlands Islands of Scotland": - return "HIGHLANDS"; - default: - // already abbrev or unknown: keep trimmed original (spaces -> underscore) - if (key.matches(".*[\\u4e00-\\u9fff].*")) - { - return key; - } - return key.replace(' ', '_').toUpperCase(Locale.ROOT).replaceAll("_+", "_"); - } - } - private static int extractZoneSort(String zoneCode) { if (StringUtils.isEmpty(zoneCode)) diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java index ae8d8ca6..1c9e1d8e 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/TmsPriceQuoteServiceImpl.java @@ -248,7 +248,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService PrcPriceSetDetail detail = detailMapper.selectFixedMatch( set.getPriceSetId(), productCode, - base.getCountryCode(), base.getCountryName(), base.getDetailId(), + base.getCountryCode(), base.getCountryName(), base.getZoneCode(), + base.getDetailId(), formatWeightLabel(base.getWeightMin(), base.getWeightMax())); if (detail == null) { diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/util/ZoneCodeAbbrevs.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/util/ZoneCodeAbbrevs.java new file mode 100644 index 00000000..a3ab48a1 --- /dev/null +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/util/ZoneCodeAbbrevs.java @@ -0,0 +1,144 @@ +package com.ruoyi.price.util; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import com.ruoyi.common.utils.StringUtils; + +/** + * Parse Excel country cell with optional zone suffix, map labels to English abbreviations. + */ +public final class ZoneCodeAbbrevs +{ + // fullwidth () and ASCII () + private static final Pattern ZONE_SUFFIX = Pattern.compile( + "^(.+?)[\\(\\uFF08]([^\\)\\uFF09]+)[\\)\\uFF09]\\s*$"); + + private ZoneCodeAbbrevs() + { + } + + public static final class CountryZoneParts + { + public final String countryName; + public final String zoneLabel; + + public CountryZoneParts(String countryName, String zoneLabel) + { + this.countryName = countryName; + this.zoneLabel = zoneLabel; + } + } + + /** + * Split country cell like Country(zoneLabel); no parenthesis -> zoneLabel null. + */ + public static CountryZoneParts splitCountryZone(String raw) + { + if (StringUtils.isEmpty(raw)) + { + return null; + } + String n = raw.trim(); + int cut = n.length(); + int nl = n.indexOf('\n'); + if (nl >= 0 && nl < cut) + { + cut = nl; + } + int cr = n.indexOf('\r'); + if (cr >= 0 && cr < cut) + { + cut = cr; + } + n = n.substring(0, cut).trim(); + if (n.isEmpty()) + { + return null; + } + Matcher m = ZONE_SUFFIX.matcher(n); + if (m.matches()) + { + String country = m.group(1).trim(); + String zone = m.group(2).trim(); + if (StringUtils.isEmpty(country)) + { + return new CountryZoneParts(n, null); + } + return new CountryZoneParts(country, StringUtils.isEmpty(zone) ? null : zone); + } + return new CountryZoneParts(n, null); + } + + public static String toAbbrev(String raw) + { + if (StringUtils.isEmpty(raw)) + { + return raw; + } + String key = raw.replace('\r', ' ').replace('\n', ' ').replaceAll("\\s+", " ").trim(); + switch (key) + { + case "1\u533a": + return "Z1"; + case "2\u533a": + return "Z2"; + case "3\u533a": + return "Z3"; + case "4\u533a": + return "Z4"; + case "5\u533a": + return "Z5"; + case "Mondial Relay": + return "MR"; + case "\u6c96\u7e04\u770c": + return "OKN"; + case "\u5317\u7231\u5c14\u5170": + return "NI"; + case "\u9ad8\u5730\u548c\u5c9b\u5c7f": + return "HI"; + case "\u6d77\u5ce1\u7fa4\u5c9b(\u542b\u9521\u5229\u5c9b)": + return "CI"; + case "\u9a6c\u6069\u5c9b": + return "IM"; + case "Beckton": + return "BEC"; + case "Bristol": + return "BRS"; + case "Cambridge": + return "CAM"; + case "Collier Wood": + return "CW"; + case "Guernsey": + return "GG"; + case "Isle of Man Bailiwick of Guernsey States of Jersey": + return "IM_GG_JE"; + case "Lakeside": + return "LKS"; + case "London Congestion Zone": + return "LCZ"; + case "London Congestion Zone - Gemini": + return "LCZ_GEM"; + case "London ULEZ - Wimbledon": + return "ULEZ_WIM"; + case "Northern Ireland Isles of Scilly": + return "NI_SCILLY"; + case "Old Kent Road Cargo Bikes": + return "OKR_CB"; + case "Oxford": + return "OXF"; + case "The Highlands Islands of Scotland": + return "HIGHLANDS"; + default: + if (key.matches("(?i)Z[1-9]\\d*")) + { + return key.toUpperCase(Locale.ROOT); + } + if (key.matches(".*[\\u4e00-\\u9fff].*")) + { + return key; + } + return key.replace(' ', '_').toUpperCase(Locale.ROOT).replaceAll("_+", "_"); + } + } +} diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml index 6115a410..03344aaf 100644 --- a/bms-back/ruoyi-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml +++ b/bms-back/ruoyi-system/src/main/resources/mapper/price/BmsProductCountryMapper.xml @@ -10,6 +10,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + @@ -26,7 +29,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select detail_id, product_id, product_code, country_name, country_code, weight_min, weight_max, + select detail_id, product_id, product_code, country_name, country_code, zone_id, zone_code, zone_name, + weight_min, weight_max, freight_price, registration_fee, size_limit, last_mile, lead_time, effective_date, expire_date, status, create_time, update_time from bms_product_country @@ -47,6 +51,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND country_code = #{countryCode} + + AND zone_id = #{zoneId} + + + AND zone_code = #{zoneCode} + AND weight_min = #{weightMin} @@ -66,16 +76,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND status = #{effectStatus} - order by effective_date desc, country_name asc, weight_min asc, detail_id asc + order by effective_date desc, country_name asc, zone_code asc, weight_min asc, detail_id asc insert into bms_product_country ( - product_id, product_code, country_name, country_code, weight_min, weight_max, + product_id, product_code, country_name, country_code, zone_id, zone_code, zone_name, + weight_min, weight_max, freight_price, registration_fee, size_limit, last_mile, lead_time, effective_date, expire_date, status, create_time ) values ( - #{productId}, #{productCode}, #{countryName}, #{countryCode}, #{weightMin}, #{weightMax}, + #{productId}, #{productCode}, #{countryName}, #{countryCode}, #{zoneId}, #{zoneCode}, #{zoneName}, + #{weightMin}, #{weightMax}, #{freightPrice}, #{registrationFee}, #{sizeLimit}, #{lastMile}, #{leadTime}, #{effectiveDate}, #{expireDate}, IFNULL(#{status}, '0'), sysdate() ) @@ -162,7 +174,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and expire_date is null - order by effective_date desc, weight_min asc, detail_id asc + order by + case when zone_code is null or zone_code = '' then 0 else 1 end, + effective_date desc, weight_min asc, detail_id asc limit 1 diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml index 4615496c..76a7d279 100644 --- a/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml +++ b/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcPriceSetDetailMapper.xml @@ -11,6 +11,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + @@ -26,6 +29,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select psd_id, price_set_id, product_code, product_name, country_code, country_name, + zone_id, zone_code, zone_name, zd_id, wsd_id, weight_label, product_country_id, fv_value, fv_registration_fee, discount_value, round_mode, pr_id, create_time, update_time from prc_price_set_detail @@ -48,6 +52,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND country_code = #{countryCode} + + AND zone_code = #{zoneCode} + order by psd_id asc @@ -67,6 +74,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" product_name, country_code, country_name, + zone_id, + zone_code, + zone_name, zd_id, wsd_id, weight_label, @@ -83,6 +93,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{productName}, #{countryCode}, #{countryName}, + #{zoneId}, + #{zoneCode}, + #{zoneName}, #{zdId}, #{wsdId}, #{weightLabel}, @@ -103,6 +116,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" product_name = #{productName}, country_code = #{countryCode}, country_name = #{countryName}, + zone_id = #{zoneId}, + zone_code = #{zoneCode}, + zone_name = #{zoneName}, zd_id = #{zdId}, wsd_id = #{wsdId}, weight_label = #{weightLabel}, @@ -149,6 +165,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 1 = 0 + + and (zone_code is null or upper(zone_code) = upper(#{zoneCode})) + and (weight_label is null or weight_label = #{weightLabel} or weight_label like concat('%', #{weightLabel}, '%')) @@ -156,6 +175,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ) order by case when #{productCountryId} is not null and product_country_id = #{productCountryId} then 0 else 1 end, + case when #{zoneCode} is not null and upper(ifnull(zone_code,'')) = upper(#{zoneCode}) then 0 else 1 end, + case when zone_code is null then 1 else 0 end, psd_id asc limit 1 diff --git a/bms-back/sql/price/price_set.sql b/bms-back/sql/price/price_set.sql index f7bc6cc7..5807678b 100644 --- a/bms-back/sql/price/price_set.sql +++ b/bms-back/sql/price/price_set.sql @@ -38,9 +38,12 @@ CREATE TABLE IF NOT EXISTS prc_price_set_detail ( price_set_id BIGINT(20) NOT NULL COMMENT '价格套餐id', product_code VARCHAR(10) NOT NULL COMMENT '产品代码', product_name VARCHAR(50) DEFAULT NULL COMMENT '产品名称', - country_code CHAR(2) DEFAULT NULL COMMENT '国家二字码', - country_name VARCHAR(50) DEFAULT NULL COMMENT '国家名称', - zd_id BIGINT(20) DEFAULT NULL COMMENT '收费区划id', + country_code CHAR(2) DEFAULT NULL COMMENT 'country ISO2', + country_name VARCHAR(50) DEFAULT NULL COMMENT 'country name', + zone_id BIGINT(20) DEFAULT NULL COMMENT 'prc_zone.zone_id', + zone_code VARCHAR(50) DEFAULT NULL COMMENT 'zone code e.g. Z1', + zone_name VARCHAR(100) DEFAULT NULL COMMENT 'zone display name', + zd_id BIGINT(20) DEFAULT NULL COMMENT 'legacy zone division id', wsd_id BIGINT(20) DEFAULT NULL COMMENT '重量段明细id', weight_label VARCHAR(50) DEFAULT NULL COMMENT '重量段展示', product_country_id BIGINT(20) DEFAULT NULL COMMENT '关联 bms_product_country.detail_id', diff --git a/bms-back/sql/price/price_set_detail_zone_alter.sql b/bms-back/sql/price/price_set_detail_zone_alter.sql new file mode 100644 index 00000000..db929899 --- /dev/null +++ b/bms-back/sql/price/price_set_detail_zone_alter.sql @@ -0,0 +1,55 @@ +-- Add zone fields to prc_price_set_detail (nullable when country has no zone) +-- Idempotent: safe to re-run +-- charset: utf8mb4 + +SET NAMES utf8mb4; +SET @db := DATABASE(); + +-- zone_id +SET @has := ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'zone_id' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE prc_price_set_detail ADD COLUMN zone_id BIGINT(20) DEFAULT NULL COMMENT ''prc_zone.zone_id'' AFTER country_name', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- zone_code +SET @has := ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'zone_code' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE prc_price_set_detail ADD COLUMN zone_code VARCHAR(50) DEFAULT NULL COMMENT ''zone code e.g. Z1'' AFTER zone_id', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- zone_name +SET @has := ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'zone_name' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE prc_price_set_detail ADD COLUMN zone_name VARCHAR(100) DEFAULT NULL COMMENT ''zone display name'' AFTER zone_code', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- index +SET @has := ( + SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'idx_psd_zone' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_zone (zone_id)', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @has := ( + SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'idx_psd_country_zone' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_country_zone (price_set_id, product_code, country_code, zone_code)', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/bms-back/sql/price/product.sql b/bms-back/sql/price/product.sql index 38315d29..94111bd1 100644 --- a/bms-back/sql/price/product.sql +++ b/bms-back/sql/price/product.sql @@ -34,6 +34,9 @@ CREATE TABLE IF NOT EXISTS bms_product_country ( product_code varchar(20) NOT NULL COMMENT '产品代码', country_name varchar(50) NOT NULL COMMENT '国家中文名', country_code char(2) DEFAULT NULL COMMENT 'ISO2,可空待补齐', + zone_id bigint DEFAULT NULL COMMENT 'prc_zone.zone_id', + zone_code varchar(50) DEFAULT NULL COMMENT 'zone code e.g. Z1', + zone_name varchar(100) DEFAULT NULL COMMENT 'zone display name', weight_min decimal(10,3) DEFAULT NULL COMMENT '重量段最小值KG(含)', weight_max decimal(10,3) DEFAULT NULL COMMENT '重量段最大值KG(含)', freight_price decimal(10,4) DEFAULT NULL COMMENT '运费 RMB/KG', @@ -47,9 +50,11 @@ CREATE TABLE IF NOT EXISTS bms_product_country ( create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (detail_id), - UNIQUE KEY uk_product_country_weight_eff (product_code, country_name, weight_min, weight_max, effective_date), + UNIQUE KEY uk_product_country_zone_weight_eff (product_code, country_name, zone_code, weight_min, weight_max, effective_date), KEY idx_product_country_pid (product_id), KEY idx_country_code (country_code), + KEY idx_product_country_zone (zone_id), + KEY idx_product_country_zone_code (country_code, zone_code), KEY idx_weight_match (product_code, country_code, weight_min, weight_max), KEY idx_product_eff (product_code, effective_date, expire_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品国家明细-重量段+运费挂号费+尺寸/尾程/时效'; diff --git a/bms-back/sql/price/product_country_zone_alter.sql b/bms-back/sql/price/product_country_zone_alter.sql new file mode 100644 index 00000000..1d9dfb5f --- /dev/null +++ b/bms-back/sql/price/product_country_zone_alter.sql @@ -0,0 +1,74 @@ +-- Add zone fields to bms_product_country (nullable when no zone) +-- Idempotent: safe to re-run (skip if column/index already exists) +-- charset: utf8mb4 + +SET NAMES utf8mb4; +SET @db := DATABASE(); + +-- zone_id +SET @has := ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND COLUMN_NAME = 'zone_id' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE bms_product_country ADD COLUMN zone_id BIGINT(20) DEFAULT NULL COMMENT ''prc_zone.zone_id'' AFTER country_code', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- zone_code +SET @has := ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND COLUMN_NAME = 'zone_code' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE bms_product_country ADD COLUMN zone_code VARCHAR(50) DEFAULT NULL COMMENT ''zone code e.g. Z1'' AFTER zone_id', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- zone_name +SET @has := ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND COLUMN_NAME = 'zone_name' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE bms_product_country ADD COLUMN zone_name VARCHAR(100) DEFAULT NULL COMMENT ''zone display name'' AFTER zone_code', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- indexes +SET @has := ( + SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND INDEX_NAME = 'idx_product_country_zone' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE bms_product_country ADD INDEX idx_product_country_zone (zone_id)', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @has := ( + SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND INDEX_NAME = 'idx_product_country_zone_code' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE bms_product_country ADD INDEX idx_product_country_zone_code (country_code, zone_code)', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- unique key: drop old, add new +SET @has := ( + SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND INDEX_NAME = 'uk_product_country_weight_eff' +); +SET @sql := IF(@has > 0, + 'ALTER TABLE bms_product_country DROP INDEX uk_product_country_weight_eff', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @has := ( + SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'bms_product_country' AND INDEX_NAME = 'uk_product_country_zone_weight_eff' +); +SET @sql := IF(@has = 0, + 'ALTER TABLE bms_product_country ADD UNIQUE KEY uk_product_country_zone_weight_eff (product_code, country_name, zone_code, weight_min, weight_max, effective_date)', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/bms-front/src/views/price/basic/product/components/ProductCountryDrawer.vue b/bms-front/src/views/price/basic/product/components/ProductCountryDrawer.vue index 8f84e285..20042dd3 100644 --- a/bms-front/src/views/price/basic/product/components/ProductCountryDrawer.vue +++ b/bms-front/src/views/price/basic/product/components/ProductCountryDrawer.vue @@ -1,7 +1,7 @@