Compare commits

2 Commits

Author SHA1 Message Date
wuweihua 00a690abe9 Merge branch 'test' of https://git.lionquexpress.com/wuweihua/BMS into test 2026-10-28 18:10:14 +08:00
wuweihua 8ed7b3146b 修改套餐规则 2026-10-28 18:10:10 +08:00
21 changed files with 536 additions and 411 deletions
@@ -139,8 +139,8 @@ public class ProductController extends BaseController
return success();
}
/** Persist create-page preview details into draft/pending version. */
@PreAuthorize("@ss.hasPermi('price:product:add')")
/** Persist create-page preview details into draft/pending/effect version. */
@PreAuthorize("@ss.hasPermi('price:product:add') or @ss.hasPermi('price:product:edit')")
@Log(title = "product preview apply", businessType = BusinessType.UPDATE)
@PostMapping("/version/applyPreview")
public AjaxResult applyPreview(@RequestBody CatalogPreviewApplyRequest body)
@@ -149,7 +149,7 @@ public class ProductController extends BaseController
}
/** Async parallel save of create-page preview. Returns taskId; poll /import/task/{taskId}. */
@PreAuthorize("@ss.hasPermi('price:product:add')")
@PreAuthorize("@ss.hasPermi('price:product:add') or @ss.hasPermi('price:product:edit')")
@Log(title = "product preview apply", businessType = BusinessType.UPDATE)
@PostMapping("/version/applyPreviewAsync")
public AjaxResult applyPreviewAsync(@RequestBody CatalogPreviewApplyRequest body)
@@ -160,7 +160,7 @@ public class ProductController extends BaseController
return success(payload);
}
/** Save import Excel attachment onto a draft/pending version (prc_price_excel_file). */
/** Save import Excel attachment onto a draft/pending/effect version (prc_price_excel_file). */
@PreAuthorize("@ss.hasPermi('price:product:import')")
@Log(title = "product version excel", businessType = BusinessType.IMPORT)
@PostMapping("/version/{psvId}/excel")
@@ -139,8 +139,8 @@ public class RedlineProductController extends BaseController
return success();
}
/** Persist create-page preview details into draft/pending version. */
@PreAuthorize("@ss.hasPermi('price:redline:add')")
/** Persist create-page preview details into draft/pending/effect version. */
@PreAuthorize("@ss.hasPermi('price:redline:add') or @ss.hasPermi('price:redline:edit')")
@Log(title = "redline preview apply", businessType = BusinessType.UPDATE)
@PostMapping("/version/applyPreview")
public AjaxResult applyPreview(@RequestBody RedlineCatalogPreviewApplyRequest body)
@@ -149,7 +149,7 @@ public class RedlineProductController extends BaseController
}
/** Async parallel save of create-page preview. Returns taskId; poll /import/task/{taskId}. */
@PreAuthorize("@ss.hasPermi('price:redline:add')")
@PreAuthorize("@ss.hasPermi('price:redline:add') or @ss.hasPermi('price:redline:edit')")
@Log(title = "redline preview apply", businessType = BusinessType.UPDATE)
@PostMapping("/version/applyPreviewAsync")
public AjaxResult applyPreviewAsync(@RequestBody RedlineCatalogPreviewApplyRequest body)
@@ -160,7 +160,7 @@ public class RedlineProductController extends BaseController
return success(payload);
}
/** Save import Excel attachment onto a draft/pending version (prc_price_excel_file). */
/** Save import Excel attachment onto a draft/pending/effect version (prc_price_excel_file). */
@PreAuthorize("@ss.hasPermi('price:redline:import')")
@Log(title = "redline version excel", businessType = BusinessType.IMPORT)
@PostMapping("/version/{psvId}/excel")
@@ -207,7 +207,7 @@ price.set.version.effectiveTime.empty=版本生效时间不能为空
price.set.version.effectiveTime.beforePrev=生效时间不能早于上一版本的生效时间
price.set.version.notExists=套餐版本不存在
price.set.version.activate.statusNotAllowed=仅草稿或待生效版本可设置生效时间
price.set.version.delete.statusNotAllowed=仅草稿或待生效版本可删除
price.set.version.delete.statusNotAllowed=仅草稿、待生效或已生效版本可删除
price.set.version.copy.draftExists=已有草稿版本,请先启动或删除后再复制
price.set.version.create.draftExists=已有草稿版本,请先启动或删除后再新建
price.set.save.task.notExists=保存任务不存在或已过期
@@ -33,6 +33,8 @@ public class PrcPriceSetVersion extends BaseEntity
* Column: price_build_mode
*/
private String priceBuildMode;
/** 0 normal / 1 soft-deleted */
private String isDel;
private List<PrcPriceSetDetail> details;
private List<PrcPriceSetProduct> products;
@@ -30,6 +30,9 @@ public interface PrcPriceSetVersionMapper
public int deletePrcPriceSetVersionByIds(Long[] psvIds);
/** Soft-delete catalog/sales version row (is_del=1). */
public int softDeleteById(@Param("psvId") Long psvId, @Param("updateBy") String updateBy);
public int deletePrcPriceSetVersionByPriceSetId(Long priceSetId);
public List<Long> selectPriceSetIds();
@@ -48,6 +48,9 @@ public interface PrcSurchargeRelMapper
/** Hard delete surcharge rows linked to products/details under a version. */
public int physicalDeleteByPsvId(@Param("psvId") Long psvId);
/** Soft-delete surcharge rows linked to products/details under a version. */
public int softDeleteByPsvId(@Param("psvId") Long psvId);
/** 附加费版本化编辑后,把产品关联从旧记录重指向新记录 */
public int rebindRelScId(@Param("oldScId") Long oldScId, @Param("newScId") Long newScId, @Param("operator") String operator);
}
@@ -546,16 +546,29 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService
{
throw new ServiceException(MessageUtils.message("price.set.version.notExists"));
}
// Draft and pending versions may be deleted; effect/expired cannot.
if (!VER_DRAFT.equals(version.getStatus()) && !VER_PENDING.equals(version.getStatus()))
if ("1".equals(version.getIsDel()))
{
throw new ServiceException(MessageUtils.message("price.set.version.notExists"));
}
// Draft / pending: hard delete; EFFECT: soft delete then reopen previous via refreshVersionStatus
if (!VER_DRAFT.equals(version.getStatus())
&& !VER_PENDING.equals(version.getStatus())
&& !VER_EFFECT.equals(version.getStatus()))
{
throw new ServiceException(MessageUtils.message("price.set.version.delete.statusNotAllowed"));
}
requireSet(version.getPriceSetId());
// Draft / pending only reach here: hard-delete children with the version row
wipeVersionChildren(psvId, version.getStatus());
int rows = versionMapper.deletePrcPriceSetVersionByIds(new Long[] { psvId });
// Rechain expire_time / status for remaining versions (e.g. reopen previous effect window)
int rows;
if (VER_EFFECT.equals(version.getStatus()))
{
rows = versionMapper.softDeleteById(psvId, updateBy);
}
else
{
rows = versionMapper.deletePrcPriceSetVersionByIds(new Long[] { psvId });
}
// Rechain expire_time / status for remaining versions (reopen previous effect window)
refreshVersionStatus(version.getPriceSetId());
return rows;
}
@@ -193,8 +193,9 @@ public class CatalogVersionSupport
}
/**
* Hard-delete draft/pending catalog version with products, details,
* surcharge links and excel attachments.
* Delete catalog version:
* - draft / pending: hard-delete version + children + excel
* - effect: soft-delete version + products + details (+ surcharge rel)
*/
public int deleteDraftVersion(Long psvId)
{
@@ -204,9 +205,17 @@ public class CatalogVersionSupport
throw new ServiceException("版本不存在");
}
assertCatalogType(ver.getPriceType());
if ("1".equals(ver.getIsDel()))
{
throw new ServiceException("版本已删除");
}
if (STATUS_EFFECT.equals(ver.getStatus()))
{
return softDeleteEffectVersion(ver);
}
if (!STATUS_DRAFT.equals(ver.getStatus()) && !STATUS_PENDING.equals(ver.getStatus()))
{
throw new ServiceException("仅草稿或待生效版本可以删除");
throw new ServiceException("仅草稿、待生效或已生效版本可以删除");
}
surchargeRelMapper.physicalDeleteByPsvId(psvId);
detailMapper.physicalDeleteByPsvId(psvId);
@@ -215,6 +224,89 @@ public class CatalogVersionSupport
return versionMapper.deletePrcPriceSetVersionByIds(new Long[] { psvId });
}
/**
* Soft-delete EFFECT version with products and details,
* then reopen the previous version as EFFECT (clear expire_time;
* product/detail expire cleared via syncEffect).
*/
private int softDeleteEffectVersion(PrcPriceSetVersion ver)
{
Long psvId = ver.getPsvId();
String priceType = ver.getPriceType();
surchargeRelMapper.softDeleteByPsvId(psvId);
detailMapper.deletePrcPriceSetDetailByPsvId(psvId);
productMapper.deleteByPsvId(psvId);
int rows = versionMapper.softDeleteById(psvId, null);
PrcPriceSetVersion prev = findPreviousVersionToRestore(ver);
if (prev != null)
{
prev.setExpireTime(null);
prev.setStatus(STATUS_EFFECT);
versionMapper.updatePrcPriceSetVersion(prev);
}
refreshCatalogStatuses(priceType);
return rows;
}
/**
* Previous catalog version (highest version_seq below deleted) that can become EFFECT again.
* Prefers expired/effect rows that already had an effective_time.
*/
private PrcPriceSetVersion findPreviousVersionToRestore(PrcPriceSetVersion deleted)
{
if (deleted == null || deleted.getPriceType() == null)
{
return null;
}
PrcPriceSetVersion q = new PrcPriceSetVersion();
q.setPriceType(deleted.getPriceType());
List<PrcPriceSetVersion> list = versionMapper.selectPrcPriceSetVersionList(q);
if (list == null || list.isEmpty())
{
return null;
}
int curSeq = deleted.getVersionSeq() == null ? 0 : deleted.getVersionSeq().intValue();
PrcPriceSetVersion best = null;
int bestSeq = Integer.MIN_VALUE;
Date now = new Date();
for (PrcPriceSetVersion v : list)
{
if (v == null || v.getPsvId() == null)
{
continue;
}
if (deleted.getPsvId() != null && deleted.getPsvId().equals(v.getPsvId()))
{
continue;
}
if (v.getEffectiveTime() == null)
{
continue;
}
// Skip future pending drafts: they are not "previous" effective windows
if (STATUS_PENDING.equals(v.getStatus()) && v.getEffectiveTime().after(now))
{
continue;
}
if (STATUS_DRAFT.equals(v.getStatus()))
{
continue;
}
int seq = v.getVersionSeq() == null ? 0 : v.getVersionSeq().intValue();
if (seq >= curSeq)
{
continue;
}
if (seq > bestSeq)
{
bestSeq = seq;
best = v;
}
}
return best;
}
/** Current draft version (status=0), or null. */
public PrcPriceSetVersion currentDraft(String priceType)
{
@@ -222,7 +314,10 @@ public class CatalogVersionSupport
return drafts.isEmpty() ? null : drafts.get(0);
}
/** Draft/pending catalog version that can accept preview apply / edit. */
/**
* Catalog version that can accept preview apply / Excel attach / edit.
* Same as sales force-edit: draft, pending, and effect are writable.
*/
public PrcPriceSetVersion requireEditableCatalogVersion(Long psvId, String priceType)
{
PrcPriceSetVersion ver = versionMapper.selectPrcPriceSetVersionById(psvId);
@@ -235,9 +330,15 @@ public class CatalogVersionSupport
{
throw new ServiceException("版本类型不匹配");
}
if (!STATUS_DRAFT.equals(ver.getStatus()) && !STATUS_PENDING.equals(ver.getStatus()))
if ("1".equals(ver.getIsDel()))
{
throw new ServiceException("仅草稿或待生效版本可以编辑");
throw new ServiceException("版本已删除");
}
if (!STATUS_DRAFT.equals(ver.getStatus())
&& !STATUS_PENDING.equals(ver.getStatus())
&& !STATUS_EFFECT.equals(ver.getStatus()))
{
throw new ServiceException("仅草稿、待生效或已生效版本可以编辑");
}
return ver;
}
@@ -14,6 +14,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="expireTime" column="expire_time" />
<result property="status" column="status" />
<result property="priceBuildMode" column="price_build_mode"/>
<result property="isDel" column="is_del" />
<result property="createBy" column="created_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="updated_by" />
@@ -24,7 +25,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectPrcPriceSetVersionVo">
select psv_id, price_set_id, price_type, version_no, version_seq, effective_time, expire_time, status,
price_build_mode, created_by, create_time, updated_by, update_time
price_build_mode, is_del, created_by, create_time, updated_by, update_time
from prc_price_set_version
</sql>
@@ -36,6 +37,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectPrcPriceSetVersionList" parameterType="PrcPriceSetVersion" resultMap="PrcPriceSetVersionResult">
<include refid="selectPrcPriceSetVersionVo"/>
<where>
and ifnull(is_del, '0') = '0'
<if test="priceSetId != null">
AND price_set_id = #{priceSetId}
</if>
@@ -57,7 +59,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectCatalogVersionList" parameterType="PrcPriceSetVersion" resultMap="PrcPriceSetVersionResult">
select v.psv_id, v.price_set_id, v.price_type, v.version_no, v.version_seq,
v.effective_time, v.expire_time, v.status, v.price_build_mode,
v.effective_time, v.expire_time, v.status, v.price_build_mode, v.is_del,
v.created_by, v.create_time, v.updated_by, v.update_time,
(select count(1) from prc_price_set_product p
where p.psv_id = v.psv_id and ifnull(p.is_del, '0') = '0') as product_count,
@@ -66,6 +68,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
from prc_price_set_version v
where v.price_set_id is null
and v.price_type = #{priceType}
and ifnull(v.is_del, '0') = '0'
<choose>
<when test="statusGroup != null and statusGroup == 'pending'">
and v.status in ('0', '3')
@@ -162,6 +165,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectPrcPriceSetVersionVo"/>
where price_type = 'SALES'
and price_set_id = #{priceSetId}
and ifnull(is_del, '0') = '0'
and effective_time is not null
and effective_time &lt;= #{asOf}
and (expire_time is null or expire_time &gt; #{asOf})
@@ -173,6 +177,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectPrcPriceSetVersionVo"/>
where price_type = #{priceType}
and price_set_id is null
and ifnull(is_del, '0') = '0'
and effective_time is not null
and effective_time &lt;= #{asOf}
and (expire_time is null or expire_time &gt; #{asOf})
@@ -180,6 +185,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit 1
</select>
<update id="softDeleteById">
update prc_price_set_version
set is_del = '1',
<if test="updateBy != null and updateBy != ''">updated_by = #{updateBy},</if>
update_time = sysdate()
where psv_id = #{psvId}
and ifnull(is_del, '0') = '0'
</update>
<update id="refreshCatalogStatusByDates">
update prc_price_set_version
set status = case
@@ -191,6 +205,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
update_time = sysdate()
where price_type = #{priceType}
and price_set_id is null
and ifnull(is_del, '0') = '0'
and status in ('0','1','2','3')
</update>
@@ -166,6 +166,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
)
</delete>
<update id="softDeleteByPsvId">
update prc_surcharge_rel r
set r.is_del = '1', r.update_time = sysdate()
where ifnull(r.is_del, '0') = '0'
and (
exists (
select 1 from prc_price_set_product p
where p.psp_id = r.product_id and p.psv_id = #{psvId}
)
or exists (
select 1 from prc_price_set_detail d
where d.psd_id = r.detail_id and d.psv_id = #{psvId}
)
)
</update>
<!-- 附加费版本化编辑后,产品关联重指向新记录 -->
<update id="rebindRelScId">
update prc_surcharge_rel
+3 -1
View File
@@ -1315,6 +1315,7 @@ CREATE TABLE `prc_price_set_version` (
`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导入',
`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 '创建时间',
`updated_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新人',
@@ -1324,7 +1325,8 @@ CREATE TABLE `prc_price_set_version` (
UNIQUE KEY `uk_psv_type_set_seq` (`price_type`,`price_set_id_key`,`version_seq`),
KEY `idx_psv_set_eff` (`price_set_id`,`effective_time`),
KEY `idx_psv_type_eff` (`price_type`,`status`,`effective_time`),
KEY `idx_psv_status` (`status`)
KEY `idx_psv_status` (`status`),
KEY `idx_psv_is_del` (`is_del`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格版本(三价统一;与套餐弱关联)';
@@ -0,0 +1,13 @@
-- Soft-delete flag for price versions (catalog EFFECT soft-delete)
SET @db := DATABASE();
SET @sql := (
SELECT IF(COUNT(*) = 0,
'ALTER TABLE prc_price_set_version ADD COLUMN is_del char(1) NOT NULL DEFAULT ''0'' COMMENT ''是否删除 0未删除 1已删除'' AFTER price_build_mode, ADD KEY idx_psv_is_del (is_del)',
'SELECT 1')
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND COLUMN_NAME = 'is_del'
);
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
UPDATE prc_price_set_version SET is_del = '0' WHERE is_del IS NULL OR is_del = '';
@@ -95,7 +95,7 @@ function findPrev(row) {
}
function pickDefault(prev) {
const d = new Date()
d.setHours(0, 0, 0, 0)
d.setHours(9, 0, 0, 0)
const now = formatHour(d)
if (!prev) return now
return toMs(now) >= toMs(prev) ? now : prev
@@ -243,6 +243,7 @@ const importProgressMessage = ref('')
const importProgressStatus = ref(undefined)
const finishing = ref(false)
const editingExisting = ref(false)
const localRemoves = ref([])
const pendingImports = ref([])
const previewDetailsByCode = ref({})
@@ -327,6 +328,7 @@ async function bootstrap() {
if (qPsv != null && qPsv !== '') {
psvId.value = Number(qPsv)
versionNo.value = (route.query && route.query.versionNo) ? String(route.query.versionNo) : ''
editingExisting.value = true
if (!buildMode.value) buildMode.value = 'IMPORT'
await loadProducts()
}
@@ -381,7 +383,7 @@ function removeRow(row) {
if (!row) return
const code = row.productCode
if (psvId.value && row.productId != null) {
proxy.$modal.confirm('确认从草稿中移除该产品?').then(() => props.delFn(row.productId)).then(() => {
proxy.$modal.confirm(editingExisting.value ? '确认移除该产品?' : '确认从草稿中移除该产品?').then(() => props.delFn(row.productId)).then(() => {
proxy.$modal.msgSuccess('移除成功')
loadProducts()
}).catch(() => {})
@@ -511,7 +513,7 @@ async function finishSave() {
await applyPendingPreview(applyImportProgress)
await savePendingExcelFiles()
await applyLocalRemoves()
proxy.$modal.msgSuccess('已保存到待生效草稿')
proxy.$modal.msgSuccess(editingExisting.value ? '已保存' : '已保存到待生效草稿')
goBack()
} catch (e) {
const msg = (e && e.message) || (e && e.msg) || 'save failed'
@@ -12,9 +12,10 @@
<el-table-column :label="colLevel" prop="serviceLevel" width="90" align="center" />
<el-table-column :label="colCargo" prop="cargoAttr" min-width="120" show-overflow-tooltip />
<el-table-column :label="colRegion" prop="destRegion" width="90" align="center" />
<el-table-column :label="colAction" width="110" align="center" fixed="right">
<el-table-column :label="colAction" :width="showSurcharge ? 180 : 110" align="center" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click.stop="view(row)">{{ viewText }}</el-button>
<el-button v-if="showSurcharge" link type="primary" @click.stop="emit('surcharge', row)">{{ surchargeText }}</el-button>
</template>
</el-table-column>
</el-table>
@@ -33,15 +34,17 @@ const L = {
colRegion: "目的区域",
colAction: "操作",
view: "查看明细",
surcharge: "附加费",
countAll: "共 ",
countUnit: " 个产品",
countFilter: " 个产品(全部 ",
countFilterEnd: " 个)"
}
const props = defineProps({
listFn: { type: Function, required: true }
listFn: { type: Function, required: true },
showSurcharge: { type: Boolean, default: false }
})
const emit = defineEmits(['view'])
const emit = defineEmits(['view', 'surcharge'])
const visible = ref(false)
const loading = ref(false)
const rows = ref([])
@@ -60,6 +63,8 @@ const colCargo = L.colCargo
const colRegion = L.colRegion
const colAction = L.colAction
const viewText = L.view
const surchargeText = L.surcharge
const showSurcharge = computed(() => !!props.showSurcharge)
const labQuery = "查询"
const labReset = "重置"
const filteredRows = computed(() => {
@@ -0,0 +1,206 @@
<template>
<div class="catalog-ver">
<div class="ver-head">
<div class="ver-title">
<i class="ver-bar" />
<span class="ver-text">{{ title }}</span>
<el-tag size="small" type="info" effect="plain" round>{{ filteredVersions.length }}</el-tag>
</div>
<el-button
type="primary"
plain
size="small"
:disabled="hasDraft"
v-hasPermi="[createPerm]"
@click="emit('create')"
>{{ createLabel }}</el-button>
</div>
<el-tabs v-model="statusTab" class="ver-tabs">
<el-tab-pane :label="tabLabel('all')" name="all" />
<el-tab-pane :label="tabLabel('pending')" name="pending" />
<el-tab-pane :label="tabLabel('1')" name="1" />
<el-tab-pane :label="tabLabel('2')" name="2" />
</el-tabs>
<el-table v-loading="loading" :data="filteredVersions" border :empty-text="emptyText" @row-click="onRowClick">
<el-table-column :label="colNo" width="120">
<template #default="{ row }">
<el-button link type="primary" @click.stop="emit('view-products', row)">{{ row.versionNo || '-' }}</el-button>
</template>
</el-table-column>
<el-table-column :label="colEff" min-width="170">
<template #default="{ row }">{{ row.effectiveTime || '-' }}</template>
</el-table-column>
<el-table-column :label="colExp" min-width="170">
<template #default="{ row }">{{ row.expireTime || '-' }}</template>
</el-table-column>
<el-table-column :label="colStatus" width="100" align="center">
<template #default="{ row }">
<el-tag :type="versionStatusTag(row.status)" size="small">{{ versionStatusLabel(row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column :label="colProducts" prop="productCount" width="90" align="center" />
<el-table-column :label="colRows" prop="detailCount" width="100" align="center" />
<el-table-column :label="colAction" width="420" align="center" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click.stop="emit('view-products', row)">{{ labViewProducts }}</el-button>
<el-button link type="primary" @click.stop="emit('view-attach', row)" v-hasPermi="[queryPerm]">{{ labViewAttach }}</el-button>
<el-button
v-if="showEditButton(row)"
link
type="primary"
@click.stop="emit('edit', row)"
>{{ labEdit }}</el-button>
<el-button
v-if="isDraftVersion(row)"
link
type="success"
@click.stop="emit('activate', row)"
v-hasPermi="[editPerm]"
>{{ labStart }}</el-button>
<el-button
v-if="isDraftVersion(row) || isPendingVersion(row) || isEffectVersion(row)"
link
type="danger"
:loading="isRemoving(row)"
:disabled="removingPsvId != null"
@click.stop="emit('remove', row)"
v-hasPermi="[removePerm]"
>{{ labDelete }}</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup name="CatalogVersionTable">
import {
versionStatusLabel,
versionStatusTag,
isDraftVersion,
isPendingVersion,
isEffectVersion,
isPriceEditableVersion
} from '@/views/price/set/utils/priceSet'
import { checkPermi } from '@/utils/permission'
const props = defineProps({
versions: { type: Array, default: () => [] },
loading: { type: Boolean, default: false },
title: { type: String, required: true },
createLabel: { type: String, required: true },
createPerm: { type: String, required: true },
editPerm: { type: String, required: true },
removePerm: { type: String, required: true },
queryPerm: { type: String, required: true },
removingPsvId: { type: [Number, String], default: null }
})
const emit = defineEmits(['create', 'edit', 'view-products', 'view-attach', 'activate', 'remove'])
const colNo = '版本号'
const colEff = '生效时间'
const colExp = '失效时间'
const colStatus = '状态'
const colProducts = '产品数'
const colRows = '明细行数'
const colAction = '操作'
const emptyText = '暂无版本'
const labViewProducts = '查看产品'
const labViewAttach = '查看附件'
const labEdit = '编辑'
const labStart = '启动'
const labDelete = '删除'
const statusTab = ref('all')
const counts = computed(() => {
const map = { all: 0, pending: 0, '1': 0, '2': 0 }
;(props.versions || []).forEach(v => {
map.all++
const st = String(v.status == null ? '' : v.status)
if (st === '0' || st === '3') map.pending++
else if (map[st] != null) map[st]++
})
return map
})
function tabLabel(key) {
const labels = {
all: '全部',
pending: '待生效',
'1': '已生效',
'2': '已失效'
}
return labels[key] + ' (' + (counts.value[key] || 0) + ')'
}
const hasDraft = computed(() => (props.versions || []).some(v => isDraftVersion(v)))
const filteredVersions = computed(() => {
const list = props.versions || []
if (statusTab.value === 'all') return list
if (statusTab.value === 'pending') {
return list.filter(v => {
const st = String(v.status == null ? '' : v.status)
return st === '0' || st === '3'
})
}
return list.filter(v => String(v.status) === statusTab.value)
})
function showEditButton(row) {
if (!checkPermi([props.editPerm])) return false
// Align with sales force-edit: draft / pending / effect can edit
return isPriceEditableVersion(row) || isEffectVersion(row)
}
function isRemoving(row) {
return props.removingPsvId != null && row && String(props.removingPsvId) === String(row.psvId)
}
function onRowClick(row) {
emit('view-products', row)
}
</script>
<style scoped>
.catalog-ver {
background: #fff;
border: 1px solid var(--el-border-color-lighter);
border-radius: 8px;
padding: 12px 16px 16px;
}
.ver-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 4px;
}
.ver-title {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.ver-bar {
width: 3px;
height: 14px;
border-radius: 2px;
background: #52c41a;
flex: none;
}
.ver-text {
font-size: 15px;
font-weight: 600;
color: #303133;
}
.ver-tabs {
margin-bottom: 8px;
}
.ver-tabs :deep(.el-tabs__header) {
margin-bottom: 8px;
}
</style>
+47 -187
View File
@@ -5,112 +5,38 @@
:closable="false"
show-icon
class="mb16"
title="已生效展示当前生效版本产品。待生效、已失效按版本展示;待生效可启动或删除整版。建价请点「新建产品价格」。"
:title="alertTitle"
/>
<el-tabs v-model="statusTab" class="catalog-tabs" @tab-change="onTabChange">
<el-tab-pane label="已生效" name="effect" />
<el-tab-pane label="待生效" name="pending" />
<el-tab-pane label="已失效" name="expired" />
</el-tabs>
<div v-show="statusTab === 'effect'">
<basic-table
ref="tableRef"
:extra-params="extraParams"
:columns="columns"
:search-columns="searchColumns"
:query="queryList"
row-key="productId"
:selection="false"
>
<template #toolbar>
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['price:product:add']">新建产品价格</el-button>
</el-col>
</template>
<template #col-cargoAttr="{ row }">
<el-tag type="primary" size="small" effect="plain">{{ row.cargoAttr || '-' }}</el-tag>
</template>
<template #col-status="{ row }">
<dict-tag :options="bms_product_effect_status" :value="row.status" />
</template>
<template #col-action="{ row }">
<el-button link type="primary" title="产品明细" icon="View" @click="countryRef.open(row)" v-hasPermi="['price:product:query']"></el-button>
<el-button link type="primary" title="附加费" icon="Money" @click="surchargeRef.open(row)" v-hasPermi="['price:surcharge:query']"></el-button>
</template>
</basic-table>
</div>
<div v-show="statusTab === 'pending'">
<el-row class="mb12">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['price:product:add']">新建产品价格</el-button>
</el-col>
</el-row>
<el-table v-loading="versionLoading" :data="versionRows" border empty-text="暂无待生效版本" @row-click="openVersionProducts">
<el-table-column label="版本号" prop="versionNo" width="120" />
<el-table-column label="生效时间" prop="effectiveTime" min-width="170" />
<el-table-column label="失效时间" prop="expireTime" min-width="170" />
<el-table-column label="状态" width="100" align="center">
<template #default="{ row }">{{ versionStatusText(row.status) }}</template>
</el-table-column>
<el-table-column label="产品数" prop="productCount" width="90" align="center" />
<el-table-column label="明细行数" prop="detailCount" width="100" align="center" />
<el-table-column label="操作" width="360" align="center" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click.stop="onEditVersion(row)" v-hasPermi="['price:product:edit']">编辑</el-button>
<el-button link type="primary" @click.stop="openVersionProducts(row)">查看产品</el-button>
<el-button link type="primary" @click.stop="openExcel(row)" v-hasPermi="['price:product:query']">查看附件</el-button>
<el-button link type="success" @click.stop="onStartVersion(row)" v-hasPermi="['price:product:edit']">启动</el-button>
<el-button link type="danger" @click.stop="onDeleteVersion(row)" v-hasPermi="['price:product:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="versionTotal > 0"
:total="versionTotal"
v-model:page="versionQuery.pageNum"
v-model:limit="versionQuery.pageSize"
@pagination="loadVersions"
/>
</div>
<div v-show="statusTab === 'expired'">
<el-table v-loading="versionLoading" :data="versionRows" border empty-text="暂无失效版本" @row-click="openVersionProducts">
<el-table-column label="版本号" prop="versionNo" width="120" />
<el-table-column label="生效时间" prop="effectiveTime" min-width="170" />
<el-table-column label="失效时间" prop="expireTime" min-width="170" />
<el-table-column label="状态" width="100" align="center">
<template #default="{ row }">{{ versionStatusText(row.status) }}</template>
</el-table-column>
<el-table-column label="产品数" prop="productCount" width="90" align="center" />
<el-table-column label="明细行数" prop="detailCount" width="100" align="center" />
<el-table-column label="操作" width="220" align="center" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click.stop="openVersionProducts(row)">查看产品</el-button>
<el-button link type="primary" @click.stop="openExcel(row)" v-hasPermi="['price:product:query']">查看附件</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="versionTotal > 0"
:total="versionTotal"
v-model:page="versionQuery.pageNum"
v-model:limit="versionQuery.pageSize"
@pagination="loadVersions"
/>
</div>
<catalog-version-table
:versions="versionRows"
:loading="versionLoading"
:removing-psv-id="removingPsvId"
:title="verTitle"
:create-label="createLabel"
create-perm="price:product:add"
edit-perm="price:product:edit"
remove-perm="price:product:remove"
query-perm="price:product:query"
@create="handleAdd"
@edit="onEditVersion"
@view-products="openVersionProducts"
@view-attach="openExcel"
@activate="onStartVersion"
@remove="onDeleteVersion"
/>
<catalog-version-excel-drawer ref="excelRef" :list-fn="listProductVersionExcelFiles" />
<product-country-drawer ref="countryRef" />
<product-surcharge-drawer ref="surchargeRef" @success="() => tableRef.refresh()" />
<catalog-version-products-drawer ref="versionProductsRef" :list-fn="listProduct" @view="(row) => countryRef.open(row)" />
<product-surcharge-drawer ref="surchargeRef" />
<catalog-version-products-drawer
ref="versionProductsRef"
:list-fn="listProduct"
show-surcharge
@view="(row) => countryRef.open(row)"
@surcharge="(row) => surchargeRef.open(row)"
/>
<catalog-activate-dialog ref="activateRef" :load-versions="loadAllVersions" :activate-fn="activateProductVersion" @success="onActivated" />
</div>
</template>
@@ -120,53 +46,24 @@ import ProductSurchargeDrawer from './components/ProductSurchargeDrawer.vue'
import CatalogVersionProductsDrawer from '../components/CatalogVersionProductsDrawer.vue'
import CatalogVersionExcelDrawer from '../components/CatalogVersionExcelDrawer.vue'
import CatalogActivateDialog from '../components/CatalogActivateDialog.vue'
import CatalogVersionTable from '../components/CatalogVersionTable.vue'
import { listProduct, listProductVersions, activateProductVersion, deleteProductVersion, listProductVersionExcelFiles } from '@/api/price/product'
const { proxy } = getCurrentInstance()
const { bms_product_effect_status } = useDict('bms_product_effect_status')
const statusTab = ref('effect')
const extraParams = reactive({ statusGroup: 'effect' })
const alertTitle = "按版本管理产品价(与套餐版本一致)。点击版本可查看产品与明细;待生效(含草稿)可编辑、启动或硬删;已生效可软删(上一版自动恢复生效并清空失效时间)。建价请点「新建产品价格」。"
const verTitle = '产品价版本'
const createLabel = '新建产品价格'
const versionProductsRef = ref()
const activateRef = ref()
const versionLoading = ref(false)
const removingPsvId = ref(null)
const versionRows = ref([])
const versionTotal = ref(0)
const versionQuery = reactive({ pageNum: 1, pageSize: 10 })
const tableRef = ref()
const countryRef = ref()
const surchargeRef = ref()
const excelRef = ref()
const columns = [
{ prop: 'productCode', label: '产品代码', minWidth: 110 },
{ prop: 'productName', label: '产品名称', minWidth: 140, showOverflowTooltip: true },
{ prop: 'serviceLevel', label: '时效等级', width: 90, align: 'center' },
{ prop: 'cargoAttr', label: '走货属性', minWidth: 120, slot: 'cargoAttr' },
{ prop: 'destRegion', label: '目的区域', width: 90, align: 'center' },
{ prop: 'effectiveDate', label: '生效时间', width: 160, align: 'center' },
{ prop: 'expireDate', label: '失效时间', width: 160, align: 'center' },
{ prop: 'countryCount', label: '国家数', width: 80, align: 'center' },
{ prop: 'surchargeCount', label: '附加费数', width: 90, align: 'center' },
{ prop: 'status', label: '状态', width: 90, align: 'center', slot: 'status' },
{ prop: 'updateNote', label: '更新内容', minWidth: 160, showOverflowTooltip: true },
{ prop: 'action', label: '操作', width: 120, fixed: 'right', slot: 'action' }
]
const searchColumns = [
{ prop: 'productCode', label: '产品代码', component: 'input', placeholder: '产品代码' },
{ prop: 'productName', label: '产品名称', component: 'input', placeholder: '产品名称' }
]
function queryList(params) {
const p = { ...(params || {}) }
// Exact status search must not be limited by default statusGroup=active.
if (p.status != null && p.status !== '') {
delete p.statusGroup
}
return listProduct(p)
}
function handleAdd() {
proxy.$router.push({ path: '/price/product-create/index' })
}
@@ -179,32 +76,10 @@ function openExcel(row) {
excelRef.value && excelRef.value.open(row)
}
function onTabChange(name) {
if (name === 'pending' || name === 'expired') {
versionQuery.pageNum = 1
loadVersions()
return
}
extraParams.statusGroup = 'effect'
nextTick(() => tableRef.value && tableRef.value.refresh())
}
function versionStatusText(status) {
const map = { '0': '草稿', '1': '已生效', '2': '已失效', '3': '待生效' }
return map[String(status)] || status || '-'
}
function loadVersions() {
versionLoading.value = true
const params = {
pageNum: versionQuery.pageNum,
pageSize: versionQuery.pageSize
}
if (statusTab.value === 'pending') params.statusGroup = 'pending'
else params.status = '2'
listProductVersions(params).then(res => {
listProductVersions({ pageNum: 1, pageSize: 500 }).then(res => {
versionRows.value = res.rows || []
versionTotal.value = res.total || 0
}).finally(() => { versionLoading.value = false })
}
@@ -230,48 +105,33 @@ function onEditVersion(row) {
function onStartVersion(row) {
if (row && row.psvId != null) {
activateRef.value && activateRef.value.open(row)
return
}
loadAllVersions().then(list => {
const pending = (list || []).filter(v => v && (String(v.status) === '0' || String(v.status) === '3'))
if (!pending.length) {
proxy.$modal.msgWarning('当前没有可启动的待生效版本')
return
}
pending.sort((a, b) => (Number(b.versionSeq) || 0) - (Number(a.versionSeq) || 0))
const draft = pending.find(v => String(v.status) === '0')
activateRef.value && activateRef.value.open(draft || pending[0])
})
}
function onDeleteVersion(row) {
if (!row || row.psvId == null) return
proxy.$modal.confirm('确认硬删除该待生效版本?版本、产品、明细将从数据库真删除,且不可恢复。').then(() => deleteProductVersion(row.psvId)).then(() => {
loadVersions()
proxy.$modal.msgSuccess('删除成功')
const effect = String(row.status) === '1'
const msg = effect ? "确认软删除该已生效版本?版本及产品、明细将标记为已删除(列表不再展示)。" : "确认硬删除该版本?版本、产品、明细将从数据库真删除,且不可恢复。"
proxy.$modal.confirm(msg).then(() => {
removingPsvId.value = row.psvId
proxy.$modal.loading("正在删除,请稍候...")
return deleteProductVersion(row.psvId).then(() => {
loadVersions()
proxy.$modal.msgSuccess("删除成功")
}).finally(() => {
removingPsvId.value = null
proxy.$modal.closeLoading()
})
}).catch(() => {})
}
function onActivated() {
if (statusTab.value === 'pending' || statusTab.value === 'expired') loadVersions()
else if (tableRef.value) tableRef.value.refresh()
loadVersions()
}
onMounted(() => loadVersions())
</script>
<style scoped>
.mb16 { margin-bottom: 16px; }
.mb12 { margin-bottom: 12px; }
.catalog-tabs { margin-bottom: 8px; }
.import-progress { padding: 8px 4px 4px; }
.import-progress-msg {
margin-top: 12px;
color: var(--el-text-color-secondary);
font-size: 13px;
line-height: 1.5;
word-break: break-word;
max-height: 160px;
overflow: auto;
}
</style>
+48 -183
View File
@@ -5,163 +5,65 @@
:closable="false"
show-icon
class="mb16"
title="红线价已生效展示当前生效版本产品。待生效、已失效按版本展示;待生效可启动或删除整版。"
:title="alertTitle"
/>
<el-tabs v-model="statusTab" class="catalog-tabs" @tab-change="onTabChange">
<el-tab-pane label="已生效" name="effect" />
<el-tab-pane label="待生效" name="pending" />
<el-tab-pane label="已失效" name="expired" />
</el-tabs>
<div v-show="statusTab === 'effect'">
<basic-table
ref="tableRef"
:extra-params="extraParams"
:columns="columns"
:search-columns="searchColumns"
:query="queryList"
row-key="productId"
:selection="false"
>
<template #toolbar>
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['price:redline:add']">新建红线价</el-button>
</el-col>
</template>
<template #col-cargoAttr="{ row }">
<el-tag type="primary" size="small" effect="plain">{{ row.cargoAttr || '-' }}</el-tag>
</template>
<template #col-status="{ row }">
<dict-tag :options="bms_product_effect_status" :value="row.status" />
</template>
<template #col-action="{ row }">
<el-button link type="primary" title="红线价明细" icon="View" @click="countryRef.open(row)" v-hasPermi="['price:redline:query']"></el-button>
<el-button link type="primary" title="附加费" icon="Money" @click="surchargeRef.open(row)" v-hasPermi="['price:surcharge:query']"></el-button>
</template>
</basic-table>
</div>
<div v-show="statusTab === 'pending'">
<el-row class="mb12">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['price:redline:add']">新建红线价</el-button>
</el-col>
</el-row>
<el-table v-loading="versionLoading" :data="versionRows" border empty-text="暂无待生效版本" @row-click="openVersionProducts">
<el-table-column label="版本号" prop="versionNo" width="120" />
<el-table-column label="生效时间" prop="effectiveTime" min-width="170" />
<el-table-column label="失效时间" prop="expireTime" min-width="170" />
<el-table-column label="状态" width="100" align="center">
<template #default="{ row }">{{ versionStatusText(row.status) }}</template>
</el-table-column>
<el-table-column label="产品数" prop="productCount" width="90" align="center" />
<el-table-column label="明细行数" prop="detailCount" width="100" align="center" />
<el-table-column label="操作" width="360" align="center" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click.stop="onEditVersion(row)" v-hasPermi="['price:redline:edit']">编辑</el-button>
<el-button link type="primary" @click.stop="openVersionProducts(row)">查看产品</el-button>
<el-button link type="primary" @click.stop="openExcel(row)" v-hasPermi="['price:redline:query']">查看附件</el-button>
<el-button link type="success" @click.stop="onStartVersion(row)" v-hasPermi="['price:redline:edit']">启动</el-button>
<el-button link type="danger" @click.stop="onDeleteVersion(row)" v-hasPermi="['price:redline:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="versionTotal > 0"
:total="versionTotal"
v-model:page="versionQuery.pageNum"
v-model:limit="versionQuery.pageSize"
@pagination="loadVersions"
/>
</div>
<div v-show="statusTab === 'expired'">
<el-table v-loading="versionLoading" :data="versionRows" border empty-text="暂无失效版本" @row-click="openVersionProducts">
<el-table-column label="版本号" prop="versionNo" width="120" />
<el-table-column label="生效时间" prop="effectiveTime" min-width="170" />
<el-table-column label="失效时间" prop="expireTime" min-width="170" />
<el-table-column label="状态" width="100" align="center">
<template #default="{ row }">{{ versionStatusText(row.status) }}</template>
</el-table-column>
<el-table-column label="产品数" prop="productCount" width="90" align="center" />
<el-table-column label="明细行数" prop="detailCount" width="100" align="center" />
<el-table-column label="操作" width="220" align="center" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click.stop="openVersionProducts(row)">查看产品</el-button>
<el-button link type="primary" @click.stop="openExcel(row)" v-hasPermi="['price:redline:query']">查看附件</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="versionTotal > 0"
:total="versionTotal"
v-model:page="versionQuery.pageNum"
v-model:limit="versionQuery.pageSize"
@pagination="loadVersions"
/>
</div>
<catalog-version-table
:versions="versionRows"
:loading="versionLoading"
:removing-psv-id="removingPsvId"
:title="verTitle"
:create-label="createLabel"
create-perm="price:redline:add"
edit-perm="price:redline:edit"
remove-perm="price:redline:remove"
query-perm="price:redline:query"
@create="handleAdd"
@edit="onEditVersion"
@view-products="openVersionProducts"
@view-attach="openExcel"
@activate="onStartVersion"
@remove="onDeleteVersion"
/>
<catalog-version-excel-drawer ref="excelRef" :list-fn="listRedlineVersionExcelFiles" />
<redline-country-drawer ref="countryRef" />
<redline-surcharge-drawer ref="surchargeRef" @success="() => tableRef.refresh()" />
<catalog-version-products-drawer ref="versionProductsRef" :list-fn="listRedline" @view="(row) => countryRef.open(row)" />
<redline-surcharge-drawer ref="surchargeRef" />
<catalog-version-products-drawer
ref="versionProductsRef"
:list-fn="listRedline"
show-surcharge
@view="(row) => countryRef.open(row)"
@surcharge="(row) => surchargeRef.open(row)"
/>
<catalog-activate-dialog ref="activateRef" :load-versions="loadAllVersions" :activate-fn="activateRedlineVersion" @success="onActivated" />
</div>
</template>
<script setup name="PriceProduct">
<script setup name="PriceRedline">
import RedlineCountryDrawer from './components/RedlineCountryDrawer.vue'
import RedlineSurchargeDrawer from './components/RedlineSurchargeDrawer.vue'
import CatalogVersionProductsDrawer from '../components/CatalogVersionProductsDrawer.vue'
import CatalogVersionExcelDrawer from '../components/CatalogVersionExcelDrawer.vue'
import CatalogActivateDialog from '../components/CatalogActivateDialog.vue'
import CatalogVersionTable from '../components/CatalogVersionTable.vue'
import { listRedline, listRedlineVersions, activateRedlineVersion, deleteRedlineVersion, listRedlineVersionExcelFiles } from '@/api/price/redline'
const { proxy } = getCurrentInstance()
const { bms_product_effect_status } = useDict('bms_product_effect_status')
const statusTab = ref('effect')
const extraParams = reactive({ statusGroup: 'effect' })
const alertTitle = "按版本管理红线价(与套餐版本一致)。点击版本可查看产品与明细;待生效(含草稿)可编辑、启动或硬删;已生效可软删(上一版自动恢复生效并清空失效时间)。建价请点「新建红线价」。"
const verTitle = '红线价版本'
const createLabel = '新建红线价'
const versionProductsRef = ref()
const activateRef = ref()
const versionLoading = ref(false)
const removingPsvId = ref(null)
const versionRows = ref([])
const versionTotal = ref(0)
const versionQuery = reactive({ pageNum: 1, pageSize: 10 })
const tableRef = ref()
const countryRef = ref()
const surchargeRef = ref()
const excelRef = ref()
const columns = [
{ prop: 'productCode', label: '产品代码', minWidth: 110 },
{ prop: 'productName', label: '产品名称', minWidth: 140, showOverflowTooltip: true },
{ prop: 'serviceLevel', label: '时效等级', width: 90, align: 'center' },
{ prop: 'cargoAttr', label: '走货属性', minWidth: 120, slot: 'cargoAttr' },
{ prop: 'destRegion', label: '目的区域', width: 90, align: 'center' },
{ prop: 'effectiveDate', label: '生效时间', width: 160, align: 'center' },
{ prop: 'expireDate', label: '失效时间', width: 160, align: 'center' },
{ prop: 'countryCount', label: '国家数', width: 80, align: 'center' },
{ prop: 'surchargeCount', label: '附加费数', width: 90, align: 'center' },
{ prop: 'status', label: '状态', width: 90, align: 'center', slot: 'status' },
{ prop: 'updateNote', label: '更新内容', minWidth: 160, showOverflowTooltip: true },
{ prop: 'action', label: '操作', width: 120, fixed: 'right', slot: 'action' }
]
const searchColumns = [
{ prop: 'productCode', label: '产品代码', component: 'input', placeholder: '产品代码' },
{ prop: 'productName', label: '产品名称', component: 'input', placeholder: '产品名称' }
]
function queryList(params) {
return listRedline(params)
}
function handleAdd() {
proxy.$router.push({ path: '/price/redline-create/index' })
}
@@ -174,32 +76,10 @@ function openExcel(row) {
excelRef.value && excelRef.value.open(row)
}
function onTabChange(name) {
if (name === 'pending' || name === 'expired') {
versionQuery.pageNum = 1
loadVersions()
return
}
extraParams.statusGroup = 'effect'
nextTick(() => tableRef.value && tableRef.value.refresh())
}
function versionStatusText(status) {
const map = { '0': '草稿', '1': '已生效', '2': '已失效', '3': '待生效' }
return map[String(status)] || status || '-'
}
function loadVersions() {
versionLoading.value = true
const params = {
pageNum: versionQuery.pageNum,
pageSize: versionQuery.pageSize
}
if (statusTab.value === 'pending') params.statusGroup = 'pending'
else params.status = '2'
listRedlineVersions(params).then(res => {
listRedlineVersions({ pageNum: 1, pageSize: 500 }).then(res => {
versionRows.value = res.rows || []
versionTotal.value = res.total || 0
}).finally(() => { versionLoading.value = false })
}
@@ -225,48 +105,33 @@ function onEditVersion(row) {
function onStartVersion(row) {
if (row && row.psvId != null) {
activateRef.value && activateRef.value.open(row)
return
}
loadAllVersions().then(list => {
const pending = (list || []).filter(v => v && (String(v.status) === '0' || String(v.status) === '3'))
if (!pending.length) {
proxy.$modal.msgWarning('当前没有可启动的待生效版本')
return
}
pending.sort((a, b) => (Number(b.versionSeq) || 0) - (Number(a.versionSeq) || 0))
const draft = pending.find(v => String(v.status) === '0')
activateRef.value && activateRef.value.open(draft || pending[0])
})
}
function onDeleteVersion(row) {
if (!row || row.psvId == null) return
proxy.$modal.confirm('确认硬删除该待生效版本?版本、产品、明细将从数据库真删除,且不可恢复。').then(() => deleteRedlineVersion(row.psvId)).then(() => {
loadVersions()
proxy.$modal.msgSuccess('删除成功')
const effect = String(row.status) === '1'
const msg = effect ? "确认软删除该已生效版本?版本及产品、明细将标记为已删除(列表不再展示)。" : "确认硬删除该版本?版本、产品、明细将从数据库真删除,且不可恢复。"
proxy.$modal.confirm(msg).then(() => {
removingPsvId.value = row.psvId
proxy.$modal.loading("正在删除,请稍候...")
return deleteRedlineVersion(row.psvId).then(() => {
loadVersions()
proxy.$modal.msgSuccess("删除成功")
}).finally(() => {
removingPsvId.value = null
proxy.$modal.closeLoading()
})
}).catch(() => {})
}
function onActivated() {
if (statusTab.value === 'pending' || statusTab.value === 'expired') loadVersions()
else if (tableRef.value) tableRef.value.refresh()
loadVersions()
}
onMounted(() => loadVersions())
</script>
<style scoped>
.mb16 { margin-bottom: 16px; }
.mb12 { margin-bottom: 12px; }
.catalog-tabs { margin-bottom: 8px; }
.import-progress { padding: 8px 4px 4px; }
.import-progress-msg {
margin-top: 12px;
color: var(--el-text-color-secondary);
font-size: 13px;
line-height: 1.5;
word-break: break-word;
max-height: 160px;
overflow: auto;
}
</style>
@@ -63,9 +63,11 @@
v-hasPermi="['price:set:edit']"
>修改生效时间</el-button>
<el-button
v-if="isDraftVersion(row) || isPendingVersion(row)"
v-if="isDraftVersion(row) || isPendingVersion(row) || isEffectVersion(row)"
link
type="danger"
:loading="isRemoving(row)"
:disabled="removingPsvId != null"
@click="emit('remove', row)"
v-hasPermi="['price:set:edit']"
>删除</el-button>
@@ -77,12 +79,13 @@
<script setup name="SetVersionTable">
import SetSectionCard from './SetSectionCard.vue'
import { versionStatusLabel, versionStatusTag, isDraftVersion, isPendingVersion, isPriceEditableVersion } from '../../utils/priceSet'
import { versionStatusLabel, versionStatusTag, isDraftVersion, isPendingVersion, isEffectVersion, isPriceEditableVersion } from '../../utils/priceSet'
import { checkPermi } from '@/utils/permission'
const props = defineProps({
versions: { type: Array, default: () => [] },
modelValue: { type: [Number, String], default: undefined }
modelValue: { type: [Number, String], default: undefined },
removingPsvId: { type: [Number, String], default: null }
})
const emit = defineEmits(['update:modelValue', 'create-version', 'open-details', 'view-import-attach', 'activate', 'reschedule', 'edit', 'remove'])
@@ -134,6 +137,10 @@ function showEditButton(row) {
return isPriceEditableVersion(row) && checkPermi(['price:set:edit'])
}
function isRemoving(row) {
return props.removingPsvId != null && row && String(props.removingPsvId) === String(row.psvId)
}
function openDetails(row) {
if (!row || row.psvId == null) return
emit('update:modelValue', row.psvId)
+17 -9
View File
@@ -25,6 +25,7 @@
<set-version-table
v-model="currentPsvId"
:versions="set.versions || []"
:removing-psv-id="removingPsvId"
@create-version="onCreateVersion"
@open-details="onOpenVersionDetails"
@view-import-attach="onViewImportAttach"
@@ -91,7 +92,7 @@ import { usePriceSetRefData } from './composables/usePriceSetRefData'
import {
getPriceSet, disablePriceSet, enablePriceSet, activatePriceSetVersion, deletePriceSetVersion
} from '@/api/price/set'
import { isDraftVersion, isPendingVersion } from './utils/priceSet'
import { isDraftVersion, isPendingVersion, isEffectVersion } from './utils/priceSet'
const { proxy } = getCurrentInstance()
const route = useRoute()
@@ -99,6 +100,7 @@ const router = useRouter()
const refData = usePriceSetRefData()
const loading = ref(false)
const removingPsvId = ref(null)
const set = ref({})
const currentPsvId = ref()
const detailDrawerVisible = ref(false)
@@ -203,7 +205,7 @@ function normalizeToHour(val) {
function formatNow() {
const d = new Date()
d.setHours(0, 0, 0, 0)
d.setHours(9, 0, 0, 0)
return formatHour(d)
}
@@ -391,13 +393,19 @@ function onCreateVersion() {
}
function onRemoveVersion(row) {
if (!row || row.psvId == null) return
const statusTip = isPendingVersion(row) ? '待生效' : '草稿'
proxy.$modal.confirm(
'确认删除' + statusTip + '版本 ' + (row.versionNo || '')
+ '?其明细与附加费将一并删除。'
).then(() => deletePriceSetVersion(row.psvId)).then(() => {
proxy.$modal.msgSuccess('版本已删除')
load()
const msg = isEffectVersion(row)
? "确认软删除已生效版本 " + (row.versionNo || '') + "?版本及产品、明细将标记为已删除,上一版将自动恢复生效并清空失效时间。"
: "确认删除" + (isPendingVersion(row) ? "待生效" : "草稿") + "版本 " + (row.versionNo || '') + "?其明细与附加费将一并删除。"
proxy.$modal.confirm(msg).then(() => {
removingPsvId.value = row.psvId
proxy.$modal.loading("正在删除,请稍候...")
return deletePriceSetVersion(row.psvId).then(() => {
proxy.$modal.msgSuccess("版本已删除")
load()
}).finally(() => {
removingPsvId.value = null
proxy.$modal.closeLoading()
})
}).catch(() => {})
}
function onEditVersion(row) {
@@ -86,6 +86,10 @@ export function isPendingVersion(row) {
return !!row && String(row.status) === VER_STATUS.PENDING
}
export function isEffectVersion(row) {
return !!row && String(row.status) === VER_STATUS.EFFECT
}
/** Draft or pending: may continue editing price details */
export function isPriceEditableVersion(row) {
return isDraftVersion(row) || isPendingVersion(row)