fix: 完善套餐分配生效条件选择
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m30s

This commit is contained in:
luo
2026-07-24 17:42:36 +08:00
parent d1ff4d5f6c
commit eb13763020
8 changed files with 1649 additions and 90 deletions

1520
q Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,10 @@
import { BaseService } from '../BaseService' import { BaseService } from '../BaseService'
import type { import type {
BaseResponse, BaseResponse,
CreateShopPackageBatchAllocationsRequest,
CreateShopPackageAllocationRequest, CreateShopPackageAllocationRequest,
ShopPackageAllocationResponse, ShopPackageAllocationResponse,
ShopPackageBatchAllocationsResponse,
UpdateShopPackageAllocationExpiryBaseRequest UpdateShopPackageAllocationExpiryBaseRequest
} from '@/types/api' } from '@/types/api'
@@ -24,6 +26,19 @@ export class ShopPackageAllocationService extends BaseService {
) )
} }
/**
* 批量创建店铺套餐分配
* POST /api/admin/shop-package-batch-allocations
*/
static createShopPackageBatchAllocations(
data: CreateShopPackageBatchAllocationsRequest
): Promise<BaseResponse<ShopPackageBatchAllocationsResponse>> {
return this.post<BaseResponse<ShopPackageBatchAllocationsResponse>>(
'/api/admin/shop-package-batch-allocations',
data
)
}
/** /**
* 更新店铺套餐分配生效条件 * 更新店铺套餐分配生效条件
* PATCH /api/admin/shop-package-allocations/{id}/expiry-base * PATCH /api/admin/shop-package-allocations/{id}/expiry-base

View File

@@ -135,10 +135,23 @@ export interface ShopPackageAllocationResponse extends PackageAllocationExpiryBa
cost_price: number cost_price: number
} }
// 批量创建店铺套餐分配请求
export interface CreateShopPackageBatchAllocationsRequest {
shop_id: number
package_ids: number[]
cost_price?: number
expiry_base_override: PackageAllocationExpiryBaseOverride | null
}
// 批量创建店铺套餐分配响应
export interface ShopPackageBatchAllocationsResponse {
allocations: ShopPackageAllocationResponse[]
}
/** /**
* 套餐响应 * 套餐响应
*/ */
export interface PackageResponse { export interface PackageResponse extends PackageAllocationExpiryBaseFields {
id: number id: number
package_code: string package_code: string
package_name: string package_name: string
@@ -323,7 +336,8 @@ export interface CreateShopSeriesGrantRequest {
commission_tiers?: CommissionTier[] // 梯度配置列表,梯度模式时必填 commission_tiers?: CommissionTier[] // 梯度配置列表,梯度模式时必填
enable_force_recharge?: boolean // 是否启用强充 enable_force_recharge?: boolean // 是否启用强充
force_recharge_amount?: number // 强充金额(分) force_recharge_amount?: number // 强充金额(分)
packages?: GrantPackageInfo[] // 套餐列表 packages?: GrantPackageItem[] // 套餐列表
expiry_base_override: PackageAllocationExpiryBaseOverride | null
} }
/** /**
@@ -342,6 +356,7 @@ export interface UpdateShopSeriesGrantRequest {
export interface GrantPackageItem { export interface GrantPackageItem {
package_id?: number // 套餐ID package_id?: number // 套餐ID
cost_price?: number // 成本价(分) cost_price?: number // 成本价(分)
expiry_base_override?: PackageAllocationExpiryBaseOverride | null
remove?: boolean | null // 是否删除该套餐授权true=删除) remove?: boolean | null // 是否删除该套餐授权true=删除)
} }
@@ -350,4 +365,5 @@ export interface GrantPackageItem {
*/ */
export interface ManageGrantPackagesRequest { export interface ManageGrantPackagesRequest {
packages?: GrantPackageItem[] | null // 套餐操作列表 packages?: GrantPackageItem[] | null // 套餐操作列表
expiry_base_override: PackageAllocationExpiryBaseOverride | null // 新增套餐统一生效条件
} }

View File

@@ -168,6 +168,14 @@
return data.expiry_base === 'from_activation' ? '实名激活时起算' : '购买时起算' return data.expiry_base === 'from_activation' ? '实名激活时起算' : '购买时起算'
} }
}, },
{ label: '套餐默认生效条件', prop: 'default_expiry_base_name' },
{
label: '分配覆盖生效条件',
prop: 'expiry_base_override_name',
formatter: (_: unknown, data: PackageResponse) =>
data.expiry_base_override_name || '跟随套餐默认'
},
{ label: '最终生效条件', prop: 'effective_expiry_base_name' },
{ {
label: '状态', label: '状态',
formatter: (_: unknown, data: PackageResponse) => { formatter: (_: unknown, data: PackageResponse) => {

View File

@@ -625,6 +625,13 @@
return map[row.expiry_base] || row.expiry_base || '-' return map[row.expiry_base] || row.expiry_base || '-'
} }
}, },
{ label: '套餐默认生效条件', prop: 'default_expiry_base_name' },
{
label: '分配覆盖生效条件',
prop: 'expiry_base_override_name',
formatter: (row: PackageResponse) => row.expiry_base_override_name || '跟随套餐默认'
},
{ label: '最终生效条件', prop: 'effective_expiry_base_name' },
...(canUpdatePackageShelfStatus ? [{ label: '上架状态', prop: 'shelf_status' }] : []), ...(canUpdatePackageShelfStatus ? [{ label: '上架状态', prop: 'shelf_status' }] : []),
...(canUpdatePackageStatus ? [{ label: '状态', prop: 'status' }] : []), ...(canUpdatePackageStatus ? [{ label: '状态', prop: 'status' }] : []),
{ label: '创建时间', prop: 'created_at' }, { label: '创建时间', prop: 'created_at' },

View File

@@ -158,7 +158,7 @@
</ElTableColumn> </ElTableColumn>
<ElTableColumn label="覆盖生效条件"> <ElTableColumn label="覆盖生效条件">
<template #default="{ row }"> <template #default="{ row }">
{{ row.expiry_base_override_name || '-' }} {{ row.expiry_base_override_name || '跟随套餐默认' }}
</template> </template>
</ElTableColumn> </ElTableColumn>
<ElTableColumn label="最终生效条件"> <ElTableColumn label="最终生效条件">
@@ -291,7 +291,7 @@
{{ packageForm.default_expiry_base_name || '-' }} {{ packageForm.default_expiry_base_name || '-' }}
</ElFormItem> </ElFormItem>
<ElFormItem label="覆盖生效条件"> <ElFormItem label="覆盖生效条件">
{{ packageForm.expiry_base_override_name || '-' }} {{ packageForm.expiry_base_override_name || '跟随套餐默认' }}
</ElFormItem> </ElFormItem>
<ElFormItem label="最终生效条件"> <ElFormItem label="最终生效条件">
{{ packageForm.effective_expiry_base_name || '-' }} {{ packageForm.effective_expiry_base_name || '-' }}
@@ -593,14 +593,17 @@
submitLoading.value = true submitLoading.value = true
try { try {
if (packageDialogType.value === 'add') { if (packageDialogType.value === 'add') {
await ShopPackageAllocationService.createShopPackageAllocation({ await ShopSeriesGrantService.manageGrantPackages(grantId, {
shop_id: detailData.value!.shop_id,
package_id: packageForm.value.package_id!,
cost_price: Math.round(packageForm.value.cost_price_yuan * 100),
expiry_base_override: expiry_base_override:
packageForm.value.expiry_base_override === 'default' packageForm.value.expiry_base_override === 'default'
? null ? null
: packageForm.value.expiry_base_override : packageForm.value.expiry_base_override,
packages: [
{
package_id: packageForm.value.package_id,
cost_price: Math.round(packageForm.value.cost_price_yuan * 100)
}
]
}) })
} else { } else {
const expiryBaseChanged = const expiryBaseChanged =
@@ -617,22 +620,24 @@
cost_price: Math.round(packageForm.value.cost_price_yuan * 100) cost_price: Math.round(packageForm.value.cost_price_yuan * 100)
} }
] ]
await Promise.all([ if (expiryBaseChanged) {
ShopSeriesGrantService.manageGrantPackages(grantId, { packages }), await ShopPackageAllocationService.updateShopPackageAllocationExpiryBase(
...(expiryBaseChanged packageForm.value.allocation_id!,
? [ {
ShopPackageAllocationService.updateShopPackageAllocationExpiryBase( expiry_base_override:
packageForm.value.allocation_id!, packageForm.value.expiry_base_override === 'default'
{ ? null
expiry_base_override: : packageForm.value.expiry_base_override
packageForm.value.expiry_base_override === 'default' }
? null )
: packageForm.value.expiry_base_override }
} await ShopSeriesGrantService.manageGrantPackages(grantId, {
) expiry_base_override:
] packageForm.value.expiry_base_override === 'default'
: []) ? null
]) : packageForm.value.expiry_base_override,
packages
})
} }
ElMessage.success(packageDialogType.value === 'add' ? '添加成功' : '更新成功') ElMessage.success(packageDialogType.value === 'add' ? '添加成功' : '更新成功')
packageDialogVisible.value = false packageDialogVisible.value = false
@@ -667,7 +672,10 @@
} }
] ]
await ShopSeriesGrantService.manageGrantPackages(detailData.value.id, { packages }) await ShopSeriesGrantService.manageGrantPackages(detailData.value.id, {
expiry_base_override: row.expiry_base_override ?? null,
packages
})
ElMessage.success('删除成功') ElMessage.success('删除成功')
// 刷新详情数据 // 刷新详情数据

View File

@@ -171,18 +171,6 @@
{{ formatSuggestedRetailPrice(pkg.suggested_retail_price) }}) {{ formatSuggestedRetailPrice(pkg.suggested_retail_price) }})
</span> </span>
</div> </div>
<div class="expiry-base-input-wrapper">
<div class="expiry-base-label">生效条件</div>
<ElSelect
v-model="pkg.expiry_base_override"
class="expiry-base-select"
placeholder="请选择生效条件"
>
<ElOption label="跟随套餐默认" value="default" />
<ElOption label="购买即生效" value="from_purchase" />
<ElOption label="实名激活时生效" value="from_activation" />
</ElSelect>
</div>
<ElButton type="danger" size="small" @click="removePackage(index)" <ElButton type="danger" size="small" @click="removePackage(index)"
>删除</ElButton >删除</ElButton
> >
@@ -191,7 +179,16 @@
<div class="form-tip" <div class="form-tip"
>设置每个套餐的成本价单位不能低于成本价不能高于建议售价的50%溢价</div >设置每个套餐的成本价单位不能低于成本价不能高于建议售价的50%溢价</div
> >
<div class="form-tip">生效条件仅影响后续新订单不影响已购买套餐</div> </ElFormItem>
<ElFormItem label="套餐生效条件">
<ElSelect v-model="form.expiry_base_override" style="width: 100%">
<ElOption label="跟随套餐默认" value="default" />
<ElOption label="购买即生效" value="from_purchase" />
<ElOption label="实名激活时生效" value="from_activation" />
</ElSelect>
<div class="form-tip"
>本次所有授权套餐共用此选择仅影响后续新订单不影响已购买套餐</div
>
</ElFormItem> </ElFormItem>
</template> </template>
</div> </div>
@@ -560,7 +557,6 @@
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { import {
ShopSeriesGrantService, ShopSeriesGrantService,
ShopPackageAllocationService,
PackageSeriesService, PackageSeriesService,
ShopService, ShopService,
PackageManageService PackageManageService
@@ -787,12 +783,12 @@
force_recharge_amount: undefined, force_recharge_amount: undefined,
force_recharge_locked: false, // 强充是否被套餐系列锁定 force_recharge_locked: false, // 强充是否被套餐系列锁定
series_force_recharge_amount: 0, // 系列强充金额(元) series_force_recharge_amount: 0, // 系列强充金额(元)
expiry_base_override: 'default' as PackageAllocationExpiryBaseOverride | 'default',
packages: [] as Array<{ packages: [] as Array<{
package_id: number package_id: number
cost_price: number cost_price: number
original_cost_price?: number original_cost_price?: number
suggested_retail_price?: number suggested_retail_price?: number
expiry_base_override: PackageAllocationExpiryBaseOverride | 'default'
}> // 套餐配置 }> // 套餐配置
}) })
@@ -1153,8 +1149,7 @@
suggested_retail_price: suggested_retail_price:
pkg?.suggested_retail_price !== undefined && pkg?.suggested_retail_price !== null pkg?.suggested_retail_price !== undefined && pkg?.suggested_retail_price !== null
? pkg.suggested_retail_price / 100 ? pkg.suggested_retail_price / 100
: undefined, : undefined
expiry_base_override: 'default'
}) })
} }
}) })
@@ -1581,8 +1576,7 @@
pkgOption?.suggested_retail_price !== undefined && pkgOption?.suggested_retail_price !== undefined &&
pkgOption?.suggested_retail_price !== null pkgOption?.suggested_retail_price !== null
? pkgOption.suggested_retail_price / 100 ? pkgOption.suggested_retail_price / 100
: undefined, : undefined
expiry_base_override: pkg.expiry_base_override ?? 'default'
} }
}) })
selectedPackageIds.value = detail.packages.map((pkg) => pkg.package_id) selectedPackageIds.value = detail.packages.map((pkg) => pkg.package_id)
@@ -1616,6 +1610,7 @@
form.force_recharge_amount = undefined form.force_recharge_amount = undefined
form.force_recharge_locked = false form.force_recharge_locked = false
form.packages = [] form.packages = []
form.expiry_base_override = 'default'
selectedPackageIds.value = [] selectedPackageIds.value = []
packageOptions.value = [] // 清空套餐选项 packageOptions.value = [] // 清空套餐选项
} }
@@ -1639,6 +1634,7 @@
form.series_name = '' form.series_name = ''
form.shop_name = '' form.shop_name = ''
form.allocator_shop_name = '' form.allocator_shop_name = ''
form.expiry_base_override = 'default'
form.enable_one_time_commission = false form.enable_one_time_commission = false
form.commission_type = 'fixed' form.commission_type = 'fixed'
form.one_time_commission_amount = undefined form.one_time_commission_amount = undefined
@@ -1649,6 +1645,7 @@
form.force_recharge_amount = undefined form.force_recharge_amount = undefined
form.force_recharge_locked = false form.force_recharge_locked = false
form.packages = [] form.packages = []
form.expiry_base_override = 'default'
selectedPackageIds.value = [] selectedPackageIds.value = []
} }
@@ -1730,15 +1727,12 @@
data.force_recharge_amount = 0 data.force_recharge_amount = 0
} }
// 新增系列授权后,套餐通过独立分配接口创建,确保显式传递生效条件覆盖值。 const packageItems = form.packages.map((pkg: any) => ({
const packageAllocations = form.packages.map((pkg: any) => ({
package_id: pkg.package_id, package_id: pkg.package_id,
cost_price: Math.round((pkg.cost_price ?? 0) * 100), cost_price: Math.round((pkg.cost_price ?? 0) * 100)
expiry_base_override:
pkg.expiry_base_override === 'default' ? null : pkg.expiry_base_override
})) }))
if (packageAllocations.length > 0) { if (packageItems.length > 0) {
// 验证是否包含赠送套餐 // 验证是否包含赠送套餐
for (const pkg of form.packages) { for (const pkg of form.packages) {
const pkgInfo = packageOptions.value.find((p) => p.id === pkg.package_id) const pkgInfo = packageOptions.value.find((p) => p.id === pkg.package_id)
@@ -1776,20 +1770,10 @@
// 新增时需要必填字段 // 新增时需要必填字段
data.series_id = form.series_id data.series_id = form.series_id
data.shop_id = form.shop_id data.shop_id = form.shop_id
data.expiry_base_override =
form.expiry_base_override === 'default' ? null : form.expiry_base_override
data.packages = packageItems
await ShopSeriesGrantService.createShopSeriesGrant(data) await ShopSeriesGrantService.createShopSeriesGrant(data)
await Promise.all(
packageAllocations.map(
(pkg: {
package_id: number
cost_price: number
expiry_base_override: PackageAllocationExpiryBaseOverride | null
}) =>
ShopPackageAllocationService.createShopPackageAllocation({
shop_id: data.shop_id,
...pkg
})
)
)
ElMessage.success('新增成功') ElMessage.success('新增成功')
} else { } else {
// 编辑时调用更新接口 // 编辑时调用更新接口

View File

@@ -47,7 +47,7 @@
</ElTableColumn> </ElTableColumn>
<ElTableColumn label="覆盖生效条件" minWidth="150"> <ElTableColumn label="覆盖生效条件" minWidth="150">
<template #default="{ row }"> <template #default="{ row }">
{{ row.expiry_base_override_name || '-' }} {{ row.expiry_base_override_name || '跟随套餐默认' }}
</template> </template>
</ElTableColumn> </ElTableColumn>
<ElTableColumn label="最终生效条件" minWidth="150"> <ElTableColumn label="最终生效条件" minWidth="150">
@@ -144,7 +144,7 @@
{{ packageForm.default_expiry_base_name || '-' }} {{ packageForm.default_expiry_base_name || '-' }}
</ElFormItem> </ElFormItem>
<ElFormItem label="覆盖生效条件"> <ElFormItem label="覆盖生效条件">
{{ packageForm.expiry_base_override_name || '-' }} {{ packageForm.expiry_base_override_name || '跟随套餐默认' }}
</ElFormItem> </ElFormItem>
<ElFormItem label="最终生效条件"> <ElFormItem label="最终生效条件">
{{ packageForm.effective_expiry_base_name || '-' }} {{ packageForm.effective_expiry_base_name || '-' }}
@@ -211,7 +211,6 @@
const grantId = computed(() => Number(route.params.id)) const grantId = computed(() => Number(route.params.id))
const seriesId = ref<number>(0) const seriesId = ref<number>(0)
const shopId = ref<number>(0)
const grantName = ref<string>('') const grantName = ref<string>('')
const packageList = ref<GrantPackageInfo[]>([]) const packageList = ref<GrantPackageInfo[]>([])
const availablePackages = ref<PackageResponse[]>([]) const availablePackages = ref<PackageResponse[]>([])
@@ -325,7 +324,6 @@
if (res.code === 0) { if (res.code === 0) {
packageList.value = res.data.packages || [] packageList.value = res.data.packages || []
seriesId.value = res.data.series_id seriesId.value = res.data.series_id
shopId.value = res.data.shop_id
grantName.value = `${res.data.series_name} - ${res.data.shop_name}` grantName.value = `${res.data.series_name} - ${res.data.shop_name}`
} }
} catch (error) { } catch (error) {
@@ -437,14 +435,12 @@
} }
const costPrice = Math.round(currentCostPriceYuan * 100) const costPrice = Math.round(currentCostPriceYuan * 100)
if (packageDialogType.value === 'add') { if (packageDialogType.value === 'add') {
await ShopPackageAllocationService.createShopPackageAllocation({ await ShopSeriesGrantService.manageGrantPackages(grantId.value, {
shop_id: shopId.value,
package_id: packageForm.value.package_id!,
cost_price: costPrice,
expiry_base_override: expiry_base_override:
packageForm.value.expiry_base_override === 'default' packageForm.value.expiry_base_override === 'default'
? null ? null
: packageForm.value.expiry_base_override : packageForm.value.expiry_base_override,
packages: [{ package_id: packageForm.value.package_id, cost_price: costPrice }]
}) })
} else { } else {
const expiryBaseChanged = const expiryBaseChanged =
@@ -458,22 +454,24 @@
const packages: GrantPackageItem[] = [ const packages: GrantPackageItem[] = [
{ package_id: packageForm.value.package_id, cost_price: costPrice } { package_id: packageForm.value.package_id, cost_price: costPrice }
] ]
await Promise.all([ if (expiryBaseChanged) {
ShopSeriesGrantService.manageGrantPackages(grantId.value, { packages }), await ShopPackageAllocationService.updateShopPackageAllocationExpiryBase(
...(expiryBaseChanged packageForm.value.allocation_id!,
? [ {
ShopPackageAllocationService.updateShopPackageAllocationExpiryBase( expiry_base_override:
packageForm.value.allocation_id!, packageForm.value.expiry_base_override === 'default'
{ ? null
expiry_base_override: : packageForm.value.expiry_base_override
packageForm.value.expiry_base_override === 'default' }
? null )
: packageForm.value.expiry_base_override }
} await ShopSeriesGrantService.manageGrantPackages(grantId.value, {
) expiry_base_override:
] packageForm.value.expiry_base_override === 'default'
: []) ? null
]) : packageForm.value.expiry_base_override,
packages
})
} }
packageDialogVisible.value = false packageDialogVisible.value = false
await fetchPackageList() await fetchPackageList()
@@ -501,7 +499,10 @@
remove: true remove: true
} }
] ]
await ShopSeriesGrantService.manageGrantPackages(grantId.value, { packages }) await ShopSeriesGrantService.manageGrantPackages(grantId.value, {
expiry_base_override: row.expiry_base_override ?? null,
packages
})
await fetchPackageList() await fetchPackageList()
} catch (error) { } catch (error) {
console.error(error) console.error(error)