修改导入套餐
This commit is contained in:
@@ -78,6 +78,7 @@ price.set.import.fixedOnly=\u4ec5\u56fa\u5b9a\u4ef7\u5957\u9910\u652f\u6301\u5bf
|
|||||||
price.set.import.noDetails=\u5957\u9910\u6682\u65e0\u4ef7\u683c\u660e\u7ec6\uff0c\u8bf7\u5148\u62c9\u53d6\u4ea7\u54c1\u4ef7
|
price.set.import.noDetails=\u5957\u9910\u6682\u65e0\u4ef7\u683c\u660e\u7ec6\uff0c\u8bf7\u5148\u62c9\u53d6\u4ea7\u54c1\u4ef7
|
||||||
price.set.import.noPriceRows=\u4ef7\u8868\u672a\u89e3\u6790\u5230\u6709\u6548\u4ef7\u683c\u884c
|
price.set.import.noPriceRows=\u4ef7\u8868\u672a\u89e3\u6790\u5230\u6709\u6548\u4ef7\u683c\u884c
|
||||||
price.set.import.success=\u5bfc\u5165\u5b8c\u6210\uff1a\u66f4\u65b0 {0} \u6761\uff0c\u672a\u5339\u914d {1} \u6761\uff0c\u5957\u9910\u65e0\u5bf9\u5e94\u4ea7\u54c1 {2} \u4e2a
|
price.set.import.success=\u5bfc\u5165\u5b8c\u6210\uff1a\u66f4\u65b0 {0} \u6761\uff0c\u672a\u5339\u914d {1} \u6761\uff0c\u5957\u9910\u65e0\u5bf9\u5e94\u4ea7\u54c1 {2} \u4e2a
|
||||||
|
price.set.import.noDetailWritten=\u672a\u5199\u5165\u4efb\u4f55\u5957\u9910\u660e\u7ec6\uff08\u4ef7\u8868\u65e0\u53ef\u7528\u91cd\u91cf\u6bb5\u6216\u5747\u4e3a\u7a7a\u8fd0\u8d39/\u6302\u53f7\u8d39\u4e14\u65e0\u53ef\u66f4\u65b0\u884c\uff09
|
||||||
price.set.import.upsertSuccess=\u5bfc\u5165\u5b8c\u6210\uff1a\u65b0\u589e {0} \u6761\uff0c\u66f4\u65b0 {1} \u6761\uff0c\u8df3\u8fc7\u7a7a\u8fd0\u8d39/\u6302\u53f7\u8d39 {2} \u6761
|
price.set.import.upsertSuccess=\u5bfc\u5165\u5b8c\u6210\uff1a\u65b0\u589e {0} \u6761\uff0c\u66f4\u65b0 {1} \u6761\uff0c\u8df3\u8fc7\u7a7a\u8fd0\u8d39/\u6302\u53f7\u8d39 {2} \u6761
|
||||||
price.set.pull.success=\u62c9\u53d6\u5b8c\u6210\uff1a\u5df2\u5199\u5165 {0} \u6761\u5957\u9910\u660e\u7ec6
|
price.set.pull.success=\u62c9\u53d6\u5b8c\u6210\uff1a\u5df2\u5199\u5165 {0} \u6761\u5957\u9910\u660e\u7ec6
|
||||||
price.set.customer.notExists=客户绑定不存在
|
price.set.customer.notExists=客户绑定不存在
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class PrcPriceSet extends BaseEntity
|
|||||||
private List<PrcCustomerPriceSet> customers;
|
private List<PrcCustomerPriceSet> customers;
|
||||||
/** async save task id (not persisted) */
|
/** async save task id (not persisted) */
|
||||||
private String saveTaskId;
|
private String saveTaskId;
|
||||||
/** Y: backend joins product library into details (skip front pull) */
|
/** Y: ignored on save; library pull only via /detail/pullFromLibrary */
|
||||||
private String pullFromLibrary;
|
private String pullFromLibrary;
|
||||||
/** Y: even if version already has details, delete+re-pull from library */
|
/** Y: even if version already has details, delete+re-pull from library */
|
||||||
private String forcePullLibrary;
|
private String forcePullLibrary;
|
||||||
|
|||||||
+35
-19
@@ -892,7 +892,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
set.setUpdateBy(operName);
|
set.setUpdateBy(operName);
|
||||||
set.setCreateBy(operName);
|
set.setCreateBy(operName);
|
||||||
set.setSaveTaskId(taskId);
|
set.setSaveTaskId(taskId);
|
||||||
set.setPullFromLibrary("Y");
|
// Dedicated pull API — does not go through savePrcPriceSet.
|
||||||
|
set.setPullFromLibrary(null);
|
||||||
set.setForcePullLibrary(force ? "Y" : "N");
|
set.setForcePullLibrary(force ? "Y" : "N");
|
||||||
if (psvId == null)
|
if (psvId == null)
|
||||||
{
|
{
|
||||||
@@ -1004,6 +1005,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
List<PrcPriceSetDetail> hits = matchImportDetails(candidates, row);
|
List<PrcPriceSetDetail> hits = matchImportDetails(candidates, row);
|
||||||
if (!hits.isEmpty())
|
if (!hits.isEmpty())
|
||||||
{
|
{
|
||||||
|
// Existing band: only skip when Excel left both fees blank (keep old prices).
|
||||||
if (freight == null && registration == null)
|
if (freight == null && registration == null)
|
||||||
{
|
{
|
||||||
skippedFee++;
|
skippedFee++;
|
||||||
@@ -1029,11 +1031,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (freight == null && registration == null)
|
// New band: always insert weight/country row (fees may be null; editable later).
|
||||||
{
|
|
||||||
skippedFee++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
PrcPriceSetDetail created = buildDetailFromImportRow(
|
PrcPriceSetDetail created = buildDetailFromImportRow(
|
||||||
priceSetId, psvId, productCode, productName, row, freight, registration);
|
priceSetId, psvId, productCode, productName, row, freight, registration);
|
||||||
created.setPspId(pspId);
|
created.setPspId(pspId);
|
||||||
@@ -1043,11 +1041,19 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
{
|
{
|
||||||
touched.add(created.getPsdId());
|
touched.add(created.getPsdId());
|
||||||
}
|
}
|
||||||
|
if (freight == null && registration == null)
|
||||||
|
{
|
||||||
|
skippedFee++;
|
||||||
|
}
|
||||||
inserted++;
|
inserted++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setProductMapper.bindDetailPspByPsvId(psvId);
|
setProductMapper.bindDetailPspByPsvId(psvId);
|
||||||
reportImportProgress(taskId, priceSetId, 95, "finishing");
|
reportImportProgress(taskId, priceSetId, 95, "finishing");
|
||||||
|
if (inserted == 0 && updated == 0)
|
||||||
|
{
|
||||||
|
throw new ServiceException(MessageUtils.message("price.set.import.noDetailWritten"));
|
||||||
|
}
|
||||||
return MessageUtils.message("price.set.import.upsertSuccess",
|
return MessageUtils.message("price.set.import.upsertSuccess",
|
||||||
Integer.valueOf(inserted), Integer.valueOf(updated), Integer.valueOf(skippedFee));
|
Integer.valueOf(inserted), Integer.valueOf(updated), Integer.valueOf(skippedFee));
|
||||||
}
|
}
|
||||||
@@ -1086,16 +1092,16 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
PrcPriceSetDetail d = new PrcPriceSetDetail();
|
PrcPriceSetDetail d = new PrcPriceSetDetail();
|
||||||
d.setPriceSetId(priceSetId);
|
d.setPriceSetId(priceSetId);
|
||||||
d.setPsvId(psvId);
|
d.setPsvId(psvId);
|
||||||
d.setProductCode(productCode);
|
d.setProductCode(trimToLen(productCode, 20));
|
||||||
d.setProductName(productName);
|
d.setProductName(trimToLen(productName, 50));
|
||||||
d.setCountryCode(row.getCountryCode());
|
d.setCountryCode(row.getCountryCode());
|
||||||
d.setCountryName(row.getCountryName());
|
d.setCountryName(trimToLen(row.getCountryName(), 50));
|
||||||
d.setZoneId(row.getZoneId());
|
d.setZoneId(row.getZoneId());
|
||||||
d.setZoneCode(row.getZoneCode());
|
d.setZoneCode(row.getZoneCode());
|
||||||
d.setZoneName(row.getZoneName());
|
d.setZoneName(trimToLen(row.getZoneName(), 100));
|
||||||
d.setWeightMin(row.getWeightMin());
|
d.setWeightMin(row.getWeightMin());
|
||||||
d.setWeightMax(row.getWeightMax());
|
d.setWeightMax(row.getWeightMax());
|
||||||
d.setWeightLabel(formatWeightLabel(row.getWeightMin(), row.getWeightMax()));
|
d.setWeightLabel(trimToLen(formatWeightLabel(row.getWeightMin(), row.getWeightMax()), 50));
|
||||||
d.setProductCountryId(row.getDetailId());
|
d.setProductCountryId(row.getDetailId());
|
||||||
d.setFvValue(freight);
|
d.setFvValue(freight);
|
||||||
d.setFvRegistrationFee(registration);
|
d.setFvRegistrationFee(registration);
|
||||||
@@ -1105,6 +1111,20 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String trimToLen(String s, int max)
|
||||||
|
{
|
||||||
|
if (s == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String t = s.trim();
|
||||||
|
if (t.length() <= max)
|
||||||
|
{
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return t.substring(0, max);
|
||||||
|
}
|
||||||
|
|
||||||
private void reportImportProgress(String taskId, Long priceSetId, int percent, String message)
|
private void reportImportProgress(String taskId, Long priceSetId, int percent, String message)
|
||||||
{
|
{
|
||||||
if (StringUtils.isEmpty(taskId) || saveAsyncRunner == null)
|
if (StringUtils.isEmpty(taskId) || saveAsyncRunner == null)
|
||||||
@@ -2136,12 +2156,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
private void saveOrUpdateVersion(PrcPriceSet set)
|
private void saveOrUpdateVersion(PrcPriceSet set)
|
||||||
{
|
{
|
||||||
PrcPriceSetVersion version = resolveSaveVersion(set);
|
PrcPriceSetVersion version = resolveSaveVersion(set);
|
||||||
boolean pullLibrary = "Y".equalsIgnoreCase(set.getPullFromLibrary());
|
// null details = leave existing rows alone; empty list = clear / replace with nothing.
|
||||||
// null details = leave existing rows alone; empty list = clear / replace with nothing
|
// Library pull must NOT run here — only via pullFromLibrary() / Excel import on Step 2.
|
||||||
boolean detailsProvided = set.getDetails() != null;
|
boolean detailsProvided = set.getDetails() != null;
|
||||||
List<PrcPriceSetDetail> details = filterDetails(set.getDetails());
|
List<PrcPriceSetDetail> details = filterDetails(set.getDetails());
|
||||||
// Allow creating an empty draft version when client sends version (Step1 -> Step2).
|
// Allow creating an empty draft version when client sends version (Step1 -> Step2).
|
||||||
if (version == null && !detailsProvided && !pullLibrary)
|
if (version == null && !detailsProvided)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2192,11 +2212,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
|
|||||||
versionMapper.updatePrcPriceSetVersion(version);
|
versionMapper.updatePrcPriceSetVersion(version);
|
||||||
}
|
}
|
||||||
set.setVersion(version);
|
set.setVersion(version);
|
||||||
if (pullLibrary)
|
if (detailsProvided)
|
||||||
{
|
|
||||||
replaceVersionDetailsFromLibrary(set, version);
|
|
||||||
}
|
|
||||||
else if (detailsProvided)
|
|
||||||
{
|
{
|
||||||
upsertVersionDetails(set, version, details);
|
upsertVersionDetails(set, version, details);
|
||||||
reportSaveProgress(set, 85, "saving surcharges");
|
reportSaveProgress(set, 85, "saving surcharges");
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- Widen prc_price_set_detail.product_code to match product / set_product (varchar 20).
|
||||||
|
ALTER TABLE prc_price_set_detail
|
||||||
|
MODIFY COLUMN product_code varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品代码';
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
:products="products"
|
:products="products"
|
||||||
:price-set-id="priceSetId"
|
:price-set-id="priceSetId"
|
||||||
:psv-id="version.psvId"
|
:psv-id="version.psvId"
|
||||||
|
@update:psv-id="v => { if (v != null && v !== '') version.psvId = v }"
|
||||||
:set-type="setType"
|
:set-type="setType"
|
||||||
:effective-time="version.effectiveTime"
|
:effective-time="version.effectiveTime"
|
||||||
:version-no="version.versionNo"
|
:version-no="version.versionNo"
|
||||||
|
|||||||
@@ -6,34 +6,19 @@
|
|||||||
{{ headerTitle }}
|
{{ headerTitle }}
|
||||||
<span class="sub">{{ typeHint }}</span>
|
<span class="sub">{{ typeHint }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
<div class="card-actions">
|
||||||
|
<el-button size="small" :disabled="!priceSetId || pulling" @click="handlePullLibrary">拉取产品列表</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="isFixedType(setType)"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
:disabled="!priceSetId || pulling"
|
||||||
|
@click="openImport"
|
||||||
|
>导入 Excel 建价</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-if="showBuildActions" class="build-choice">
|
|
||||||
<p class="build-choice__tip">暂无套餐销售价,请选择一种建价方式</p>
|
|
||||||
<div class="build-choice__cards">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="build-card build-card--pull"
|
|
||||||
:disabled="!priceSetId"
|
|
||||||
@click="handlePullLibrary"
|
|
||||||
>
|
|
||||||
<div class="build-card__title">拉取产品列表</div>
|
|
||||||
<div class="build-card__desc">按产品库基础价生成本版本套餐销售价</div>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="build-card build-card--import"
|
|
||||||
:disabled="!priceSetId || !isFixedType(setType)"
|
|
||||||
@click="openImport"
|
|
||||||
>
|
|
||||||
<div class="build-card__title">导入 Excel 建价</div>
|
|
||||||
<div class="build-card__desc">上传威狮B2C小包价表,直接生成套餐销售价</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<el-input v-model="query.keyword" clearable :placeholder="phKeyword" style="width: 180px" />
|
<el-input v-model="query.keyword" clearable :placeholder="phKeyword" style="width: 180px" />
|
||||||
<el-select v-model="query.taxMode" clearable :placeholder="phTax" style="width: 140px">
|
<el-select v-model="query.taxMode" clearable :placeholder="phTax" style="width: 140px">
|
||||||
@@ -42,7 +27,12 @@
|
|||||||
<el-button size="small" @click="noop">查询</el-button>
|
<el-button size="small" @click="noop">查询</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :data="filteredProducts" border>
|
<el-empty
|
||||||
|
v-if="!(filteredProducts && filteredProducts.length)"
|
||||||
|
:description="emptyTip"
|
||||||
|
:image-size="80"
|
||||||
|
/>
|
||||||
|
<el-table v-else :data="filteredProducts" border>
|
||||||
<el-table-column :label="colCode" min-width="80">
|
<el-table-column :label="colCode" min-width="80">
|
||||||
<template #default="{ row }"><b>{{ row.productCode }}</b></template>
|
<template #default="{ row }"><b>{{ row.productCode }}</b></template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -55,11 +45,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="colRegion" prop="destRegion" width="90" align="center" />
|
<el-table-column :label="colRegion" prop="destRegion" width="90" align="center" />
|
||||||
<!-- <el-table-column :label="colTax" width="90" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<dict-tag :options="bms_tax_mode" :value="row.taxMode" />
|
|
||||||
</template>
|
|
||||||
</el-table-column> -->
|
|
||||||
<el-table-column :label="colAction" width="220" align="center" fixed="right">
|
<el-table-column :label="colAction" width="220" align="center" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button link type="primary" @click="openPrice(row)">编辑价格</el-button>
|
<el-button link type="primary" @click="openPrice(row)">编辑价格</el-button>
|
||||||
@@ -68,7 +53,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</template>
|
|
||||||
|
|
||||||
<surcharge-picker-dialog
|
<surcharge-picker-dialog
|
||||||
v-model="scVisible"
|
v-model="scVisible"
|
||||||
@@ -156,6 +140,7 @@ const props = defineProps({
|
|||||||
headerTitle: { type: String, default: '第 2 步 · 编辑产品价格' }
|
headerTitle: { type: String, default: '第 2 步 · 编辑产品价格' }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:psvId'])
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
const { bms_tax_mode } = useDict('bms_tax_mode')
|
const { bms_tax_mode } = useDict('bms_tax_mode')
|
||||||
const { hydrateRow } = useDetailCascade(props.refData)
|
const { hydrateRow } = useDetailCascade(props.refData)
|
||||||
@@ -204,9 +189,29 @@ const importDesc = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const typeHint = computed(() => '· 当前:' + setTypeLabel(props.setType))
|
const typeHint = computed(() => '· 当前:' + setTypeLabel(props.setType))
|
||||||
|
const emptyTip = '暂无产品,请先拉取产品列表或导入 Excel 建价'
|
||||||
|
|
||||||
/** Show pull/import when this version has no set-product rows yet. */
|
|
||||||
const showBuildActions = computed(() => !(props.products || []).some(p => p && p.productCode))
|
/** Resolve version id: prop first, else reload package current version. */
|
||||||
|
const resolvedPsvId = ref(props.psvId)
|
||||||
|
watch(() => props.psvId, v => { if (v != null && v !== '') resolvedPsvId.value = v })
|
||||||
|
|
||||||
|
async function resolvePsvId(force) {
|
||||||
|
if (!force && resolvedPsvId.value != null && resolvedPsvId.value !== '') {
|
||||||
|
return resolvedPsvId.value
|
||||||
|
}
|
||||||
|
if (!props.priceSetId) return null
|
||||||
|
const res = await getPriceSet(props.priceSetId)
|
||||||
|
const data = res.data || {}
|
||||||
|
const versions = data.versions || []
|
||||||
|
const fromData = (data.version && data.version.psvId) ? data.version : null
|
||||||
|
const current = fromData || versions[0] || {}
|
||||||
|
if (current.psvId != null) {
|
||||||
|
resolvedPsvId.value = current.psvId
|
||||||
|
emit('update:psvId', current.psvId)
|
||||||
|
}
|
||||||
|
return resolvedPsvId.value || null
|
||||||
|
}
|
||||||
|
|
||||||
/** productCode -> detail rows (loaded on demand) */
|
/** productCode -> detail rows (loaded on demand) */
|
||||||
const detailByProduct = reactive({})
|
const detailByProduct = reactive({})
|
||||||
@@ -218,8 +223,9 @@ function clearDetailCache() {
|
|||||||
async function loadProductDetails(productCode, force) {
|
async function loadProductDetails(productCode, force) {
|
||||||
if (!productCode) return []
|
if (!productCode) return []
|
||||||
if (!force && detailByProduct[productCode]) return detailByProduct[productCode]
|
if (!force && detailByProduct[productCode]) return detailByProduct[productCode]
|
||||||
if (!props.psvId) return []
|
const psvId = await resolvePsvId(false)
|
||||||
const res = await listSetDetailByProduct(props.psvId, productCode)
|
if (!psvId) return []
|
||||||
|
const res = await listSetDetailByProduct(psvId, productCode)
|
||||||
const rows = (res.data || []).map(d => hydrateRow(d, props.setType))
|
const rows = (res.data || []).map(d => hydrateRow(d, props.setType))
|
||||||
detailByProduct[productCode] = rows
|
detailByProduct[productCode] = rows
|
||||||
// keep parent details array in sync for any legacy callers
|
// keep parent details array in sync for any legacy callers
|
||||||
@@ -258,7 +264,7 @@ const pulling = ref(false)
|
|||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
|
|
||||||
function openImport() {
|
async function openImport() {
|
||||||
if (!props.priceSetId) {
|
if (!props.priceSetId) {
|
||||||
proxy.$modal.msgWarning('缺少套餐ID')
|
proxy.$modal.msgWarning('缺少套餐ID')
|
||||||
return
|
return
|
||||||
@@ -267,6 +273,11 @@ function openImport() {
|
|||||||
proxy.$modal.msgWarning('仅固定价套餐支持导入')
|
proxy.$modal.msgWarning('仅固定价套餐支持导入')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const psvId = await resolvePsvId(true)
|
||||||
|
if (!psvId) {
|
||||||
|
proxy.$modal.msgWarning('缺少套餐版本,请先保存基本信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
importRef.value && importRef.value.open()
|
importRef.value && importRef.value.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +295,7 @@ async function runImportAsync(uploadFile) {
|
|||||||
try {
|
try {
|
||||||
const { task } = await importSetDetailPricesAsync(
|
const { task } = await importSetDetailPricesAsync(
|
||||||
raw,
|
raw,
|
||||||
{ priceSetId: props.priceSetId, psvId: props.psvId },
|
{ priceSetId: props.priceSetId, psvId: await resolvePsvId(false) },
|
||||||
applyBuildProgress
|
applyBuildProgress
|
||||||
)
|
)
|
||||||
importProgressPercent.value = 100
|
importProgressPercent.value = 100
|
||||||
@@ -336,9 +347,14 @@ async function reloadDetailsFromServer() {
|
|||||||
const res = await getPriceSet(props.priceSetId)
|
const res = await getPriceSet(props.priceSetId)
|
||||||
const data = res.data || {}
|
const data = res.data || {}
|
||||||
const versions = data.versions || []
|
const versions = data.versions || []
|
||||||
const qPsv = props.psvId
|
const qPsv = resolvedPsvId.value || props.psvId
|
||||||
const byQuery = qPsv != null ? versions.find(v => String(v.psvId) === String(qPsv)) : null
|
const byQuery = qPsv != null ? versions.find(v => String(v.psvId) === String(qPsv)) : null
|
||||||
const current = byQuery || data.version || versions[0] || {}
|
const fromData = (data.version && data.version.psvId) ? data.version : null
|
||||||
|
const current = byQuery || fromData || versions[0] || {}
|
||||||
|
if (current.psvId != null) {
|
||||||
|
resolvedPsvId.value = current.psvId
|
||||||
|
emit('update:psvId', current.psvId)
|
||||||
|
}
|
||||||
if (Array.isArray(props.details)) {
|
if (Array.isArray(props.details)) {
|
||||||
props.details.splice(0, props.details.length)
|
props.details.splice(0, props.details.length)
|
||||||
}
|
}
|
||||||
@@ -387,7 +403,7 @@ async function pullAllFromLibrary(opts = {}) {
|
|||||||
pulling.value = true
|
pulling.value = true
|
||||||
try {
|
try {
|
||||||
const { task } = await pullSetDetailsFromLibraryAsync(
|
const { task } = await pullSetDetailsFromLibraryAsync(
|
||||||
{ priceSetId: props.priceSetId, psvId: props.psvId, force },
|
{ priceSetId: props.priceSetId, psvId: await resolvePsvId(false), force },
|
||||||
applyBuildProgress
|
applyBuildProgress
|
||||||
)
|
)
|
||||||
importProgressPercent.value = 100
|
importProgressPercent.value = 100
|
||||||
@@ -691,57 +707,11 @@ defineExpose({ pullAllFromLibrary })
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
.build-choice {
|
.card-actions {
|
||||||
padding: 48px 24px 56px;
|
flex: none;
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.build-choice__tip {
|
|
||||||
margin: 0 0 28px;
|
|
||||||
color: #909399;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
.build-choice__cards {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
gap: 20px;
|
gap: 8px;
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.build-card {
|
|
||||||
width: 280px;
|
|
||||||
min-height: 120px;
|
|
||||||
padding: 24px 20px;
|
|
||||||
border: 1px solid #e4e7ed;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: left;
|
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
|
||||||
}
|
|
||||||
.build-card:hover:not(:disabled) {
|
|
||||||
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
|
|
||||||
}
|
|
||||||
.build-card:disabled {
|
|
||||||
opacity: 0.55;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.build-card--pull:hover:not(:disabled) {
|
|
||||||
border-color: #67c23a;
|
|
||||||
}
|
|
||||||
.build-card--import:hover:not(:disabled) {
|
|
||||||
border-color: #409eff;
|
|
||||||
}
|
|
||||||
.build-card__title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #303133;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.build-card--pull .build-card__title { color: #67c23a; }
|
|
||||||
.build-card--import .build-card__title { color: #409eff; }
|
|
||||||
.build-card__desc {
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.6;
|
|
||||||
color: #909399;
|
|
||||||
}
|
}
|
||||||
.filters {
|
.filters {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
: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"
|
||||||
:set-type="form.basic.setType"
|
:set-type="form.basic.setType"
|
||||||
:version-no="form.version.versionNo"
|
:version-no="form.version.versionNo"
|
||||||
:ref-data="refData"
|
:ref-data="refData"
|
||||||
@@ -141,7 +142,7 @@ async function runSaveWithProgress(payload) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Step1 -> Step2: create empty draft version; pull/import on Step 2 */
|
/** Step1 -> Step2: create empty draft version only; products/details persist on pull or Excel import */
|
||||||
async function goStep2() {
|
async function goStep2() {
|
||||||
const validBasic = await basicRef.value.validate()
|
const validBasic = await basicRef.value.validate()
|
||||||
if (!validBasic) return
|
if (!validBasic) return
|
||||||
@@ -153,10 +154,31 @@ async function goStep2() {
|
|||||||
|
|
||||||
creating.value = true
|
creating.value = true
|
||||||
try {
|
try {
|
||||||
const priceSetId = await runSaveWithProgress(buildPayload())
|
// Never auto-pull product library on enter Step2 (even if old clients sent the flag).
|
||||||
|
const payload = buildPayload()
|
||||||
|
delete payload.pullFromLibrary
|
||||||
|
delete payload.forcePullLibrary
|
||||||
|
delete payload.details
|
||||||
|
const priceSetId = await runSaveWithProgress(payload)
|
||||||
form.priceSetId = priceSetId
|
form.priceSetId = priceSetId
|
||||||
const fresh = await getPriceSet(priceSetId)
|
const fresh = await getPriceSet(priceSetId)
|
||||||
applySetData(fresh.data || {}, false)
|
applySetData(fresh.data || {}, false)
|
||||||
|
form.details = []
|
||||||
|
form.products = []
|
||||||
|
if (!form.version.psvId) {
|
||||||
|
const versions = (fresh.data && fresh.data.versions) || []
|
||||||
|
const v = (fresh.data && fresh.data.version && fresh.data.version.psvId)
|
||||||
|
? fresh.data.version
|
||||||
|
: versions[0]
|
||||||
|
if (v && v.psvId) {
|
||||||
|
form.version.psvId = v.psvId
|
||||||
|
form.version.versionNo = v.versionNo || form.version.versionNo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!form.version.psvId) {
|
||||||
|
proxy.$modal.msgError('套餐版本创建失败,请重试')
|
||||||
|
return
|
||||||
|
}
|
||||||
active.value = 1
|
active.value = 1
|
||||||
proxy.$modal.msgSuccess('已创建空套餐版本,请在第 2 步拉取或导入价格')
|
proxy.$modal.msgSuccess('已创建空套餐版本,请在第 2 步拉取或导入价格')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -241,6 +263,10 @@ function goBack() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPsvIdUpdate(psvId) {
|
||||||
|
if (psvId != null && psvId !== '') form.version.psvId = psvId
|
||||||
|
}
|
||||||
|
|
||||||
function applySetData(data, asCopy) {
|
function applySetData(data, asCopy) {
|
||||||
form.priceSetId = data.priceSetId
|
form.priceSetId = data.priceSetId
|
||||||
Object.assign(form.basic, {
|
Object.assign(form.basic, {
|
||||||
@@ -252,7 +278,8 @@ function applySetData(data, asCopy) {
|
|||||||
const versions = data.versions || []
|
const versions = data.versions || []
|
||||||
const qPsv = route.query.psvId
|
const qPsv = route.query.psvId
|
||||||
const byQuery = qPsv != null ? versions.find(v => String(v.psvId) === String(qPsv)) : null
|
const byQuery = qPsv != null ? versions.find(v => String(v.psvId) === String(qPsv)) : null
|
||||||
const current = byQuery || data.version || versions[0] || {}
|
const fromData = (data.version && data.version.psvId) ? data.version : null
|
||||||
|
const current = byQuery || fromData || versions[0] || {}
|
||||||
if (asCopy) {
|
if (asCopy) {
|
||||||
form.version.psvId = undefined
|
form.version.psvId = undefined
|
||||||
form.version.versionNo = nextVersionNo(versions)
|
form.version.versionNo = nextVersionNo(versions)
|
||||||
|
|||||||
Reference in New Issue
Block a user