优化套餐编辑逻辑
This commit is contained in:
@@ -30,6 +30,9 @@ public interface PrcPriceSetDetailMapper
|
|||||||
|
|
||||||
public int deletePrcPriceSetDetailByPsvId(Long psvId);
|
public int deletePrcPriceSetDetailByPsvId(Long psvId);
|
||||||
|
|
||||||
|
/** Hard delete detail rows by ids (draft / pending). */
|
||||||
|
public int physicalDeleteByIds(Long[] psdIds);
|
||||||
|
|
||||||
/** Hard delete all detail rows under a version. */
|
/** Hard delete all detail rows under a version. */
|
||||||
public int physicalDeleteByPsvId(@Param("psvId") Long psvId);
|
public int physicalDeleteByPsvId(@Param("psvId") Long psvId);
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ public interface PrcPriceSetProductMapper
|
|||||||
|
|
||||||
public int deleteByPsvIdAndCode(@Param("psvId") Long psvId, @Param("productCode") String productCode);
|
public int deleteByPsvIdAndCode(@Param("psvId") Long psvId, @Param("productCode") String productCode);
|
||||||
|
|
||||||
|
/** Hard delete one product under a version (draft / pending). */
|
||||||
|
public int physicalDeleteByPsvIdAndCode(@Param("psvId") Long psvId, @Param("productCode") String productCode);
|
||||||
|
|
||||||
/** Hard delete all product rows under a version. */
|
/** Hard delete all product rows under a version. */
|
||||||
public int physicalDeleteByPsvId(@Param("psvId") Long psvId);
|
public int physicalDeleteByPsvId(@Param("psvId") Long psvId);
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,16 @@ public interface PrcSurchargeRelMapper
|
|||||||
@Param("productIds") List<Long> productIds,
|
@Param("productIds") List<Long> productIds,
|
||||||
@Param("priceType") String priceType);
|
@Param("priceType") String priceType);
|
||||||
|
|
||||||
|
/** Hard delete surcharge rows by detail ids (draft / pending). */
|
||||||
|
public int physicalDeleteByDetailIdsAndPriceType(
|
||||||
|
@Param("detailIds") List<Long> detailIds,
|
||||||
|
@Param("priceType") String priceType);
|
||||||
|
|
||||||
|
/** Hard delete surcharge rows by set-product ids (draft / pending). */
|
||||||
|
public int physicalDeleteByProductIdsAndPriceType(
|
||||||
|
@Param("productIds") List<Long> productIds,
|
||||||
|
@Param("priceType") String priceType);
|
||||||
|
|
||||||
/** Hard delete surcharge rows linked to products/details under a version. */
|
/** Hard delete surcharge rows linked to products/details under a version. */
|
||||||
public int physicalDeleteByPsvId(@Param("psvId") Long psvId);
|
public int physicalDeleteByPsvId(@Param("psvId") Long psvId);
|
||||||
|
|
||||||
|
|||||||
+129
-33
@@ -547,9 +547,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
throw new ServiceException(MessageUtils.message("price.set.version.delete.statusNotAllowed"));
|
throw new ServiceException(MessageUtils.message("price.set.version.delete.statusNotAllowed"));
|
||||||
}
|
}
|
||||||
requireSet(version.getPriceSetId());
|
requireSet(version.getPriceSetId());
|
||||||
deleteSalesRelsByPsvId(psvId);
|
// Draft / pending only reach here: hard-delete children with the version row
|
||||||
detailMapper.deletePrcPriceSetDetailByPsvId(psvId);
|
wipeVersionChildren(psvId, version.getStatus());
|
||||||
setProductMapper.deleteByPsvId(psvId);
|
|
||||||
int rows = versionMapper.deletePrcPriceSetVersionByIds(new Long[] { psvId });
|
int rows = versionMapper.deletePrcPriceSetVersionByIds(new Long[] { psvId });
|
||||||
// Rechain expire_time / status for remaining versions (e.g. reopen previous effect window)
|
// Rechain expire_time / status for remaining versions (e.g. reopen previous effect window)
|
||||||
refreshVersionStatus(version.getPriceSetId());
|
refreshVersionStatus(version.getPriceSetId());
|
||||||
@@ -1506,7 +1505,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
}
|
}
|
||||||
if (!deleteIds.isEmpty())
|
if (!deleteIds.isEmpty())
|
||||||
{
|
{
|
||||||
detailMapper.deletePrcPriceSetDetailByIds(deleteIds.toArray(new Long[0]));
|
// Draft/pending: hard delete covered rows; effect/expired: soft delete.
|
||||||
|
deleteDetailRowsByIds(deleteIds, isDraftOrPendingVersionStatus(resolveVersionStatus(psvId)));
|
||||||
}
|
}
|
||||||
removeDetailsByReplaceScopes(details, detailIndex, replaceScopes);
|
removeDetailsByReplaceScopes(details, detailIndex, replaceScopes);
|
||||||
}
|
}
|
||||||
@@ -2122,9 +2122,20 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
@Override
|
@Override
|
||||||
public int deleteDetailByIds(Long[] psdIds)
|
public int deleteDetailByIds(Long[] psdIds)
|
||||||
{
|
{
|
||||||
Set<String> touchKeys = new HashSet<String>();
|
if (psdIds == null || psdIds.length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
List<Long> hardIds = new ArrayList<Long>();
|
||||||
|
List<Long> softIds = new ArrayList<Long>();
|
||||||
|
Set<String> hardTouchKeys = new HashSet<String>();
|
||||||
|
Set<String> softTouchKeys = new HashSet<String>();
|
||||||
for (Long id : psdIds)
|
for (Long id : psdIds)
|
||||||
{
|
{
|
||||||
|
if (id == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
PrcPriceSetDetail old = detailMapper.selectPrcPriceSetDetailById(id);
|
PrcPriceSetDetail old = detailMapper.selectPrcPriceSetDetailById(id);
|
||||||
if (old == null)
|
if (old == null)
|
||||||
{
|
{
|
||||||
@@ -2132,27 +2143,84 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
}
|
}
|
||||||
PrcPriceSet db = requireSet(resolvePriceSetIdByPsvId(old.getPsvId()));
|
PrcPriceSet db = requireSet(resolvePriceSetIdByPsvId(old.getPsvId()));
|
||||||
assertChildEditable(db);
|
assertChildEditable(db);
|
||||||
if (old.getPsvId() != null && StringUtils.isNotEmpty(old.getProductCode()))
|
boolean hard = isDraftOrPendingVersionStatus(resolveVersionStatus(old.getPsvId()));
|
||||||
|
if (hard)
|
||||||
{
|
{
|
||||||
touchKeys.add(old.getPsvId() + "|" + old.getProductCode());
|
hardIds.add(id);
|
||||||
}
|
if (old.getPsvId() != null && StringUtils.isNotEmpty(old.getProductCode()))
|
||||||
}
|
|
||||||
if (psdIds != null && psdIds.length > 0)
|
|
||||||
{
|
|
||||||
List<Long> ids = new ArrayList<Long>();
|
|
||||||
for (Long id : psdIds)
|
|
||||||
{
|
|
||||||
if (id != null)
|
|
||||||
{
|
{
|
||||||
ids.add(id);
|
hardTouchKeys.add(old.getPsvId() + "|" + old.getProductCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ids.isEmpty())
|
else
|
||||||
{
|
{
|
||||||
surchargeRelMapper.deleteByDetailIdsAndPriceType(ids, PrcSurchargeRel.PRICE_TYPE_SALES);
|
softIds.add(id);
|
||||||
|
if (old.getPsvId() != null && StringUtils.isNotEmpty(old.getProductCode()))
|
||||||
|
{
|
||||||
|
softTouchKeys.add(old.getPsvId() + "|" + old.getProductCode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int rows = detailMapper.deletePrcPriceSetDetailByIds(psdIds);
|
int rows = 0;
|
||||||
|
if (!hardIds.isEmpty())
|
||||||
|
{
|
||||||
|
rows += deleteDetailRowsByIds(hardIds, true);
|
||||||
|
cleanupOrphanSetProducts(hardTouchKeys, true);
|
||||||
|
}
|
||||||
|
if (!softIds.isEmpty())
|
||||||
|
{
|
||||||
|
rows += deleteDetailRowsByIds(softIds, false);
|
||||||
|
cleanupOrphanSetProducts(softTouchKeys, false);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete detail rows (+ SALES surcharge rels).
|
||||||
|
* hard=true: physical delete (draft/pending); false: soft delete (effect/expired).
|
||||||
|
*/
|
||||||
|
private int deleteDetailRowsByIds(List<Long> ids, boolean hard)
|
||||||
|
{
|
||||||
|
if (ids == null || ids.isEmpty())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Long[] arr = ids.toArray(new Long[0]);
|
||||||
|
if (hard)
|
||||||
|
{
|
||||||
|
surchargeRelMapper.physicalDeleteByDetailIdsAndPriceType(ids, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||||
|
return detailMapper.physicalDeleteByIds(arr);
|
||||||
|
}
|
||||||
|
surchargeRelMapper.deleteByDetailIdsAndPriceType(ids, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||||
|
return detailMapper.deletePrcPriceSetDetailByIds(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Version status draft(0) / pending(3): hard delete children. */
|
||||||
|
private boolean isDraftOrPendingVersionStatus(String status)
|
||||||
|
{
|
||||||
|
return VER_DRAFT.equals(status) || VER_PENDING.equals(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveVersionStatus(Long psvId)
|
||||||
|
{
|
||||||
|
if (psvId == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PrcPriceSetVersion ver = versionMapper.selectPrcPriceSetVersionById(psvId);
|
||||||
|
return ver == null ? null : ver.getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After detail delete: if no detail left for product, remove set-product + its SALES rels.
|
||||||
|
* hard=true uses physical delete (draft/pending); otherwise soft delete (effect/expired).
|
||||||
|
*/
|
||||||
|
private void cleanupOrphanSetProducts(Set<String> touchKeys, boolean hard)
|
||||||
|
{
|
||||||
|
if (touchKeys == null || touchKeys.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (String key : touchKeys)
|
for (String key : touchKeys)
|
||||||
{
|
{
|
||||||
int sep = key.indexOf('|');
|
int sep = key.indexOf('|');
|
||||||
@@ -2166,18 +2234,51 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
q.setPsvId(psvId);
|
q.setPsvId(psvId);
|
||||||
q.setProductCode(productCode);
|
q.setProductCode(productCode);
|
||||||
List<PrcPriceSetDetail> left = detailMapper.selectPrcPriceSetDetailList(q);
|
List<PrcPriceSetDetail> left = detailMapper.selectPrcPriceSetDetailList(q);
|
||||||
if (left == null || left.isEmpty())
|
if (left != null && !left.isEmpty())
|
||||||
{
|
{
|
||||||
Long pspId = lookupSetProductId(psvId, productCode);
|
continue;
|
||||||
if (pspId != null)
|
}
|
||||||
|
Long pspId = lookupSetProductId(psvId, productCode);
|
||||||
|
if (pspId != null)
|
||||||
|
{
|
||||||
|
List<Long> pspIds = java.util.Collections.singletonList(pspId);
|
||||||
|
if (hard)
|
||||||
{
|
{
|
||||||
surchargeRelMapper.deleteByProductIdsAndPriceType(
|
surchargeRelMapper.physicalDeleteByProductIdsAndPriceType(pspIds, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||||
java.util.Collections.singletonList(pspId), PrcSurchargeRel.PRICE_TYPE_SALES);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
surchargeRelMapper.deleteByProductIdsAndPriceType(pspIds, PrcSurchargeRel.PRICE_TYPE_SALES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hard)
|
||||||
|
{
|
||||||
|
setProductMapper.physicalDeleteByPsvIdAndCode(psvId, productCode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
setProductMapper.deleteByPsvIdAndCode(psvId, productCode);
|
setProductMapper.deleteByPsvIdAndCode(psvId, productCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rows;
|
}
|
||||||
|
|
||||||
|
/** Wipe version products/details/SALES rels: physical for draft/pending, soft otherwise. */
|
||||||
|
private void wipeVersionChildren(Long psvId, String versionStatus)
|
||||||
|
{
|
||||||
|
if (psvId == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isDraftOrPendingVersionStatus(versionStatus))
|
||||||
|
{
|
||||||
|
surchargeRelMapper.physicalDeleteByPsvId(psvId);
|
||||||
|
detailMapper.physicalDeleteByPsvId(psvId);
|
||||||
|
setProductMapper.physicalDeleteByPsvId(psvId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deleteSalesRelsByPsvId(psvId);
|
||||||
|
detailMapper.deletePrcPriceSetDetailByPsvId(psvId);
|
||||||
|
setProductMapper.deleteByPsvId(psvId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -3207,9 +3308,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Long psvId = db.getPsvId();
|
Long psvId = db.getPsvId();
|
||||||
deleteSalesRelsByPsvId(psvId);
|
wipeVersionChildren(psvId, db.getStatus());
|
||||||
detailMapper.deletePrcPriceSetDetailByPsvId(psvId);
|
|
||||||
setProductMapper.deleteByPsvId(psvId);
|
|
||||||
if ("IMPORT".equalsIgnoreCase(newMode))
|
if ("IMPORT".equalsIgnoreCase(newMode))
|
||||||
{
|
{
|
||||||
copyLatestPriorVersionChildren(db.getPriceSetId(), psvId);
|
copyLatestPriorVersionChildren(db.getPriceSetId(), psvId);
|
||||||
@@ -3332,9 +3431,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
set.setDetails(new ArrayList<PrcPriceSetDetail>());
|
set.setDetails(new ArrayList<PrcPriceSetDetail>());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
deleteSalesRelsByPsvId(version.getPsvId());
|
wipeVersionChildren(version.getPsvId(), version.getStatus());
|
||||||
detailMapper.deletePrcPriceSetDetailByPsvId(version.getPsvId());
|
|
||||||
setProductMapper.deleteByPsvId(version.getPsvId());
|
|
||||||
// clean legacy rows without psv_id under this set (would look like duplicates in UI)
|
// clean legacy rows without psv_id under this set (would look like duplicates in UI)
|
||||||
detailMapper.deleteOrphanDetailsByPriceSetId(set.getPriceSetId());
|
detailMapper.deleteOrphanDetailsByPriceSetId(set.getPriceSetId());
|
||||||
int productRows = setProductMapper.insertFromLibrary(version.getPsvId());
|
int productRows = setProductMapper.insertFromLibrary(version.getPsvId());
|
||||||
@@ -3481,8 +3578,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
}
|
}
|
||||||
if (!orphanIds.isEmpty())
|
if (!orphanIds.isEmpty())
|
||||||
{
|
{
|
||||||
surchargeRelMapper.deleteByDetailIdsAndPriceType(orphanIds, PrcSurchargeRel.PRICE_TYPE_SALES);
|
deleteDetailRowsByIds(orphanIds, isDraftOrPendingVersionStatus(version.getStatus()));
|
||||||
detailMapper.deletePrcPriceSetDetailByIds(orphanIds.toArray(new Long[0]));
|
|
||||||
}
|
}
|
||||||
Set<String> keepCodes = new HashSet<String>();
|
Set<String> keepCodes = new HashSet<String>();
|
||||||
if (details != null)
|
if (details != null)
|
||||||
|
|||||||
@@ -426,6 +426,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
</foreach>
|
</foreach>
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<delete id="physicalDeleteByIds" parameterType="Long">
|
||||||
|
delete from prc_price_set_detail
|
||||||
|
where psd_id in
|
||||||
|
<foreach item="psdId" collection="array" open="(" separator="," close=")">
|
||||||
|
#{psdId}
|
||||||
|
</foreach>
|
||||||
|
</delete>
|
||||||
|
|
||||||
<update id="deletePrcPriceSetDetailByPriceSetId" parameterType="Long">
|
<update id="deletePrcPriceSetDetailByPriceSetId" parameterType="Long">
|
||||||
update prc_price_set_detail d
|
update prc_price_set_detail d
|
||||||
inner join prc_price_set_version v on v.psv_id = d.psv_id
|
inner join prc_price_set_version v on v.psv_id = d.psv_id
|
||||||
|
|||||||
@@ -105,6 +105,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
and ifnull(is_del, '0') = '0'
|
and ifnull(is_del, '0') = '0'
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<delete id="physicalDeleteByPsvIdAndCode">
|
||||||
|
delete from prc_price_set_product
|
||||||
|
where psv_id = #{psvId}
|
||||||
|
and product_code = #{productCode}
|
||||||
|
</delete>
|
||||||
|
|
||||||
<insert id="copyFromVersion">
|
<insert id="copyFromVersion">
|
||||||
insert into prc_price_set_product (
|
insert into prc_price_set_product (
|
||||||
price_type, psv_id, product_code, product_name, service_level, cargo_attr,
|
price_type, psv_id, product_code, product_name, service_level, cargo_attr,
|
||||||
|
|||||||
@@ -136,6 +136,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
</foreach>
|
</foreach>
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<delete id="physicalDeleteByDetailIdsAndPriceType">
|
||||||
|
delete from prc_surcharge_rel
|
||||||
|
where price_type = #{priceType}
|
||||||
|
and detail_id in
|
||||||
|
<foreach item="id" collection="detailIds" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<delete id="physicalDeleteByProductIdsAndPriceType">
|
||||||
|
delete from prc_surcharge_rel
|
||||||
|
where price_type = #{priceType}
|
||||||
|
and product_id in
|
||||||
|
<foreach item="id" collection="productIds" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</delete>
|
||||||
|
|
||||||
<delete id="physicalDeleteByPsvId">
|
<delete id="physicalDeleteByPsvId">
|
||||||
delete r from prc_surcharge_rel r
|
delete r from prc_surcharge_rel r
|
||||||
where exists (
|
where exists (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card shadow="never" class="step-card">
|
<el-card shadow="never" class="step-card">
|
||||||
<template #header><span>第 1 步 · 基本信息</span></template>
|
<template #header><span>基本信息</span></template>
|
||||||
<el-form ref="formRef" :model="basic" :rules="rules" label-width="120px" class="max-form">
|
<el-form ref="formRef" :model="basic" :rules="rules" label-width="120px" class="max-form">
|
||||||
<el-form-item label="套餐名称" prop="priceSetName">
|
<el-form-item label="套餐名称" prop="priceSetName">
|
||||||
<el-input v-model="basic.priceSetName" maxlength="50" placeholder="如:金牌电商专属套餐" />
|
<el-input v-model="basic.priceSetName" maxlength="50" placeholder="如:金牌电商专属套餐" />
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="StepBasicInfo">
|
<script setup name="StepBasicInfo">
|
||||||
/** Step1 basic info. setType always FIXED on save; priceBuildMode chosen on Step2. */
|
/** Basic info. setType always FIXED on save; priceBuildMode chosen in details panel. */
|
||||||
const basic = defineModel({ type: Object, required: true })
|
const basic = defineModel({ type: Object, required: true })
|
||||||
|
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ const props = defineProps({
|
|||||||
effectiveTime: { type: String, default: undefined },
|
effectiveTime: { type: String, default: undefined },
|
||||||
versionNo: { type: String, default: '' },
|
versionNo: { type: String, default: '' },
|
||||||
refData: { type: Object, required: true },
|
refData: { type: Object, required: true },
|
||||||
headerTitle: { type: String, default: '第 2 步 · 编辑产品价格' },
|
headerTitle: { type: String, default: '编辑产品价格' },
|
||||||
/** true=edit live DB; false=create local draft until finish */
|
/** true=edit live DB; false=create local draft until finish */
|
||||||
livePersist: { type: Boolean, default: true },
|
livePersist: { type: Boolean, default: true },
|
||||||
/** Version status: draft can change build mode; started versions lock it */
|
/** Version status: draft can change build mode; started versions lock it */
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-alert type="info" :closable="false" show-icon class="mb12" :title="hint" />
|
<el-alert type="info" :closable="false" show-icon class="mb12" :title="hint" />
|
||||||
<el-empty v-if="!filteredProducts.length" :description="emptyProduct" :image-size="72" />
|
<el-empty v-if="!filteredProducts.length" :description="emptyProduct" :image-size="72" />
|
||||||
<el-table v-else :data="filteredProducts" border height="calc(100vh - 280px)" @row-click="openProductDetail">
|
<el-table v-else :data="filteredProducts" border height="calc(100vh - 280px)">
|
||||||
<el-table-column :label="colCode" min-width="100">
|
<el-table-column :label="colCode" min-width="100">
|
||||||
<template #default="{ row }"><b>{{ row.productCode }}</b></template>
|
<template #default="{ row }"><b>{{ row.productCode }}</b></template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -151,7 +151,7 @@ const activeProduct = ref(null)
|
|||||||
|
|
||||||
const phSearch = '产品代码/名称'
|
const phSearch = '产品代码/名称'
|
||||||
const phDetailSearch = '搜索国家/分区'
|
const phDetailSearch = '搜索国家/分区'
|
||||||
const hint = '点击产品行查看国家/重量段明细(仅查看)'
|
const hint = '点击「查看明细」查看国家/重量段明细(仅查看)'
|
||||||
const emptyProduct = '暂无产品'
|
const emptyProduct = '暂无产品'
|
||||||
const emptyDetail = '暂无明细'
|
const emptyDetail = '暂无明细'
|
||||||
const colCode = '产品代码'
|
const colCode = '产品代码'
|
||||||
|
|||||||
@@ -1,46 +1,34 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-steps
|
<div v-if="showBasic" class="create-section">
|
||||||
v-if="!newVersionMode"
|
|
||||||
:active="active"
|
|
||||||
finish-status="success"
|
|
||||||
align-center
|
|
||||||
class="wizard-steps"
|
|
||||||
>
|
|
||||||
<el-step :title="step1Title" />
|
|
||||||
<el-step :title="step2Title" />
|
|
||||||
</el-steps>
|
|
||||||
|
|
||||||
<div v-show="active === 0">
|
|
||||||
<step-basic-info ref="basicRef" v-model="form.basic" />
|
<step-basic-info ref="basicRef" v-model="form.basic" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<step-details
|
<div class="create-section">
|
||||||
v-show="active === 1"
|
<step-details
|
||||||
ref="detailsRef"
|
ref="detailsRef"
|
||||||
:details="form.details"
|
:details="form.details"
|
||||||
:products="form.products"
|
:products="form.products"
|
||||||
:price-set-id="form.priceSetId"
|
:price-set-id="form.priceSetId"
|
||||||
:psv-id="form.version.psvId"
|
:psv-id="form.version.psvId"
|
||||||
@update:psv-id="onPsvIdUpdate"
|
@update:psv-id="onPsvIdUpdate"
|
||||||
:set-type="form.basic.setType"
|
:set-type="form.basic.setType"
|
||||||
:price-build-mode="form.version.priceBuildMode"
|
:price-build-mode="form.version.priceBuildMode"
|
||||||
@update:price-build-mode="onPriceBuildModeUpdate"
|
@update:price-build-mode="onPriceBuildModeUpdate"
|
||||||
@update:pef-id="onPefIdUpdate"
|
@update:pef-id="onPefIdUpdate"
|
||||||
:version-no="form.version.versionNo"
|
:version-no="form.version.versionNo"
|
||||||
:version-status="form.version.status"
|
:version-status="form.version.status"
|
||||||
:ref-data="refData"
|
:ref-data="refData"
|
||||||
:live-persist="livePersist"
|
:live-persist="livePersist"
|
||||||
:build-mode-dirty="buildModeDirty"
|
:build-mode-dirty="buildModeDirty"
|
||||||
:header-title="detailsHeaderTitle"
|
:header-title="detailsHeaderTitle"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="wizard-actions">
|
<div class="wizard-actions">
|
||||||
<el-button @click="goBack">取消</el-button>
|
<el-button @click="goBack">取消</el-button>
|
||||||
<el-button v-if="active > 0 && !directStep2" @click="active--">上一步</el-button>
|
<el-button v-if="showBasic" type="info" :loading="saving" @click="saveDraft">保存</el-button>
|
||||||
<el-button v-if="active === 0" type="primary" :loading="creating" @click="goStep2">创建套餐价格</el-button>
|
<el-button type="primary" :loading="creating" @click="finishEdit">完成</el-button>
|
||||||
<el-button v-if="active === 0" type="info" :loading="saving" @click="saveDraft">保存</el-button>
|
|
||||||
<el-button v-if="active === 1" type="primary" :loading="creating" @click="finishEdit">完成</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
@@ -79,7 +67,6 @@ const route = useRoute()
|
|||||||
|
|
||||||
const refData = usePriceSetRefData()
|
const refData = usePriceSetRefData()
|
||||||
|
|
||||||
const active = ref(0)
|
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const creating = ref(false)
|
const creating = ref(false)
|
||||||
const progressVisible = ref(false)
|
const progressVisible = ref(false)
|
||||||
@@ -90,18 +77,14 @@ const basicRef = ref()
|
|||||||
const detailsRef = ref()
|
const detailsRef = ref()
|
||||||
const copyMode = computed(() => String(route.query.copyVersion || '') === '1')
|
const copyMode = computed(() => String(route.query.copyVersion || '') === '1')
|
||||||
const newVersionMode = computed(() => String(route.query.newVersion || '') === '1')
|
const newVersionMode = computed(() => String(route.query.newVersion || '') === '1')
|
||||||
/** copy / newVersion: defer DB write until finish (same as create step2) */
|
/** copy / newVersion: defer DB write until finish */
|
||||||
const deferPersist = computed(() => copyMode.value || newVersionMode.value)
|
const deferPersist = computed(() => copyMode.value || newVersionMode.value)
|
||||||
/** Entered from detail: continue-config / copy-version / new-version jump to step2 */
|
/** Continue-config / copy / new-version: hide basic (package already exists) */
|
||||||
const directStep2 = computed(() => deferPersist.value || String(route.query.step || '') === '2')
|
const showBasic = computed(() => !deferPersist.value && String(route.query.step || '') !== '2')
|
||||||
/** Edit existing version: live PUT/pull/import. New create / copy / newVersion: defer until finish. */
|
/** Edit existing version: live PUT/pull/import. New create / copy / newVersion: defer until finish. */
|
||||||
const livePersist = computed(() => !!form.priceSetId && !deferPersist.value)
|
const livePersist = computed(() => !!form.priceSetId && !deferPersist.value)
|
||||||
|
|
||||||
const step1Title = '基本信息'
|
const detailsHeaderTitle = '编辑产品价格'
|
||||||
const step2Title = '编辑产品价格'
|
|
||||||
const detailsHeaderTitle = computed(() =>
|
|
||||||
newVersionMode.value ? '编辑产品价格' : '第 2 步 · 编辑产品价格'
|
|
||||||
)
|
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
priceSetId: undefined,
|
priceSetId: undefined,
|
||||||
@@ -172,18 +155,7 @@ async function runSaveWithProgress(payload) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Step1 -> Step2: validate only; no DB write on create (defer until finish). */
|
/** Finish: create/copy async save all; edit already live-saved. */
|
||||||
async function goStep2() {
|
|
||||||
const validBasic = await basicRef.value.validate()
|
|
||||||
if (!validBasic) return
|
|
||||||
if (!(form.products || []).some(p => p && p.productCode) && !form.version.priceBuildMode) {
|
|
||||||
form.details = []
|
|
||||||
form.products = []
|
|
||||||
}
|
|
||||||
active.value = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Step2 finish: create/copy async save all; edit already live-saved. */
|
|
||||||
async function finishEdit() {
|
async function finishEdit() {
|
||||||
if (livePersist.value) {
|
if (livePersist.value) {
|
||||||
if (!form.priceSetId) {
|
if (!form.priceSetId) {
|
||||||
@@ -235,10 +207,9 @@ async function finishEdit() {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const validBasic = await basicRef.value.validate()
|
if (showBasic.value) {
|
||||||
if (!validBasic) {
|
const validBasic = await basicRef.value.validate()
|
||||||
active.value = 0
|
if (!validBasic) return
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if (!form.version.priceBuildMode) {
|
if (!form.version.priceBuildMode) {
|
||||||
proxy.$modal.msgWarning('请先选择创建价格方式')
|
proxy.$modal.msgWarning('请先选择创建价格方式')
|
||||||
@@ -277,13 +248,10 @@ async function finishEdit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Step0 only: persist basic draft (no detail wipe). */
|
/** Persist basic draft (no detail wipe). */
|
||||||
async function saveDraft() {
|
async function saveDraft() {
|
||||||
const validBasic = await basicRef.value.validate()
|
const validBasic = await basicRef.value.validate()
|
||||||
if (!validBasic) {
|
if (!validBasic) return
|
||||||
active.value = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
saving.value = true
|
saving.value = true
|
||||||
try {
|
try {
|
||||||
const priceSetId = await runSaveWithProgress(buildPayload())
|
const priceSetId = await runSaveWithProgress(buildPayload())
|
||||||
@@ -532,9 +500,6 @@ function applySetData(data, asCopy, asNewVersion) {
|
|||||||
function loadExisting(id, asCopy, asNewVersion) {
|
function loadExisting(id, asCopy, asNewVersion) {
|
||||||
getPriceSet(id).then(res => {
|
getPriceSet(id).then(res => {
|
||||||
applySetData(res.data || {}, asCopy, asNewVersion)
|
applySetData(res.data || {}, asCopy, asNewVersion)
|
||||||
if (asCopy || asNewVersion || String(route.query.step || '') === '2') {
|
|
||||||
active.value = 1
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,11 +514,8 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.wizard-steps {
|
.create-section {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 16px;
|
||||||
background: #fff;
|
|
||||||
padding: 20px 12px 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
}
|
||||||
.wizard-actions {
|
.wizard-actions {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user