From 1eb0e45513a1cef5ebbebf62e2fdf26bb71fefcc Mon Sep 17 00:00:00 2001
From: wuweihua <88888888>
Date: Wed, 28 Oct 2026 21:42:45 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BB=BA=E7=AB=8B=E6=96=B0?=
=?UTF-8?q?=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bms/price/domain/PrcPriceSetVersion.java | 5 +-
.../service/impl/PrcPriceSetServiceImpl.java | 4 +-
bms-back/sql/bms_init.sql | 2 +-
...ice_set_version_price_build_mode_alter.sql | 2 +-
.../set/components/create/PriceEditPanel.vue | 3 ++
.../set/components/create/StepDetails.vue | 54 ++++++++++++++-----
bms-front/src/views/price/set/create.vue | 48 +++++++++++++----
.../src/views/price/set/utils/priceSet.js | 19 ++++++-
8 files changed, 109 insertions(+), 28 deletions(-)
diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersion.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersion.java
index e69aa7ea..96255825 100644
--- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersion.java
+++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetVersion.java
@@ -29,7 +29,10 @@ public class PrcPriceSetVersion extends BaseEntity
/** 0 draft / 3 pending / 1 active / 2 expired */
private String status;
/**
- * Create-price mode for this version: PULL = from product library, IMPORT = Excel.
+ * Create-price mode for this version:
+ * PULL = from product library,
+ * IMPORT = Excel with prior version preload,
+ * IMPORT_FRESH = Excel without prior version data.
* Column: price_build_mode
*/
private String priceBuildMode;
diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java
index 5e66c17b..dc84b7b4 100644
--- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java
+++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java
@@ -3361,9 +3361,10 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
}
/**
- * Draft only: when priceBuildMode changes (PULL/IMPORT), delete this version's
+ * Draft only: when priceBuildMode changes (PULL/IMPORT/IMPORT_FRESH), delete this version's
* product / detail / SALES surcharge rel rows. Same mode or non-draft: no-op.
* IMPORT: after wipe, copy products+details from latest prior version (if any).
+ * IMPORT_FRESH: wipe only, do not copy prior version (blank Excel build).
*/
private void clearDraftVersionChildrenIfBuildModeChanged(PrcPriceSetVersion db, PrcPriceSetVersion incoming)
{
@@ -3387,6 +3388,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
}
Long psvId = db.getPsvId();
wipeVersionChildren(psvId, db.getStatus());
+ // IMPORT inherits prior version; IMPORT_FRESH starts empty for Excel import
if ("IMPORT".equalsIgnoreCase(newMode))
{
copyLatestPriorVersionChildren(db.getPriceSetId(), psvId);
diff --git a/bms-back/sql/bms_init.sql b/bms-back/sql/bms_init.sql
index 09709539..cb20c5fc 100644
--- a/bms-back/sql/bms_init.sql
+++ b/bms-back/sql/bms_init.sql
@@ -1314,7 +1314,7 @@ CREATE TABLE `prc_price_set_version` (
`effective_time` datetime DEFAULT NULL COMMENT '生效时间',
`expire_time` datetime DEFAULT NULL COMMENT '失效时间(空=长期;下一版本启动时写入该启动时间)',
`status` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '0 draft 3 pending 1 effect 2 expired',
- `price_build_mode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建价格方式 PULL=拉取产品库 IMPORT=Excel导入',
+ `price_build_mode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建价格方式 PULL=拉取产品库 IMPORT=Excel继承上一版 IMPORT_FRESH=Excel重新建价',
`is_del` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '是否删除 0未删除 1已删除',
`created_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
diff --git a/bms-back/sql/price/prc_price_set_version_price_build_mode_alter.sql b/bms-back/sql/price/prc_price_set_version_price_build_mode_alter.sql
index 4f3751a7..5f20dac9 100644
--- a/bms-back/sql/price/prc_price_set_version_price_build_mode_alter.sql
+++ b/bms-back/sql/price/prc_price_set_version_price_build_mode_alter.sql
@@ -1,3 +1,3 @@
-- Add create-price mode on package version: PULL / IMPORT
ALTER TABLE prc_price_set_version
- ADD COLUMN price_build_mode varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建价格方式 PULL=拉取产品库 IMPORT=Excel导入' AFTER status;
+ ADD COLUMN price_build_mode varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建价格方式 PULL=拉取产品库 IMPORT=Excel继承上一版 IMPORT_FRESH=Excel重新建价' AFTER status;
diff --git a/bms-front/src/views/price/set/components/create/PriceEditPanel.vue b/bms-front/src/views/price/set/components/create/PriceEditPanel.vue
index 3692239d..9cd0f36b 100644
--- a/bms-front/src/views/price/set/components/create/PriceEditPanel.vue
+++ b/bms-front/src/views/price/set/components/create/PriceEditPanel.vue
@@ -201,6 +201,9 @@ async function finish() {
// Backend copies latest prior version products/details into this draft
await reloadProductsAfterImportModeSave()
proxy.$modal.msgSuccess("建价方式已保存,已带出上一版产品,可导入 Excel 按产品+国家覆盖或新增")
+ } else if (version.priceBuildMode === 'IMPORT_FRESH') {
+ savedProductsSnapshot.value = (products.value || []).map(p => ({ ...p }))
+ proxy.$modal.msgSuccess("建价方式已保存并清空旧明细,可直接导入 Excel 建价(不延用以前的产品价格)")
} else {
savedProductsSnapshot.value = (products.value || []).map(p => ({ ...p }))
proxy.$modal.msgSuccess("建价方式已保存并清空旧明细,可导入 Excel 建价")
diff --git a/bms-front/src/views/price/set/components/create/StepDetails.vue b/bms-front/src/views/price/set/components/create/StepDetails.vue
index 2be973d5..9a42c765 100644
--- a/bms-front/src/views/price/set/components/create/StepDetails.vue
+++ b/bms-front/src/views/price/set/components/create/StepDetails.vue
@@ -51,7 +51,7 @@
type="button"
class="build-card build-card--pull"
:class="{ selected: isPullBuildMode(priceBuildMode) }"
- :disabled="buildModeLocked || choosingMode"
+ :disabled="!canChooseBuildMode"
@click="chooseBuildMode('PULL')"
>
按产品价建价
@@ -61,11 +61,21 @@
type="button"
class="build-card build-card--import"
:class="{ selected: isImportBuildMode(priceBuildMode) }"
- :disabled="buildModeLocked || !isFixedType(setType) || choosingMode"
+ :disabled="!canChooseBuildMode || !isFixedType(setType)"
@click="chooseBuildMode('IMPORT')"
>
- Excel 导入建价
- 上传威狮 B2C 小包价表,直接生成套餐销售价
+ 按上一版 Excel 建价
+ 延用以前的产品价格,带出上一版后再导入 Excel 按产品+国家覆盖或新增
+
+
@@ -207,7 +217,9 @@ import {
isBuildModeLockedVersion,
isDiscountType,
isFixedType,
+ isExcelBuildMode,
isImportBuildMode,
+ isImportFreshBuildMode,
isPullBuildMode,
pickLatestPriorVersion,
setTypeLabel,
@@ -233,7 +245,9 @@ const props = defineProps({
/** Version status: draft can change build mode; started versions lock it */
versionStatus: { type: [String, Number], default: undefined },
/** True when build mode changed locally and not saved yet */
- buildModeDirty: { type: Boolean, default: false }
+ buildModeDirty: { type: Boolean, default: false },
+ /** False while parent still loading package data — avoid first click being wiped by applySetData */
+ buildModeReady: { type: Boolean, default: true }
})
const emit = defineEmits(['update:psvId', 'update:priceBuildMode', 'update:pefId'])
@@ -279,7 +293,7 @@ const regionOptions = ref([])
const countryImportDesc = [
'选择目的国家后上传专线成本价表(含「产品目录」),目录中的产品都会记到该国家。',
'按产品+国家覆盖:仅替换已选国家的明细,其它产品/国家保留。',
- '邮编、赔偿标准等 sheet 不导入。仅套餐「Excel 导入建价」可用。'
+ '邮编、赔偿标准等 sheet 不导入。仅套餐 Excel 建价方式可用。'
]
const importProgressVisible = ref(false)
const importProgressPercent = ref(0)
@@ -287,16 +301,19 @@ const importProgressMessage = ref('')
const importProgressStatus = ref(undefined)
const progressDialogTitle = ref('正在导入价格')
const hasBuildMode = computed(() => {
- if (isPullBuildMode(props.priceBuildMode) || isImportBuildMode(props.priceBuildMode)) return true
+ if (isPullBuildMode(props.priceBuildMode) || isExcelBuildMode(props.priceBuildMode)) return true
// Legacy / already has products: treat as build mode ready
return (props.products || []).some(p => p && p.productCode)
})
/** Started (non-draft) versions: show cards but disable switching */
const buildModeLocked = computed(() => isBuildModeLockedVersion(props.versionStatus))
const choosingMode = ref(false)
+const canChooseBuildMode = computed(() =>
+ !!props.buildModeReady && !buildModeLocked.value && !choosingMode.value
+)
const isModifyImport = computed(() => {
if (isPullBuildMode(props.priceBuildMode)) return true
- if (isImportBuildMode(props.priceBuildMode)) return false
+ if (isExcelBuildMode(props.priceBuildMode)) return false
return (props.products || []).some(p => p && p.productCode)
})
const importActionLabel = computed(() =>
@@ -311,27 +328,36 @@ const importDesc = computed(() => {
'仅固定价套餐可导入;未匹配行不会新增明细。'
]
}
+ if (isImportFreshBuildMode(props.priceBuildMode)) {
+ return [
+ '适用威狮B2C小包价表(含「产品目录」)。',
+ '不延用以前的产品价格;导入按「产品+国家」写入套餐销售价,直接生成产品明细。',
+ '同一产品下重复国家+重量段将整单失败。仅固定价套餐「Excel 重新建价」可用。'
+ ]
+ }
return [
'适用威狮B2C小包价表(含「产品目录」)。',
- '先带出上一版全部产品;导入按「产品+国家」匹配:命中则覆盖该国家明细,未命中则新增;其它产品/国家保留。',
- '同一产品下重复国家+重量段将整单失败。仅固定价套餐「导入 Excel 建价」可用。'
+ '延用以前的产品价格:先带出上一版全部产品;导入按「产品+国家」匹配:命中则覆盖该国家明细,未命中则新增;其它产品/国家保留。',
+ '同一产品下重复国家+重量段将整单失败。仅固定价套餐「按上一版 Excel 建价」可用。'
]
})
const typeHint = computed(() => {
if (isPullBuildMode(props.priceBuildMode)) return '· 按产品价建价'
- if (isImportBuildMode(props.priceBuildMode)) return '· Excel 导入'
+ if (isImportBuildMode(props.priceBuildMode)) return '· 按上一版 Excel 建价'
+ if (isImportFreshBuildMode(props.priceBuildMode)) return '· Excel 重新建价'
if (hasBuildMode.value) return '· 当前:' + setTypeLabel(props.setType)
return '· 请选择创建价格方式'
})
// PULL: hide pull btn (already pulled), show modify-import.
-// IMPORT: show create-import.
+// IMPORT / IMPORT_FRESH: show create-import.
const showPullAction = computed(() => false)
const showImportAction = computed(() => hasBuildMode.value && isFixedType(props.setType))
const emptyTip = computed(() => {
if (props.livePersist) return '暂无产品'
if (isPullBuildMode(props.priceBuildMode)) return '暂无产品,请先返回上一步重新拉取或等待拉取完成'
if (isImportBuildMode(props.priceBuildMode)) return '暂无上一版产品,请点击右上角导入 Excel 建价(按产品+国家覆盖或新增)'
+ if (isImportFreshBuildMode(props.priceBuildMode)) return '暂无产品,请点击右上角导入 Excel 建价'
return '暂无产品,请先选择创建价格方式'
})
@@ -467,7 +493,7 @@ const pulling = ref(false)
function noop() {}
async function chooseBuildMode(mode) {
- if (!mode || choosingMode.value || buildModeLocked.value) return
+ if (!mode || !canChooseBuildMode.value) return
if (mode === props.priceBuildMode) return
choosingMode.value = true
try {
@@ -1103,6 +1129,7 @@ defineExpose({ pullAllFromLibrary, clearLocalDraft, loadLatestPriorVersionDraft
}
.build-card--pull:hover:not(:disabled) { border-color: #67c23a; }
.build-card--import:hover:not(:disabled) { border-color: #409eff; }
+.build-card--import-fresh:hover:not(:disabled) { border-color: #e6a23c; }
.build-card__title {
font-size: 16px;
font-weight: 600;
@@ -1111,6 +1138,7 @@ defineExpose({ pullAllFromLibrary, clearLocalDraft, loadLatestPriorVersionDraft
}
.build-card--pull .build-card__title { color: #67c23a; }
.build-card--import .build-card__title { color: #409eff; }
+.build-card--import-fresh .build-card__title { color: #e6a23c; }
.build-card__desc {
font-size: 13px;
line-height: 1.6;
diff --git a/bms-front/src/views/price/set/create.vue b/bms-front/src/views/price/set/create.vue
index a8810aa0..df56f312 100644
--- a/bms-front/src/views/price/set/create.vue
+++ b/bms-front/src/views/price/set/create.vue
@@ -21,6 +21,7 @@
:ref-data="refData"
:live-persist="livePersist"
:build-mode-dirty="buildModeDirty"
+ :build-mode-ready="pageReady"
:header-title="detailsHeaderTitle"
/>
@@ -100,6 +101,8 @@ const savedProductsSnapshot = ref([])
const buildModeDirty = computed(() =>
String(form.version.priceBuildMode || '') !== String(savedPriceBuildMode.value || '')
)
+/** Gate build-mode cards until package payload applied (avoids first click being wiped). */
+const pageReady = ref(!route.query.priceSetId)
function mapSaveProgressMessage(message) {
const m = message || ''
@@ -182,6 +185,9 @@ async function finishEdit() {
// Backend copied prior version into this draft; refresh product list from current psv
await refreshProductsFromCurrentVersion()
proxy.$modal.msgSuccess("建价方式已保存,已带出上一版产品")
+ } else if (form.version.priceBuildMode === 'IMPORT_FRESH') {
+ savedProductsSnapshot.value = (form.products || []).map(p => ({ ...p }))
+ proxy.$modal.msgSuccess("建价方式已保存,可直接导入 Excel 建价(不延用以前的产品价格)")
} else {
savedProductsSnapshot.value = (form.products || []).map(p => ({ ...p }))
proxy.$modal.msgSuccess("建价方式已保存")
@@ -335,7 +341,7 @@ function onPefIdUpdate(pefId) {
}
}
-/** Step2 chose PULL / IMPORT: edit live-saves mode; create only local + optional preview pull. */
+/** Step2 chose PULL / IMPORT / IMPORT_FRESH: edit live-saves mode; create only local + optional preview pull. */
function onPriceBuildModeUpdate(mode) {
if (!mode) return
if (isBuildModeLockedVersion(form.version.status)) return
@@ -346,7 +352,7 @@ function onPriceBuildModeUpdate(mode) {
savedProductsSnapshot.value = (form.products || []).map(p => ({ ...p }))
}
form.version.priceBuildMode = mode
- if (mode !== 'IMPORT') {
+ if (mode !== 'IMPORT' && mode !== 'IMPORT_FRESH') {
draftPefId.value = undefined
draftPefIds.value = []
}
@@ -403,6 +409,8 @@ function onPriceBuildModeUpdate(mode) {
proxy.$modal.msgSuccess("请导入 Excel 建价")
}
})
+ } else if (mode === 'IMPORT_FRESH') {
+ proxy.$modal.msgSuccess("请直接导入 Excel 建价(不延用以前的产品价格)")
}
}
@@ -448,31 +456,48 @@ function applySetData(data, asCopy, asNewVersion) {
const fromData = (data.version && data.version.psvId) ? data.version : null
const current = byQuery || fromData || versions[0] || {}
if (asCopy) {
+ const keptMode = form.version.priceBuildMode
+ const keptProducts = keptMode ? form.products.slice() : []
+ const keptDetails = keptMode ? form.details.slice() : []
form.version.psvId = undefined
form.version.versionNo = nextVersionNo(versions)
form.version.effectiveTime = undefined
form.version.expireTime = undefined
form.version.status = undefined
- form.version.priceBuildMode = undefined
+ form.version.priceBuildMode = keptMode || undefined
draftPefId.value = undefined
draftPefIds.value = []
savedPriceBuildMode.value = undefined
- form.details = []
- form.products = []
+ if (keptMode) {
+ form.products.splice(0, form.products.length, ...keptProducts)
+ form.details.splice(0, form.details.length, ...keptDetails)
+ } else {
+ form.details = []
+ form.products = []
+ }
} else if (asNewVersion) {
// Draft is created on finish only. Resume only when query has psvId (legacy empty draft).
+ // Keep mode/products if user already chose a build mode before this response landed.
+ const keptMode = form.version.priceBuildMode
+ const keptProducts = keptMode ? form.products.slice() : []
+ const keptDetails = keptMode ? form.details.slice() : []
const resume = qPsv != null ? byQuery : null
form.version.psvId = resume ? resume.psvId : undefined
form.version.versionNo = (resume && resume.versionNo) || nextVersionNo(versions)
form.version.effectiveTime = undefined
form.version.expireTime = undefined
form.version.status = (resume && resume.status) || '0'
- form.version.priceBuildMode = undefined
+ form.version.priceBuildMode = keptMode || undefined
draftPefId.value = undefined
draftPefIds.value = []
savedPriceBuildMode.value = undefined
- form.details = []
- form.products = []
+ if (keptMode) {
+ form.products.splice(0, form.products.length, ...keptProducts)
+ form.details.splice(0, form.details.length, ...keptDetails)
+ } else {
+ form.details = []
+ form.products = []
+ }
} else {
form.version.psvId = current.psvId
form.version.versionNo = current.versionNo || nextVersionNo(versions)
@@ -491,8 +516,11 @@ function applySetData(data, asCopy, asNewVersion) {
}
function loadExisting(id, asCopy, asNewVersion) {
- getPriceSet(id).then(res => {
+ pageReady.value = false
+ return getPriceSet(id).then(res => {
applySetData(res.data || {}, asCopy, asNewVersion)
+ }).finally(() => {
+ pageReady.value = true
})
}
@@ -501,6 +529,8 @@ onMounted(() => {
const id = route.query.priceSetId
if (id) {
loadExisting(id, copyMode.value, newVersionMode.value)
+ } else {
+ pageReady.value = true
}
})
})
diff --git a/bms-front/src/views/price/set/utils/priceSet.js b/bms-front/src/views/price/set/utils/priceSet.js
index 7b6ac931..c4c43bc4 100644
--- a/bms-front/src/views/price/set/utils/priceSet.js
+++ b/bms-front/src/views/price/set/utils/priceSet.js
@@ -12,10 +12,16 @@ export const SET_TYPE = {
DISCOUNT: 'DISCOUNT'
}
-/** Version create-price mode */
+/**
+ * Version create-price mode
+ * PULL: from product library
+ * IMPORT: Excel + preload latest prior version
+ * IMPORT_FRESH: Excel only, no prior version data
+ */
export const PRICE_BUILD_MODE = {
PULL: 'PULL',
- IMPORT: 'IMPORT'
+ IMPORT: 'IMPORT',
+ IMPORT_FRESH: 'IMPORT_FRESH'
}
export function isPullBuildMode(mode) {
@@ -26,6 +32,15 @@ export function isImportBuildMode(mode) {
return mode === PRICE_BUILD_MODE.IMPORT
}
+export function isImportFreshBuildMode(mode) {
+ return mode === PRICE_BUILD_MODE.IMPORT_FRESH
+}
+
+/** Either Excel mode (with or without prior version) */
+export function isExcelBuildMode(mode) {
+ return isImportBuildMode(mode) || isImportFreshBuildMode(mode)
+}
+
/** 套餐状态枚举:D 待启用 / A 审核中 / E 生效 / R 驳回 / S 已停用 */
export const SET_STATUS = {
DRAFT: 'D',