Compare commits
2 Commits
1090d838a6
...
7807534e5d
| Author | SHA1 | Date | |
|---|---|---|---|
| 7807534e5d | |||
| e415843b14 |
+3
-2
@@ -37,13 +37,14 @@ public class PriceLogController extends BaseController
|
|||||||
@RequestParam(value = "priceKind", required = false) String priceKind,
|
@RequestParam(value = "priceKind", required = false) String priceKind,
|
||||||
@RequestParam(value = "productCode", required = false) String productCode,
|
@RequestParam(value = "productCode", required = false) String productCode,
|
||||||
@RequestParam(value = "updateTime", required = false) String updateTime,
|
@RequestParam(value = "updateTime", required = false) String updateTime,
|
||||||
@RequestParam(value = "updateTimePrefix", required = false) String updateTimePrefix)
|
@RequestParam(value = "updateTimePrefix", required = false) String updateTimePrefix,
|
||||||
|
@RequestParam(value = "countryKeyword", required = false) String countryKeyword)
|
||||||
{
|
{
|
||||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||||
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
|
int pageNum = pageDomain.getPageNum() == null ? 1 : pageDomain.getPageNum().intValue();
|
||||||
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
|
int pageSize = pageDomain.getPageSize() == null ? 10 : pageDomain.getPageSize().intValue();
|
||||||
BillSearchPage page = billSearchStore.search(EsLogType.PRICE_CHANGE, bizId, status, psvId, priceKind,
|
BillSearchPage page = billSearchStore.search(EsLogType.PRICE_CHANGE, bizId, status, psvId, priceKind,
|
||||||
productCode, updateTime, updateTimePrefix, pageNum, pageSize);
|
productCode, updateTime, updateTimePrefix, countryKeyword, pageNum, pageSize);
|
||||||
TableDataInfo rsp = new TableDataInfo();
|
TableDataInfo rsp = new TableDataInfo();
|
||||||
rsp.setCode(HttpStatus.SUCCESS);
|
rsp.setCode(HttpStatus.SUCCESS);
|
||||||
rsp.setMsg("查询成功");
|
rsp.setMsg("查询成功");
|
||||||
|
|||||||
+5
@@ -450,6 +450,11 @@ public class BmsCustomerLogServiceImpl implements IBmsCustomerLogService
|
|||||||
realChanges.add(c);
|
realChanges.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Rebind/update with no field diff: skip empty UPDATE noise (full package save upsert)
|
||||||
|
if (existing != null && realChanges.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
saveLog(bind.getCustomerId(), customer == null ? null : customer.getCustomerCode(),
|
saveLog(bind.getCustomerId(), customer == null ? null : customer.getCustomerCode(),
|
||||||
customer == null ? null : customer.getCustomerName(),
|
customer == null ? null : customer.getCustomerName(),
|
||||||
BmsCustomerLog.MODULE_PRICE_SET, newSet.getPriceSetName(),
|
BmsCustomerLog.MODULE_PRICE_SET, newSet.getPriceSetName(),
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ public interface PrcPriceSetDetailMapper
|
|||||||
|
|
||||||
public int countByPsvId(Long psvId);
|
public int countByPsvId(Long psvId);
|
||||||
|
|
||||||
|
/** Count detail rows including soft-deleted (for deleted-version tab). */
|
||||||
|
public int countByPsvIdAll(Long psvId);
|
||||||
|
|
||||||
public int copyDetailsFromVersion(@Param("srcPsvId") Long srcPsvId, @Param("newPsvId") Long newPsvId);
|
public int copyDetailsFromVersion(@Param("srcPsvId") Long srcPsvId, @Param("newPsvId") Long newPsvId);
|
||||||
|
|
||||||
/** Copy version effective time, expire time and status onto catalog detail rows. */
|
/** Copy version effective time, expire time and status onto catalog detail rows. */
|
||||||
|
|||||||
@@ -173,6 +173,15 @@ public class EsBillSearchStore implements IBillSearchStore
|
|||||||
@Override
|
@Override
|
||||||
public BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind,
|
public BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind,
|
||||||
String productCode, String updateTime, String updateTimePrefix, int pageNum, int pageSize)
|
String productCode, String updateTime, String updateTimePrefix, int pageNum, int pageSize)
|
||||||
|
{
|
||||||
|
return search(logType, bizId, status, psvId, priceKind, productCode, updateTime, updateTimePrefix,
|
||||||
|
null, pageNum, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind,
|
||||||
|
String productCode, String updateTime, String updateTimePrefix, String countryKeyword,
|
||||||
|
int pageNum, int pageSize)
|
||||||
{
|
{
|
||||||
BillSearchPage page = new BillSearchPage();
|
BillSearchPage page = new BillSearchPage();
|
||||||
if (pageNum < 1)
|
if (pageNum < 1)
|
||||||
@@ -197,7 +206,7 @@ public class EsBillSearchStore implements IBillSearchStore
|
|||||||
sort.add(sortItem);
|
sort.add(sortItem);
|
||||||
body.put("sort", sort);
|
body.put("sort", sort);
|
||||||
body.put("query", buildQuery(logType, bizId, status, psvId, priceKind, productCode, updateTime,
|
body.put("query", buildQuery(logType, bizId, status, psvId, priceKind, productCode, updateTime,
|
||||||
updateTimePrefix));
|
updateTimePrefix, countryKeyword));
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
HttpEntity<String> entity = new HttpEntity<String>(body.toJSONString(), headers);
|
HttpEntity<String> entity = new HttpEntity<String>(body.toJSONString(), headers);
|
||||||
@@ -378,7 +387,7 @@ public class EsBillSearchStore implements IBillSearchStore
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JSONObject buildQuery(String logType, String bizId, String status, Long psvId, String priceKind,
|
private JSONObject buildQuery(String logType, String bizId, String status, Long psvId, String priceKind,
|
||||||
String productCode, String updateTime, String updateTimePrefix)
|
String productCode, String updateTime, String updateTimePrefix, String countryKeyword)
|
||||||
{
|
{
|
||||||
JSONArray filters = new JSONArray();
|
JSONArray filters = new JSONArray();
|
||||||
addTerm(filters, "logType", logType);
|
addTerm(filters, "logType", logType);
|
||||||
@@ -387,6 +396,7 @@ public class EsBillSearchStore implements IBillSearchStore
|
|||||||
addPsvIdFilter(filters, psvId);
|
addPsvIdFilter(filters, psvId);
|
||||||
addTerm(filters, "request.priceKind", priceKind);
|
addTerm(filters, "request.priceKind", priceKind);
|
||||||
addProductCodeFilter(filters, productCode);
|
addProductCodeFilter(filters, productCode);
|
||||||
|
addCountryKeywordFilter(filters, countryKeyword);
|
||||||
addUpdateTimeFilter(filters, updateTime);
|
addUpdateTimeFilter(filters, updateTime);
|
||||||
addUpdateTimePrefixFilter(filters, updateTimePrefix);
|
addUpdateTimePrefixFilter(filters, updateTimePrefix);
|
||||||
if (filters.isEmpty())
|
if (filters.isEmpty())
|
||||||
@@ -430,6 +440,65 @@ public class EsBillSearchStore implements IBillSearchStore
|
|||||||
filters.add(boolWrap);
|
filters.add(boolWrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Match country code / name (exact or contains). */
|
||||||
|
private void addCountryKeywordFilter(JSONArray filters, String countryKeyword)
|
||||||
|
{
|
||||||
|
if (StringUtils.isEmpty(countryKeyword))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String kw = countryKeyword.trim();
|
||||||
|
String wild = "*" + escapeWildcard(kw) + "*";
|
||||||
|
JSONArray should = new JSONArray();
|
||||||
|
addShouldTerm(should, "request.bizKeys.countryCode.keyword", kw);
|
||||||
|
addShouldTerm(should, "request.bizKeys.countryCode", kw);
|
||||||
|
addShouldWildcard(should, "request.bizKeys.countryCode.keyword", wild);
|
||||||
|
addShouldWildcard(should, "request.bizKeys.countryCode", wild);
|
||||||
|
addShouldWildcard(should, "request.bizKeys.countryName.keyword", wild);
|
||||||
|
addShouldWildcard(should, "request.bizKeys.countryName", wild);
|
||||||
|
JSONObject matchName = new JSONObject();
|
||||||
|
JSONObject matchField = new JSONObject();
|
||||||
|
matchField.put("query", kw);
|
||||||
|
matchName.put("request.bizKeys.countryName", matchField);
|
||||||
|
JSONObject matchWrap = new JSONObject();
|
||||||
|
matchWrap.put("match", matchName);
|
||||||
|
should.add(matchWrap);
|
||||||
|
|
||||||
|
JSONObject inner = new JSONObject();
|
||||||
|
inner.put("should", should);
|
||||||
|
inner.put("minimum_should_match", Integer.valueOf(1));
|
||||||
|
JSONObject boolWrap = new JSONObject();
|
||||||
|
boolWrap.put("bool", inner);
|
||||||
|
filters.add(boolWrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addShouldTerm(JSONArray should, String field, String value)
|
||||||
|
{
|
||||||
|
JSONObject term = new JSONObject();
|
||||||
|
term.put(field, value);
|
||||||
|
JSONObject wrap = new JSONObject();
|
||||||
|
wrap.put("term", term);
|
||||||
|
should.add(wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addShouldWildcard(JSONArray should, String field, String value)
|
||||||
|
{
|
||||||
|
JSONObject wild = new JSONObject();
|
||||||
|
wild.put(field, value);
|
||||||
|
JSONObject wrap = new JSONObject();
|
||||||
|
wrap.put("wildcard", wild);
|
||||||
|
should.add(wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String escapeWildcard(String raw)
|
||||||
|
{
|
||||||
|
if (raw == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return raw.replace("\\", "\\\\").replace("*", "\\*").replace("?", "\\?");
|
||||||
|
}
|
||||||
|
|
||||||
private void addUpdateTimeFilter(JSONArray filters, String updateTime)
|
private void addUpdateTimeFilter(JSONArray filters, String updateTime)
|
||||||
{
|
{
|
||||||
if (StringUtils.isEmpty(updateTime))
|
if (StringUtils.isEmpty(updateTime))
|
||||||
|
|||||||
@@ -38,6 +38,17 @@ public interface IBillSearchStore
|
|||||||
|
|
||||||
default BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind,
|
default BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind,
|
||||||
String productCode, String updateTime, String updateTimePrefix, int pageNum, int pageSize)
|
String productCode, String updateTime, String updateTimePrefix, int pageNum, int pageSize)
|
||||||
|
{
|
||||||
|
return search(logType, bizId, status, psvId, priceKind, productCode, updateTime, updateTimePrefix,
|
||||||
|
null, pageNum, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search with optional countryKeyword (matches bizKeys.countryCode / countryName).
|
||||||
|
*/
|
||||||
|
default BillSearchPage search(String logType, String bizId, String status, Long psvId, String priceKind,
|
||||||
|
String productCode, String updateTime, String updateTimePrefix, String countryKeyword,
|
||||||
|
int pageNum, int pageSize)
|
||||||
{
|
{
|
||||||
return search(logType, bizId, status, pageNum, pageSize);
|
return search(logType, bizId, status, pageNum, pageSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,11 +152,31 @@ public final class PriceChangeEsLog
|
|||||||
keys.put("countryCode", d.getCountryCode());
|
keys.put("countryCode", d.getCountryCode());
|
||||||
keys.put("countryName", d.getCountryName());
|
keys.put("countryName", d.getCountryName());
|
||||||
keys.put("zoneCode", d.getZoneCode());
|
keys.put("zoneCode", d.getZoneCode());
|
||||||
|
keys.put("zoneName", d.getZoneName());
|
||||||
keys.put("weightMin", d.getWeightMin());
|
keys.put("weightMin", d.getWeightMin());
|
||||||
keys.put("weightMax", d.getWeightMax());
|
keys.put("weightMax", d.getWeightMax());
|
||||||
|
keys.put("weightLabel", weightBandOf(d));
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Display weight band for sales detail logs (e.g. 0<W≤0.5). */
|
||||||
|
public static String weightBandOf(PrcPriceSetDetail d)
|
||||||
|
{
|
||||||
|
if (d == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(d.getWeightLabel()))
|
||||||
|
{
|
||||||
|
return d.getWeightLabel();
|
||||||
|
}
|
||||||
|
if (d.getWeightMin() == null && d.getWeightMax() == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return stringify(d.getWeightMin()) + "<W\u2264" + stringify(d.getWeightMax());
|
||||||
|
}
|
||||||
|
|
||||||
public static void save(IBillSearchStore store, String bizId, String priceKind, String action,
|
public static void save(IBillSearchStore store, String bizId, String priceKind, String action,
|
||||||
Map<String, Object> bizKeys, List<Map<String, String>> changes)
|
Map<String, Object> bizKeys, List<Map<String, String>> changes)
|
||||||
{
|
{
|
||||||
|
|||||||
+165
-4
@@ -53,6 +53,11 @@ import com.bms.price.search.PriceChangeEsLog;
|
|||||||
import com.bms.price.service.IPrcPriceExcelFileService;
|
import com.bms.price.service.IPrcPriceExcelFileService;
|
||||||
import com.bms.price.service.IPrcPriceSetService;
|
import com.bms.price.service.IPrcPriceSetService;
|
||||||
import com.bms.price.task.PrcPriceSetSaveAsyncRunner;
|
import com.bms.price.task.PrcPriceSetSaveAsyncRunner;
|
||||||
|
import com.bms.customer.customerinfo.domain.BmsCustomer;
|
||||||
|
import com.bms.customer.customerinfo.domain.BmsCustomerPriceSet;
|
||||||
|
import com.bms.customer.customerinfo.domain.BmsPriceSetOption;
|
||||||
|
import com.bms.customer.customerinfo.service.IBmsCustomerLogService;
|
||||||
|
import com.bms.customer.customerinfo.service.IBmsCustomerService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 价格套餐 服务层实现
|
* 价格套餐 服务层实现
|
||||||
@@ -130,6 +135,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
@Autowired
|
@Autowired
|
||||||
private IBillSearchStore billSearchStore;
|
private IBillSearchStore billSearchStore;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IBmsCustomerLogService customerLogService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IBmsCustomerService bmsCustomerService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PrcPriceSet selectPrcPriceSetById(Long priceSetId)
|
public PrcPriceSet selectPrcPriceSetById(Long priceSetId)
|
||||||
{
|
{
|
||||||
@@ -2511,7 +2522,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return customerMapper.insertPrcCustomerPriceSet(customer);
|
int rows = customerMapper.insertPrcCustomerPriceSet(customer);
|
||||||
|
if (rows > 0)
|
||||||
|
{
|
||||||
|
logCustomerPriceSetBind(null, customer, db);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -2610,6 +2626,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
}
|
}
|
||||||
validateCustomer(row, db);
|
validateCustomer(row, db);
|
||||||
customerMapper.updatePrcCustomerPriceSet(row);
|
customerMapper.updatePrcCustomerPriceSet(row);
|
||||||
|
logCustomerPriceSetBind(existing, row, db);
|
||||||
replaced++;
|
replaced++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2630,6 +2647,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
logCustomerPriceSetBind(null, row, db);
|
||||||
inserted++;
|
inserted++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2657,12 +2675,30 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
assertChildEditable(db);
|
assertChildEditable(db);
|
||||||
customer.setPriceSetId(old.getPriceSetId());
|
customer.setPriceSetId(old.getPriceSetId());
|
||||||
validateCustomer(customer, db);
|
validateCustomer(customer, db);
|
||||||
return customerMapper.updatePrcCustomerPriceSet(customer);
|
int rows = customerMapper.updatePrcCustomerPriceSet(customer);
|
||||||
|
if (rows > 0)
|
||||||
|
{
|
||||||
|
if (customer.getCustomerId() == null)
|
||||||
|
{
|
||||||
|
customer.setCustomerId(old.getCustomerId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(customer.getCustomerCode()))
|
||||||
|
{
|
||||||
|
customer.setCustomerCode(old.getCustomerCode());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(customer.getCustomerName()))
|
||||||
|
{
|
||||||
|
customer.setCustomerName(old.getCustomerName());
|
||||||
|
}
|
||||||
|
logCustomerPriceSetBind(old, customer, db);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int deleteCustomerByIds(Long[] customerPricesetIds)
|
public int deleteCustomerByIds(Long[] customerPricesetIds)
|
||||||
{
|
{
|
||||||
|
List<PrcCustomerPriceSet> snapshots = new ArrayList<PrcCustomerPriceSet>();
|
||||||
for (Long id : customerPricesetIds)
|
for (Long id : customerPricesetIds)
|
||||||
{
|
{
|
||||||
PrcCustomerPriceSet old = customerMapper.selectPrcCustomerPriceSetById(id);
|
PrcCustomerPriceSet old = customerMapper.selectPrcCustomerPriceSetById(id);
|
||||||
@@ -2675,8 +2711,17 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
throw new ServiceException(MessageUtils.message("price.set.customer.unbindAudit"));
|
throw new ServiceException(MessageUtils.message("price.set.customer.unbindAudit"));
|
||||||
}
|
}
|
||||||
|
snapshots.add(old);
|
||||||
}
|
}
|
||||||
return customerMapper.deletePrcCustomerPriceSetByIds(customerPricesetIds);
|
int rows = customerMapper.deletePrcCustomerPriceSetByIds(customerPricesetIds);
|
||||||
|
if (rows > 0)
|
||||||
|
{
|
||||||
|
for (PrcCustomerPriceSet snapshot : snapshots)
|
||||||
|
{
|
||||||
|
logCustomerPriceSetUnbind(snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void replaceChildren(PrcPriceSet set)
|
private void replaceChildren(PrcPriceSet set)
|
||||||
@@ -2747,6 +2792,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
c.setUpdateBy(set.getUpdateBy());
|
c.setUpdateBy(set.getUpdateBy());
|
||||||
customerMapper.updatePrcCustomerPriceSet(c);
|
customerMapper.updatePrcCustomerPriceSet(c);
|
||||||
keepIds.add(hit.getCustomerPricesetId());
|
keepIds.add(hit.getCustomerPricesetId());
|
||||||
|
logCustomerPriceSetBind(hit, c, set);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2757,6 +2803,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
keepIds.add(c.getCustomerPricesetId());
|
keepIds.add(c.getCustomerPricesetId());
|
||||||
}
|
}
|
||||||
|
logCustomerPriceSetBind(null, c, set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -2778,10 +2825,99 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
}
|
}
|
||||||
if (!orphanIds.isEmpty())
|
if (!orphanIds.isEmpty())
|
||||||
{
|
{
|
||||||
|
for (Long orphanId : orphanIds)
|
||||||
|
{
|
||||||
|
PrcCustomerPriceSet snapshot = byId.get(orphanId);
|
||||||
|
if (snapshot != null)
|
||||||
|
{
|
||||||
|
logCustomerPriceSetUnbind(snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
customerMapper.deletePrcCustomerPriceSetByIds(orphanIds.toArray(new Long[0]));
|
customerMapper.deletePrcCustomerPriceSetByIds(orphanIds.toArray(new Long[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Write customer biz log for package bind / rebind (visible in customer info Logs). */
|
||||||
|
private void logCustomerPriceSetBind(PrcCustomerPriceSet existing, PrcCustomerPriceSet bind, PrcPriceSet set)
|
||||||
|
{
|
||||||
|
if (customerLogService == null || bind == null || set == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BmsCustomer customer = resolveCustomerForLog(bind);
|
||||||
|
if (customer == null && bind.getCustomerId() != null)
|
||||||
|
{
|
||||||
|
customer = new BmsCustomer();
|
||||||
|
customer.setCustomerId(bind.getCustomerId());
|
||||||
|
customer.setCustomerCode(bind.getCustomerCode());
|
||||||
|
customer.setCustomerName(bind.getCustomerName());
|
||||||
|
}
|
||||||
|
BmsCustomerPriceSet existingLog = toBmsCustomerPriceSet(existing, resolvePriceSetName(existing));
|
||||||
|
BmsCustomerPriceSet bindLog = toBmsCustomerPriceSet(bind, set.getPriceSetName());
|
||||||
|
if (bindLog.getCustomerId() == null && customer != null)
|
||||||
|
{
|
||||||
|
bindLog.setCustomerId(customer.getCustomerId());
|
||||||
|
}
|
||||||
|
BmsPriceSetOption option = new BmsPriceSetOption();
|
||||||
|
option.setPriceSetId(set.getPriceSetId());
|
||||||
|
option.setPriceSetCode(set.getPriceSetCode());
|
||||||
|
option.setPriceSetName(set.getPriceSetName());
|
||||||
|
customerLogService.recordPriceSetBind(customer, existingLog, bindLog, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logCustomerPriceSetUnbind(PrcCustomerPriceSet bind)
|
||||||
|
{
|
||||||
|
if (customerLogService == null || bind == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BmsCustomerPriceSet snapshot = toBmsCustomerPriceSet(bind, resolvePriceSetName(bind));
|
||||||
|
customerLogService.recordPriceSetUnbind(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BmsCustomer resolveCustomerForLog(PrcCustomerPriceSet bind)
|
||||||
|
{
|
||||||
|
if (bind == null || bmsCustomerService == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (bind.getCustomerId() != null)
|
||||||
|
{
|
||||||
|
return bmsCustomerService.selectBmsCustomerByCustomerId(bind.getCustomerId());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolvePriceSetName(PrcCustomerPriceSet bind)
|
||||||
|
{
|
||||||
|
if (bind == null || bind.getPriceSetId() == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PrcPriceSet set = setMapper.selectPrcPriceSetById(bind.getPriceSetId());
|
||||||
|
return set == null ? null : set.getPriceSetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private BmsCustomerPriceSet toBmsCustomerPriceSet(PrcCustomerPriceSet src, String priceSetName)
|
||||||
|
{
|
||||||
|
if (src == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BmsCustomerPriceSet dest = new BmsCustomerPriceSet();
|
||||||
|
dest.setCustomerPricesetId(src.getCustomerPricesetId());
|
||||||
|
dest.setPriceSetId(src.getPriceSetId());
|
||||||
|
dest.setCustomerId(src.getCustomerId());
|
||||||
|
dest.setCustomerCode(src.getCustomerCode());
|
||||||
|
dest.setCustomerName(src.getCustomerName());
|
||||||
|
dest.setBindPriority(src.getBindPriority());
|
||||||
|
dest.setPrBegindate(src.getPrBegindate());
|
||||||
|
dest.setPrEnddate(src.getPrEnddate());
|
||||||
|
dest.setPriceSetNote(src.getPriceSetNote());
|
||||||
|
dest.setPriceSetName(priceSetName);
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
private void reportSaveProgress(PrcPriceSet set, int percent, String message)
|
private void reportSaveProgress(PrcPriceSet set, int percent, String message)
|
||||||
{
|
{
|
||||||
if (set == null || StringUtils.isEmpty(set.getSaveTaskId()) || saveAsyncRunner == null)
|
if (set == null || StringUtils.isEmpty(set.getSaveTaskId()) || saveAsyncRunner == null)
|
||||||
@@ -3482,6 +3618,22 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
fillVersionChildren(version);
|
fillVersionChildren(version);
|
||||||
}
|
}
|
||||||
|
// Soft-deleted EFFECT versions for the "deleted" tab (status may still be EFFECT)
|
||||||
|
PrcPriceSetVersion dq = new PrcPriceSetVersion();
|
||||||
|
dq.setPriceSetId(priceSetId);
|
||||||
|
dq.setPriceType(com.bms.price.domain.PriceType.SALES);
|
||||||
|
dq.setIsDel("1");
|
||||||
|
List<PrcPriceSetVersion> deleted = versionMapper.selectPrcPriceSetVersionList(dq);
|
||||||
|
if (deleted != null)
|
||||||
|
{
|
||||||
|
for (PrcPriceSetVersion version : deleted)
|
||||||
|
{
|
||||||
|
version.setProducts(new ArrayList<PrcPriceSetProduct>());
|
||||||
|
version.setDetails(new ArrayList<PrcPriceSetDetail>());
|
||||||
|
version.setDetailCount(Integer.valueOf(detailMapper.countByPsvIdAll(version.getPsvId())));
|
||||||
|
versions.add(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
set.setVersions(versions);
|
set.setVersions(versions);
|
||||||
PrcPriceSetVersion current = pickCurrentVersion(versions);
|
PrcPriceSetVersion current = pickCurrentVersion(versions);
|
||||||
set.setVersion(current);
|
set.setVersion(current);
|
||||||
@@ -3508,14 +3660,23 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
PrcPriceSetVersion firstAlive = null;
|
||||||
for (PrcPriceSetVersion v : versions)
|
for (PrcPriceSetVersion v : versions)
|
||||||
{
|
{
|
||||||
|
if (v == null || "1".equals(v.getIsDel()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (firstAlive == null)
|
||||||
|
{
|
||||||
|
firstAlive = v;
|
||||||
|
}
|
||||||
if (VER_EFFECT.equals(v.getStatus()))
|
if (VER_EFFECT.equals(v.getStatus()))
|
||||||
{
|
{
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return versions.get(0);
|
return firstAlive;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveOrUpdateVersion(PrcPriceSet set)
|
private void saveOrUpdateVersion(PrcPriceSet set)
|
||||||
|
|||||||
@@ -158,6 +158,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
select count(1) from prc_price_set_detail where psv_id = #{psvId} and ifnull(is_del, '0') = '0'
|
select count(1) from prc_price_set_detail where psv_id = #{psvId} and ifnull(is_del, '0') = '0'
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="countByPsvIdAll" parameterType="Long" resultType="int">
|
||||||
|
select count(1) from prc_price_set_detail where psv_id = #{psvId}
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="selectDetailOptionRows" resultMap="PrcPriceSetDetailResult">
|
<select id="selectDetailOptionRows" resultMap="PrcPriceSetDetailResult">
|
||||||
select distinct product_code, product_name, country_code, country_name, zone_code, zone_name
|
select distinct product_code, product_name, country_code, country_name, zone_code, zone_name
|
||||||
from prc_price_set_detail
|
from prc_price_set_detail
|
||||||
|
|||||||
@@ -37,7 +37,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<select id="selectPrcPriceSetVersionList" parameterType="PrcPriceSetVersion" resultMap="PrcPriceSetVersionResult">
|
<select id="selectPrcPriceSetVersionList" parameterType="PrcPriceSetVersion" resultMap="PrcPriceSetVersionResult">
|
||||||
<include refid="selectPrcPriceSetVersionVo"/>
|
<include refid="selectPrcPriceSetVersionVo"/>
|
||||||
<where>
|
<where>
|
||||||
and ifnull(is_del, '0') = '0'
|
<choose>
|
||||||
|
<when test="isDel != null and isDel != ''">
|
||||||
|
and ifnull(is_del, '0') = #{isDel}
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
and ifnull(is_del, '0') = '0'
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
<if test="priceSetId != null">
|
<if test="priceSetId != null">
|
||||||
AND price_set_id = #{priceSetId}
|
AND price_set_id = #{priceSetId}
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Price change log under system monitor
|
-- Price change log: remove monitor page menu; keep query permission for package version change log
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
SET @monitorParent := (
|
SET @monitorParent := (
|
||||||
@@ -8,29 +8,36 @@ SET @monitorParent := (
|
|||||||
LIMIT 1
|
LIMIT 1
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO sys_menu (
|
-- Drop legacy monitor page menu
|
||||||
menu_id, menu_name, parent_id, order_num, path, component, query, route_name,
|
DELETE FROM sys_role_menu WHERE menu_id = 2350;
|
||||||
is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark
|
DELETE FROM sys_menu WHERE menu_id = 2350
|
||||||
)
|
OR (menu_type = 'C' AND (path = 'priceLog' OR component = 'monitor/priceLog/index'));
|
||||||
SELECT 2350, N'价格日志', IFNULL(@monitorParent, 2), 10,
|
|
||||||
'priceLog', 'monitor/priceLog/index', '', 'PriceLog',
|
-- Normalize existing permission row to button (F), no page component
|
||||||
1, 0, 'C', '0', '0', 'monitor:priceLog:list', 'log', 'admin', sysdate(),
|
UPDATE sys_menu
|
||||||
N'销售价/产品价/红线价改前改后(ES)'
|
SET parent_id = IFNULL(@monitorParent, parent_id),
|
||||||
FROM DUAL
|
menu_type = 'F',
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2350 OR perms = 'monitor:priceLog:list');
|
path = '',
|
||||||
|
component = NULL,
|
||||||
|
visible = '0',
|
||||||
|
status = '0',
|
||||||
|
menu_name = N'价格变更日志查询',
|
||||||
|
remark = N'套餐版本变更记录等查询权限(无监控页面)'
|
||||||
|
WHERE menu_id = 2351
|
||||||
|
OR perms = 'monitor:priceLog:list';
|
||||||
|
|
||||||
INSERT INTO sys_menu (
|
INSERT INTO sys_menu (
|
||||||
menu_id, menu_name, parent_id, order_num, path, component,
|
menu_id, menu_name, parent_id, order_num, path, component,
|
||||||
is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time
|
is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark
|
||||||
)
|
)
|
||||||
SELECT 2351, N'查询', 2350, 1, '', NULL,
|
SELECT 2351, N'价格变更日志查询', IFNULL(@monitorParent, 2), 10, '', NULL,
|
||||||
1, 0, 'F', '0', '0', 'monitor:priceLog:list', '#', 'admin', sysdate()
|
1, 0, 'F', '0', '0', 'monitor:priceLog:list', '#', 'admin', sysdate(),
|
||||||
|
N'套餐版本变更记录等查询权限(无监控页面)'
|
||||||
FROM DUAL
|
FROM DUAL
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2351);
|
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2351 OR perms = 'monitor:priceLog:list');
|
||||||
|
|
||||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
SELECT 1, m.menu_id
|
SELECT 1, m.menu_id
|
||||||
FROM (SELECT 2350 AS menu_id UNION SELECT 2351) m
|
FROM sys_menu m
|
||||||
WHERE NOT EXISTS (
|
WHERE m.perms = 'monitor:priceLog:list'
|
||||||
SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id
|
AND NOT EXISTS (SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id);
|
||||||
);
|
|
||||||
|
|||||||
@@ -1,265 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<basic-table
|
|
||||||
ref="tableRef"
|
|
||||||
:columns="columns"
|
|
||||||
:search-columns="searchColumns"
|
|
||||||
:query="queryList"
|
|
||||||
row-key="docId"
|
|
||||||
>
|
|
||||||
<template #col-priceKind="{ row }">
|
|
||||||
<el-tag size="small" :type="kindTag(row)">{{ kindLabel(row) }}</el-tag>
|
|
||||||
</template>
|
|
||||||
<template #col-actionName="{ row }">
|
|
||||||
{{ actionLabel(row) }}
|
|
||||||
</template>
|
|
||||||
<template #col-operator="{ row }">
|
|
||||||
{{ operatorOf(row) }}
|
|
||||||
</template>
|
|
||||||
<template #col-productCode="{ row }">
|
|
||||||
{{ productCodeOf(row) }}
|
|
||||||
</template>
|
|
||||||
<template #col-status="{ row }">
|
|
||||||
<el-tag size="small" :type="statusTag(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
|
||||||
</template>
|
|
||||||
<template #col-action="{ row }">
|
|
||||||
<el-button link type="primary" icon="View" @click="handleView(row)" v-hasPermi="['monitor:priceLog:list']">查看</el-button>
|
|
||||||
</template>
|
|
||||||
</basic-table>
|
|
||||||
|
|
||||||
<el-drawer v-model="open" size="760px" :title="drawerTitle" append-to-body destroy-on-close>
|
|
||||||
<div v-loading="loading">
|
|
||||||
<el-empty v-if="!detail" description="暂无详情" />
|
|
||||||
<div v-else>
|
|
||||||
<el-descriptions :column="2" border size="small" class="mb12">
|
|
||||||
<el-descriptions-item label="价格类型">
|
|
||||||
<el-tag size="small" :type="kindTag(detail)">{{ kindLabel(detail) }}</el-tag>
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="操作">{{ actionLabel(detail) }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="操作人">{{ operatorOf(detail) }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="状态">
|
|
||||||
<el-tag size="small" :type="statusTag(detail.status)">{{ statusLabel(detail.status) }}</el-tag>
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="业务ID">{{ detail.bizId || '-' }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="写入时间">{{ detail.updateTime || '-' }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="产品代码">{{ productCodeOf(detail) }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="国家">{{ countryOf(detail) }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item v-if="detail.errorMessage" label="错误信息" :span="2">
|
|
||||||
<span class="err-text">{{ detail.errorMessage }}</span>
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
|
|
||||||
<div class="change-panel">
|
|
||||||
<div class="change-panel__head">改前改后</div>
|
|
||||||
<el-table :data="changesOf(detail)" border size="small" empty-text="无字段变更">
|
|
||||||
<el-table-column prop="fieldLabel" label="字段" min-width="120" />
|
|
||||||
<el-table-column prop="oldValue" label="改前" min-width="140" show-overflow-tooltip />
|
|
||||||
<el-table-column prop="newValue" label="改后" min-width="140" show-overflow-tooltip />
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="json-panel">
|
|
||||||
<div class="json-panel__head">
|
|
||||||
<span>请求报文</span>
|
|
||||||
<el-button size="small" icon="CopyDocument" @click="copyJson(detail.requestJson)">复制</el-button>
|
|
||||||
</div>
|
|
||||||
<pre class="code-pre">{{ formatJson(detail.requestJson) }}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-drawer>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup name="PriceLog">
|
|
||||||
import { listPriceLog, getPriceLog } from '@/api/monitor/priceLog'
|
|
||||||
|
|
||||||
const tableRef = ref()
|
|
||||||
const open = ref(false)
|
|
||||||
const loading = ref(false)
|
|
||||||
const detail = ref(null)
|
|
||||||
const drawerTitle = ref('价格日志详情')
|
|
||||||
|
|
||||||
const kindOptions = [
|
|
||||||
{ label: '销售价', value: 'SALES' },
|
|
||||||
{ label: '产品价', value: 'PRODUCT' },
|
|
||||||
{ label: '红线价', value: 'REDLINE' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const actionOptions = [
|
|
||||||
{ label: '新增', value: 'INSERT' },
|
|
||||||
{ label: '修改', value: 'UPDATE' },
|
|
||||||
{ label: '删除', value: 'DELETE' },
|
|
||||||
{ label: '导入', value: 'IMPORT' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const statusOptions = [
|
|
||||||
{ label: '成功', value: 'SUCCESS' },
|
|
||||||
{ label: '失败', value: 'FAIL' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const searchColumns = [
|
|
||||||
{ label: '状态', prop: 'status', component: 'select', options: statusOptions, placeholder: '请选择状态', width: '140px', props: { clearable: true } },
|
|
||||||
{ label: '业务ID', prop: 'bizId', component: 'input', placeholder: 'SALES_/PRODUCT_/REDLINE_', width: '240px' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ label: '价格类型', prop: 'priceKind', align: 'center', width: 100, slot: 'priceKind' },
|
|
||||||
{ label: '操作', prop: 'actionName', align: 'center', width: 90, slot: 'actionName' },
|
|
||||||
{ label: '操作人', prop: 'operator', width: 110, slot: 'operator', showOverflowTooltip: true },
|
|
||||||
{ label: '产品代码', prop: 'productCode', minWidth: 120, slot: 'productCode', showOverflowTooltip: true },
|
|
||||||
{ label: '状态', prop: 'status', align: 'center', width: 90, slot: 'status' },
|
|
||||||
{ label: '业务ID', prop: 'bizId', minWidth: 160, showOverflowTooltip: true },
|
|
||||||
{ label: '写入时间', prop: 'updateTime', align: 'center', width: 180 },
|
|
||||||
{ label: '操作', prop: 'action', align: 'center', width: 90, fixed: 'right', slot: 'action', showInToolbar: false }
|
|
||||||
]
|
|
||||||
|
|
||||||
function queryList(params) {
|
|
||||||
return listPriceLog(params)
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseReq(row) {
|
|
||||||
if (!row || !row.requestJson) return {}
|
|
||||||
try {
|
|
||||||
return typeof row.requestJson === 'object' ? row.requestJson : JSON.parse(row.requestJson)
|
|
||||||
} catch (e) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function kindOf(row) {
|
|
||||||
const req = parseReq(row)
|
|
||||||
return req.priceKind || ''
|
|
||||||
}
|
|
||||||
|
|
||||||
function kindLabel(row) {
|
|
||||||
const v = kindOf(row)
|
|
||||||
const found = kindOptions.find(i => i.value === v)
|
|
||||||
return found ? found.label : (v || '-')
|
|
||||||
}
|
|
||||||
|
|
||||||
function kindTag(row) {
|
|
||||||
const v = kindOf(row)
|
|
||||||
if (v === 'SALES') return 'success'
|
|
||||||
if (v === 'PRODUCT') return 'warning'
|
|
||||||
if (v === 'REDLINE') return 'danger'
|
|
||||||
return 'info'
|
|
||||||
}
|
|
||||||
|
|
||||||
function actionLabel(row) {
|
|
||||||
const req = parseReq(row)
|
|
||||||
const found = actionOptions.find(i => i.value === req.action)
|
|
||||||
return found ? found.label : (req.action || '-')
|
|
||||||
}
|
|
||||||
|
|
||||||
function operatorOf(row) {
|
|
||||||
return parseReq(row).operator || '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
function productCodeOf(row) {
|
|
||||||
const keys = parseReq(row).bizKeys || {}
|
|
||||||
return keys.productCode || '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
function countryOf(row) {
|
|
||||||
const keys = parseReq(row).bizKeys || {}
|
|
||||||
if (keys.countryCode && keys.countryName) return keys.countryName + ' (' + keys.countryCode + ')'
|
|
||||||
return keys.countryCode || keys.countryName || '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
function changesOf(row) {
|
|
||||||
const req = parseReq(row)
|
|
||||||
return Array.isArray(req.changes) ? req.changes : []
|
|
||||||
}
|
|
||||||
|
|
||||||
function statusLabel(status) {
|
|
||||||
const found = statusOptions.find(item => item.value === status)
|
|
||||||
if (found) return found.label
|
|
||||||
return status === 'FAIL' ? '失败' : '成功'
|
|
||||||
}
|
|
||||||
|
|
||||||
function statusTag(status) {
|
|
||||||
return status === 'FAIL' ? 'danger' : 'success'
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleView(row) {
|
|
||||||
drawerTitle.value = '价格日志 #' + (row.bizId || row.docId)
|
|
||||||
open.value = true
|
|
||||||
loading.value = true
|
|
||||||
detail.value = null
|
|
||||||
getPriceLog(row.docId).then(res => {
|
|
||||||
detail.value = res.data || row
|
|
||||||
}).finally(() => {
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatJson(val) {
|
|
||||||
if (val === null || val === undefined || val === '') return '无数据'
|
|
||||||
if (typeof val === 'object') {
|
|
||||||
try { return JSON.stringify(val, null, 2) } catch (e) { return String(val) }
|
|
||||||
}
|
|
||||||
try { return JSON.stringify(JSON.parse(val), null, 2) } catch (e) { return String(val) }
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyJson(val) {
|
|
||||||
const text = formatJson(val)
|
|
||||||
const done = () => ElMessage({ message: '已复制', type: 'success', duration: 1500 })
|
|
||||||
if (navigator.clipboard) {
|
|
||||||
navigator.clipboard.writeText(text).then(done)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const ta = document.createElement('textarea')
|
|
||||||
ta.value = text
|
|
||||||
document.body.appendChild(ta)
|
|
||||||
ta.select()
|
|
||||||
document.execCommand('copy')
|
|
||||||
document.body.removeChild(ta)
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.mb12 { margin-bottom: 12px; }
|
|
||||||
.err-text { color: #f56c6c; word-break: break-all; }
|
|
||||||
.change-panel {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
border: 1px solid #e8ecf0;
|
|
||||||
border-radius: 6px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.change-panel__head {
|
|
||||||
padding: 8px 12px;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #606266;
|
|
||||||
background: #fafbfc;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
}
|
|
||||||
.json-panel {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
background: #f7f9fb;
|
|
||||||
border: 1px solid #e8ecf0;
|
|
||||||
border-radius: 6px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.json-panel__head {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 8px 12px;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #606266;
|
|
||||||
background: #fafbfc;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
}
|
|
||||||
.json-panel .code-pre {
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 14px;
|
|
||||||
max-height: 280px;
|
|
||||||
overflow: auto;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -9,9 +9,13 @@
|
|||||||
>
|
>
|
||||||
<div class="price-log-drawer" v-loading="loading">
|
<div class="price-log-drawer" v-loading="loading">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<el-select v-model="query.status" clearable :placeholder="phStatus" style="width: 120px" @change="reload">
|
<el-input
|
||||||
<el-option v-for="o in statusOptions" :key="o.value" :label="o.label" :value="o.value" />
|
v-model="query.productKeyword"
|
||||||
</el-select>
|
clearable
|
||||||
|
:placeholder="phProduct"
|
||||||
|
style="width: 220px"
|
||||||
|
@keyup.enter="reload"
|
||||||
|
/>
|
||||||
<el-button type="primary" @click="reload">查询</el-button>
|
<el-button type="primary" @click="reload">查询</el-button>
|
||||||
<el-button @click="resetQuery">重置</el-button>
|
<el-button @click="resetQuery">重置</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -36,11 +40,6 @@
|
|||||||
<template #default="{ row }">{{ row.operator || '-' }}</template>
|
<template #default="{ row }">{{ row.operator || '-' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="colCount" width="100" align="center" prop="changeCount" />
|
<el-table-column :label="colCount" width="100" align="center" prop="changeCount" />
|
||||||
<el-table-column :label="colStatus" width="90" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-tag size="small" :type="statusTag(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :label="colOps" width="90" align="center" fixed="right">
|
<el-table-column :label="colOps" width="90" align="center" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button link type="primary" @click="openProduct(row)">明细</el-button>
|
<el-button link type="primary" @click="openProduct(row)">明细</el-button>
|
||||||
@@ -50,23 +49,41 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-drawer v-model="groupOpen" size="780px" :title="groupTitle" append-to-body destroy-on-close>
|
<el-drawer v-model="groupOpen" size="860px" :title="groupTitle" append-to-body destroy-on-close>
|
||||||
<div v-loading="groupLoading">
|
<div v-loading="groupLoading">
|
||||||
<el-table :data="groupRows" border height="calc(100vh - 220px)" :empty-text="emptyGroup">
|
<div class="toolbar">
|
||||||
|
<el-input
|
||||||
|
v-model="groupQuery.countryKeyword"
|
||||||
|
clearable
|
||||||
|
:placeholder="phCountry"
|
||||||
|
style="width: 220px"
|
||||||
|
@keyup.enter="searchGroup"
|
||||||
|
/>
|
||||||
|
<el-button type="primary" @click="searchGroup">查询</el-button>
|
||||||
|
<el-button @click="resetGroupQuery">重置</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="displayGroupRows"
|
||||||
|
border
|
||||||
|
height="calc(100vh - 260px)"
|
||||||
|
:empty-text="emptyGroup"
|
||||||
|
:span-method="groupSpanMethod"
|
||||||
|
>
|
||||||
<el-table-column :label="colCountry" min-width="140" show-overflow-tooltip>
|
<el-table-column :label="colCountry" min-width="140" show-overflow-tooltip>
|
||||||
<template #default="{ row }">{{ countryOf(row) }}</template>
|
<template #default="{ row }">{{ countryOf(row) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column :label="colZone" min-width="120" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">{{ zoneOf(row) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="colWeight" min-width="120" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">{{ weightBandOf(row) }}</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column :label="colAction" width="90" align="center">
|
<el-table-column :label="colAction" width="90" align="center">
|
||||||
<template #default="{ row }">{{ actionLabel(row) }}</template>
|
<template #default="{ row }">{{ actionLabel(row) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="colChangeSummary" min-width="220" show-overflow-tooltip>
|
<el-table-column :label="colChangeSummary" min-width="240" show-overflow-tooltip>
|
||||||
<template #default="{ row }">{{ changeSummary(row) }}</template>
|
<template #default="{ row }">{{ changeSummary(row) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="colStatus" width="90" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-tag size="small" :type="statusTag(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :label="colTime" width="170" align="center" prop="updateTime" />
|
<el-table-column :label="colTime" width="170" align="center" prop="updateTime" />
|
||||||
<el-table-column :label="colOps" width="90" align="center" fixed="right">
|
<el-table-column :label="colOps" width="90" align="center" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -101,6 +118,8 @@
|
|||||||
<el-descriptions-item :label="lbTime">{{ detail.updateTime || '-' }}</el-descriptions-item>
|
<el-descriptions-item :label="lbTime">{{ detail.updateTime || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="lbProduct">{{ productCodeOf(detail) }}</el-descriptions-item>
|
<el-descriptions-item :label="lbProduct">{{ productCodeOf(detail) }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="lbCountry">{{ countryOf(detail) }}</el-descriptions-item>
|
<el-descriptions-item :label="lbCountry">{{ countryOf(detail) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="lbWeight">{{ weightBandOf(detail) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="lbZone">{{ zoneOf(detail) }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
<div class="change-panel">
|
<div class="change-panel">
|
||||||
@@ -133,7 +152,7 @@ const visible = computed({
|
|||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const allGroups = ref([])
|
const allGroups = ref([])
|
||||||
const query = reactive({ status: undefined })
|
const query = reactive({ productKeyword: '' })
|
||||||
const activeTime = ref('')
|
const activeTime = ref('')
|
||||||
|
|
||||||
const groupOpen = ref(false)
|
const groupOpen = ref(false)
|
||||||
@@ -142,14 +161,15 @@ const groupRows = ref([])
|
|||||||
const groupTotal = ref(0)
|
const groupTotal = ref(0)
|
||||||
const groupTitle = ref('')
|
const groupTitle = ref('')
|
||||||
const activeProduct = ref(null)
|
const activeProduct = ref(null)
|
||||||
const groupQuery = reactive({ pageNum: 1, pageSize: 20 })
|
const groupQuery = reactive({ pageNum: 1, pageSize: 20, countryKeyword: '' })
|
||||||
|
|
||||||
const detailOpen = ref(false)
|
const detailOpen = ref(false)
|
||||||
const detailLoading = ref(false)
|
const detailLoading = ref(false)
|
||||||
const detail = ref(null)
|
const detail = ref(null)
|
||||||
const detailTitle = ref('')
|
const detailTitle = ref('')
|
||||||
|
|
||||||
const phStatus = '状态'
|
const phProduct = '产品代码'
|
||||||
|
const phCountry = '国家代码/名称'
|
||||||
const emptyText = '该版本暂无价格变更记录'
|
const emptyText = '该版本暂无价格变更记录'
|
||||||
const emptyProducts = '该时间点暂无产品变更'
|
const emptyProducts = '该时间点暂无产品变更'
|
||||||
const emptyGroup = '该产品暂无明细变更'
|
const emptyGroup = '该产品暂无明细变更'
|
||||||
@@ -159,6 +179,8 @@ const colAction = '操作'
|
|||||||
const colOperator = '操作人'
|
const colOperator = '操作人'
|
||||||
const colProduct = '产品代码'
|
const colProduct = '产品代码'
|
||||||
const colCountry = '国家'
|
const colCountry = '国家'
|
||||||
|
const colZone = '分区'
|
||||||
|
const colWeight = '公斤段'
|
||||||
const colCount = '变更条数'
|
const colCount = '变更条数'
|
||||||
const colChangeSummary = '变更摘要'
|
const colChangeSummary = '变更摘要'
|
||||||
const colStatus = '状态'
|
const colStatus = '状态'
|
||||||
@@ -173,6 +195,8 @@ const lbStatus = '状态'
|
|||||||
const lbTime = '写入时间'
|
const lbTime = '写入时间'
|
||||||
const lbProduct = '产品代码'
|
const lbProduct = '产品代码'
|
||||||
const lbCountry = '国家'
|
const lbCountry = '国家'
|
||||||
|
const lbWeight = '公斤段'
|
||||||
|
const lbZone = '分区'
|
||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
{ label: '成功', value: 'SUCCESS' },
|
{ label: '成功', value: 'SUCCESS' },
|
||||||
@@ -224,10 +248,12 @@ const timeTabs = computed(() => {
|
|||||||
const productRows = computed(() => {
|
const productRows = computed(() => {
|
||||||
const minute = activeTime.value
|
const minute = activeTime.value
|
||||||
if (!minute) return []
|
if (!minute) return []
|
||||||
|
const kw = (query.productKeyword || '').trim().toLowerCase()
|
||||||
const map = new Map()
|
const map = new Map()
|
||||||
for (const g of allGroups.value || []) {
|
for (const g of allGroups.value || []) {
|
||||||
if (toMinuteKey(g && g.updateTime) !== minute) continue
|
if (toMinuteKey(g && g.updateTime) !== minute) continue
|
||||||
const code = g.productCode || '-'
|
const code = g.productCode || '-'
|
||||||
|
if (kw && !String(code).toLowerCase().includes(kw)) continue
|
||||||
let row = map.get(code)
|
let row = map.get(code)
|
||||||
if (!row) {
|
if (!row) {
|
||||||
row = {
|
row = {
|
||||||
@@ -253,7 +279,7 @@ function close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetQuery() {
|
function resetQuery() {
|
||||||
query.status = undefined
|
query.productKeyword = ''
|
||||||
reload()
|
reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,6 +321,79 @@ function countryOf(row) {
|
|||||||
return keys.countryCode || keys.countryName || '-'
|
return keys.countryCode || keys.countryName || '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function weightBandOf(row) {
|
||||||
|
const keys = parseReq(row).bizKeys || {}
|
||||||
|
if (keys.weightLabel) return String(keys.weightLabel)
|
||||||
|
if (keys.weightMin != null || keys.weightMax != null) {
|
||||||
|
const min = keys.weightMin != null ? String(keys.weightMin) : ''
|
||||||
|
const max = keys.weightMax != null ? String(keys.weightMax) : ''
|
||||||
|
return min + '<W≤' + max
|
||||||
|
}
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoneOf(row) {
|
||||||
|
const keys = parseReq(row).bizKeys || {}
|
||||||
|
if (keys.zoneCode && keys.zoneName) return keys.zoneName + ' (' + keys.zoneCode + ')'
|
||||||
|
return keys.zoneCode || keys.zoneName || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function countryKeyOf(row) {
|
||||||
|
const keys = parseReq(row).bizKeys || {}
|
||||||
|
return String(keys.countryCode || keys.countryName || '').toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoneKeyOf(row) {
|
||||||
|
const keys = parseReq(row).bizKeys || {}
|
||||||
|
return String(keys.zoneCode || keys.zoneName || '')
|
||||||
|
}
|
||||||
|
|
||||||
|
function weightKeyOf(row) {
|
||||||
|
const keys = parseReq(row).bizKeys || {}
|
||||||
|
if (keys.weightLabel) return String(keys.weightLabel)
|
||||||
|
return String(keys.weightMin ?? '') + '~' + String(keys.weightMax ?? '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayGroupRows = computed(() => {
|
||||||
|
const list = (groupRows.value || []).slice()
|
||||||
|
list.sort((a, b) => {
|
||||||
|
const ca = countryKeyOf(a)
|
||||||
|
const cb = countryKeyOf(b)
|
||||||
|
if (ca !== cb) return ca.localeCompare(cb)
|
||||||
|
const za = zoneKeyOf(a)
|
||||||
|
const zb = zoneKeyOf(b)
|
||||||
|
if (za !== zb) return za.localeCompare(zb)
|
||||||
|
return weightKeyOf(a).localeCompare(weightKeyOf(b))
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
})
|
||||||
|
|
||||||
|
function buildSpans(rows, keyOf) {
|
||||||
|
const spans = new Array(rows.length).fill(1)
|
||||||
|
let i = 0
|
||||||
|
while (i < rows.length) {
|
||||||
|
let j = i + 1
|
||||||
|
const key = keyOf(rows[i])
|
||||||
|
while (j < rows.length && keyOf(rows[j]) === key) j++
|
||||||
|
spans[i] = j - i
|
||||||
|
for (let k = i + 1; k < j; k++) spans[k] = 0
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
return spans
|
||||||
|
}
|
||||||
|
|
||||||
|
const countrySpans = computed(() => buildSpans(displayGroupRows.value, countryKeyOf))
|
||||||
|
|
||||||
|
function spanOf(spans, rowIndex) {
|
||||||
|
const rowspan = spans[rowIndex] || 0
|
||||||
|
return rowspan > 0 ? { rowspan, colspan: 1 } : { rowspan: 0, colspan: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupSpanMethod({ columnIndex, rowIndex }) {
|
||||||
|
if (columnIndex === 0) return spanOf(countrySpans.value, rowIndex)
|
||||||
|
return { rowspan: 1, colspan: 1 }
|
||||||
|
}
|
||||||
|
|
||||||
function changesOf(row) {
|
function changesOf(row) {
|
||||||
const req = parseReq(row)
|
const req = parseReq(row)
|
||||||
return Array.isArray(req.changes) ? req.changes : []
|
return Array.isArray(req.changes) ? req.changes : []
|
||||||
@@ -302,13 +401,19 @@ function changesOf(row) {
|
|||||||
|
|
||||||
function changeSummary(row) {
|
function changeSummary(row) {
|
||||||
const list = changesOf(row)
|
const list = changesOf(row)
|
||||||
if (!list.length) return '-'
|
const band = weightBandOf(row)
|
||||||
return list.map(c => {
|
const pricePart = !list.length
|
||||||
const label = (c && c.fieldLabel) || (c && c.field) || ''
|
? '-'
|
||||||
const oldV = c && c.oldValue != null ? c.oldValue : ''
|
: list.map(c => {
|
||||||
const newV = c && c.newValue != null ? c.newValue : ''
|
const label = (c && c.fieldLabel) || (c && c.field) || ''
|
||||||
return label + ': ' + oldV + ' -> ' + newV
|
const oldV = c && c.oldValue != null ? c.oldValue : ''
|
||||||
}).join(';')
|
const newV = c && c.newValue != null ? c.newValue : ''
|
||||||
|
return label + ': ' + oldV + ' -> ' + newV
|
||||||
|
}).join(';')
|
||||||
|
if (band && band !== '-') {
|
||||||
|
return '公斤段 ' + band + (pricePart === '-' ? '' : ':' + pricePart)
|
||||||
|
}
|
||||||
|
return pricePart
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusLabel(status) {
|
function statusLabel(status) {
|
||||||
@@ -333,7 +438,6 @@ async function reload() {
|
|||||||
const res = await listPriceLogGroups({
|
const res = await listPriceLogGroups({
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 500,
|
pageSize: 500,
|
||||||
status: query.status,
|
|
||||||
psvId,
|
psvId,
|
||||||
priceKind: 'SALES'
|
priceKind: 'SALES'
|
||||||
})
|
})
|
||||||
@@ -354,10 +458,22 @@ function openProduct(row) {
|
|||||||
activeProduct.value = row
|
activeProduct.value = row
|
||||||
groupTitle.value = '变更明细 · ' + (row.productCode || '-') + (activeTime.value ? ' @ ' + activeTime.value : '')
|
groupTitle.value = '变更明细 · ' + (row.productCode || '-') + (activeTime.value ? ' @ ' + activeTime.value : '')
|
||||||
groupQuery.pageNum = 1
|
groupQuery.pageNum = 1
|
||||||
|
groupQuery.countryKeyword = ''
|
||||||
groupOpen.value = true
|
groupOpen.value = true
|
||||||
reloadGroup()
|
reloadGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function searchGroup() {
|
||||||
|
groupQuery.pageNum = 1
|
||||||
|
reloadGroup()
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetGroupQuery() {
|
||||||
|
groupQuery.countryKeyword = ''
|
||||||
|
groupQuery.pageNum = 1
|
||||||
|
reloadGroup()
|
||||||
|
}
|
||||||
|
|
||||||
async function reloadGroup() {
|
async function reloadGroup() {
|
||||||
const psvId = props.version && props.version.psvId
|
const psvId = props.version && props.version.psvId
|
||||||
const p = activeProduct.value
|
const p = activeProduct.value
|
||||||
@@ -368,14 +484,15 @@ async function reloadGroup() {
|
|||||||
}
|
}
|
||||||
groupLoading.value = true
|
groupLoading.value = true
|
||||||
try {
|
try {
|
||||||
|
const kw = (groupQuery.countryKeyword || '').trim()
|
||||||
const res = await listPriceLog({
|
const res = await listPriceLog({
|
||||||
pageNum: groupQuery.pageNum,
|
pageNum: groupQuery.pageNum,
|
||||||
pageSize: groupQuery.pageSize,
|
pageSize: groupQuery.pageSize,
|
||||||
status: query.status,
|
|
||||||
psvId,
|
psvId,
|
||||||
priceKind: 'SALES',
|
priceKind: 'SALES',
|
||||||
productCode: p.productCode === '-' ? undefined : p.productCode,
|
productCode: p.productCode === '-' ? undefined : p.productCode,
|
||||||
updateTimePrefix: activeTime.value || p.updateTimePrefix || undefined
|
updateTimePrefix: activeTime.value || p.updateTimePrefix || undefined,
|
||||||
|
countryKeyword: kw || undefined
|
||||||
})
|
})
|
||||||
groupRows.value = res.rows || []
|
groupRows.value = res.rows || []
|
||||||
groupTotal.value = Number(res.total) || 0
|
groupTotal.value = Number(res.total) || 0
|
||||||
|
|||||||
@@ -12,10 +12,10 @@
|
|||||||
</template>
|
</template>
|
||||||
<el-tabs v-model="statusTab" class="ver-tabs">
|
<el-tabs v-model="statusTab" class="ver-tabs">
|
||||||
<el-tab-pane :label="tabLabel('all')" name="all" />
|
<el-tab-pane :label="tabLabel('all')" name="all" />
|
||||||
<el-tab-pane :label="tabLabel('0')" name="0" />
|
<el-tab-pane :label="tabLabel('pending')" name="pending" />
|
||||||
<el-tab-pane :label="tabLabel('3')" name="3" />
|
|
||||||
<el-tab-pane :label="tabLabel('1')" name="1" />
|
<el-tab-pane :label="tabLabel('1')" name="1" />
|
||||||
<el-tab-pane :label="tabLabel('2')" name="2" />
|
<el-tab-pane :label="tabLabel('2')" name="2" />
|
||||||
|
<el-tab-pane :label="tabLabel('del')" name="del" />
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|
||||||
<el-table :data="filteredVersions" border empty-text="暂无版本">
|
<el-table :data="filteredVersions" border empty-text="暂无版本">
|
||||||
@@ -32,9 +32,12 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="colStatus" width="100" align="center">
|
<el-table-column :label="colStatus" width="100" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="versionStatusTag(row.status)" size="small">{{ versionStatusLabel(row.status) }}</el-tag>
|
<el-tag :type="isDeletedVersion(row) ? 'danger' : versionStatusTag(row.status)" size="small">{{ statusText(row) }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column :label="colCreate" min-width="170" align="center">
|
||||||
|
<template #default="{ row }">{{ row.createTime || '-' }}</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column :label="colRows" width="100" align="center">
|
<el-table-column :label="colRows" width="100" align="center">
|
||||||
<template #default="{ row }">{{ detailCount(row) }}</template>
|
<template #default="{ row }">{{ detailCount(row) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -49,27 +52,27 @@
|
|||||||
@click="emit('view-price-log', row)"
|
@click="emit('view-price-log', row)"
|
||||||
>变更记录</el-button>
|
>变更记录</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="showEditButton(row)"
|
v-if="!isDeletedVersion(row) && showEditButton(row)"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="emit('edit', row)"
|
@click="emit('edit', row)"
|
||||||
>编辑</el-button>
|
>编辑</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="isDraftVersion(row)"
|
v-if="!isDeletedVersion(row) && isDraftVersion(row)"
|
||||||
link
|
link
|
||||||
type="success"
|
type="success"
|
||||||
@click="emit('activate', row)"
|
@click="emit('activate', row)"
|
||||||
v-hasPermi="editPerm"
|
v-hasPermi="editPerm"
|
||||||
>启动</el-button>
|
>启动</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="isPendingVersion(row)"
|
v-if="!isDeletedVersion(row) && isPendingVersion(row)"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="emit('reschedule', row)"
|
@click="emit('reschedule', row)"
|
||||||
v-hasPermi="editPerm"
|
v-hasPermi="editPerm"
|
||||||
>修改生效时间</el-button>
|
>修改生效时间</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="isDraftVersion(row) || isPendingVersion(row) || isEffectVersion(row)"
|
v-if="!isDeletedVersion(row) && (isDraftVersion(row) || isPendingVersion(row) || isEffectVersion(row))"
|
||||||
link
|
link
|
||||||
type="danger"
|
type="danger"
|
||||||
:loading="isRemoving(row)"
|
:loading="isRemoving(row)"
|
||||||
@@ -102,6 +105,7 @@ const emit = defineEmits(['update:modelValue', 'create-version', 'open-details',
|
|||||||
|
|
||||||
const title = '套餐版本'
|
const title = '套餐版本'
|
||||||
const colVer = '版本号'
|
const colVer = '版本号'
|
||||||
|
const colCreate = '创建时间'
|
||||||
const colEff = '生效时间'
|
const colEff = '生效时间'
|
||||||
const colExp = '失效时间'
|
const colExp = '失效时间'
|
||||||
const colStatus = '状态'
|
const colStatus = '状态'
|
||||||
@@ -109,28 +113,42 @@ const colRows = '明细行数'
|
|||||||
const colAction = '操作'
|
const colAction = '操作'
|
||||||
const statusTab = ref('all')
|
const statusTab = ref('all')
|
||||||
|
|
||||||
|
function isDeletedVersion(row) {
|
||||||
|
return !!row && String(row.isDel) === '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusText(row) {
|
||||||
|
if (isDeletedVersion(row)) return '已删除'
|
||||||
|
return versionStatusLabel(row.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeVersions = computed(() => (props.versions || []).filter(v => !isDeletedVersion(v)))
|
||||||
|
const deletedVersions = computed(() => (props.versions || []).filter(v => isDeletedVersion(v)))
|
||||||
|
|
||||||
const counts = computed(() => {
|
const counts = computed(() => {
|
||||||
const map = { all: 0, '0': 0, '3': 0, '1': 0, '2': 0 }
|
const map = { all: 0, pending: 0, '1': 0, '2': 0, del: 0 }
|
||||||
;(props.versions || []).forEach(v => {
|
activeVersions.value.forEach(v => {
|
||||||
map.all++
|
map.all++
|
||||||
const s = String(v.status == null ? '' : v.status)
|
const s = String(v.status == null ? '' : v.status)
|
||||||
if (map[s] != null) map[s]++
|
if (s === '0' || s === '3') map.pending++
|
||||||
|
else if (map[s] != null) map[s]++
|
||||||
})
|
})
|
||||||
|
map.del = deletedVersions.value.length
|
||||||
return map
|
return map
|
||||||
})
|
})
|
||||||
|
|
||||||
function tabLabel(key) {
|
function tabLabel(key) {
|
||||||
const labels = {
|
const labels = {
|
||||||
all: '全部',
|
all: '全部',
|
||||||
'0': '草稿',
|
pending: '待生效',
|
||||||
'3': '待生效',
|
|
||||||
'1': '已生效',
|
'1': '已生效',
|
||||||
'2': '已失效'
|
'2': '已失效',
|
||||||
|
del: '已删除'
|
||||||
}
|
}
|
||||||
return labels[key] + ' (' + (counts.value[key] || 0) + ')'
|
return labels[key] + ' (' + (counts.value[key] || 0) + ')'
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasDraft = computed(() => (props.versions || []).some(v => isDraftVersion(v)))
|
const hasDraft = computed(() => activeVersions.value.some(v => isDraftVersion(v)))
|
||||||
|
|
||||||
function toEffMs(v) {
|
function toEffMs(v) {
|
||||||
if (!v || !v.effectiveTime) return NaN
|
if (!v || !v.effectiveTime) return NaN
|
||||||
@@ -166,10 +184,21 @@ function sortVersions(list) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const filteredVersions = computed(() => {
|
const filteredVersions = computed(() => {
|
||||||
const list = props.versions || []
|
if (statusTab.value === 'del') {
|
||||||
const filtered = statusTab.value === 'all'
|
return sortVersions(deletedVersions.value)
|
||||||
? list
|
}
|
||||||
: list.filter(v => String(v.status) === statusTab.value)
|
const list = activeVersions.value
|
||||||
|
let filtered
|
||||||
|
if (statusTab.value === 'all') {
|
||||||
|
filtered = list
|
||||||
|
} else if (statusTab.value === 'pending') {
|
||||||
|
filtered = list.filter(v => {
|
||||||
|
const s = String(v.status)
|
||||||
|
return s === '0' || s === '3'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
filtered = list.filter(v => String(v.status) === statusTab.value)
|
||||||
|
}
|
||||||
return sortVersions(filtered)
|
return sortVersions(filtered)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -299,6 +299,7 @@ function findPrevVersionEffectiveTime(row) {
|
|||||||
let prev = null
|
let prev = null
|
||||||
for (const v of versions) {
|
for (const v of versions) {
|
||||||
if (!v || v.psvId == null || String(v.psvId) === String(row.psvId)) continue
|
if (!v || v.psvId == null || String(v.psvId) === String(row.psvId)) continue
|
||||||
|
if (String(v.isDel) === '1') continue
|
||||||
const vs = Number(v.versionSeq) || 0
|
const vs = Number(v.versionSeq) || 0
|
||||||
if (vs >= curSeq || !v.effectiveTime) continue
|
if (vs >= curSeq || !v.effectiveTime) continue
|
||||||
if (!prev || vs > (Number(prev.versionSeq) || 0)) prev = v
|
if (!prev || vs > (Number(prev.versionSeq) || 0)) prev = v
|
||||||
@@ -312,6 +313,7 @@ function collectActivateBoundaryTimes(excludePsvId) {
|
|||||||
const seen = new Set()
|
const seen = new Set()
|
||||||
for (const v of set.value.versions || []) {
|
for (const v of set.value.versions || []) {
|
||||||
if (!v || v.psvId == null || String(v.psvId) === String(excludePsvId)) continue
|
if (!v || v.psvId == null || String(v.psvId) === String(excludePsvId)) continue
|
||||||
|
if (String(v.isDel) === '1') continue
|
||||||
;[v.effectiveTime, v.expireTime].forEach(t => {
|
;[v.effectiveTime, v.expireTime].forEach(t => {
|
||||||
const n = t ? normalizeToHour(t) : ''
|
const n = t ? normalizeToHour(t) : ''
|
||||||
if (!n || seen.has(n)) return
|
if (!n || seen.has(n)) return
|
||||||
@@ -449,7 +451,7 @@ function submitActivate() {
|
|||||||
function onCreateVersion() {
|
function onCreateVersion() {
|
||||||
const priceSetId = set.value.priceSetId
|
const priceSetId = set.value.priceSetId
|
||||||
if (priceSetId == null) return
|
if (priceSetId == null) return
|
||||||
const hasDraft = (set.value.versions || []).some(v => v && String(v.status) === '0')
|
const hasDraft = (set.value.versions || []).some(v => v && String(v.isDel) !== '1' && String(v.status) === '0')
|
||||||
if (hasDraft) {
|
if (hasDraft) {
|
||||||
proxy.$modal.msgWarning('已有草稿版本,请先启动或删除后再新建')
|
proxy.$modal.msgWarning('已有草稿版本,请先启动或删除后再新建')
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user