fix: 回调配置, 调整信用位置, 套餐
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m55s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m55s
This commit is contained in:
@@ -727,10 +727,10 @@
|
||||
package_name: '',
|
||||
series_id: undefined,
|
||||
package_type: '',
|
||||
calendar_type: undefined,
|
||||
calendar_type: null,
|
||||
duration_days: undefined,
|
||||
duration_months: 1,
|
||||
data_reset_cycle: undefined,
|
||||
data_reset_cycle: null,
|
||||
enable_virtual_data: false,
|
||||
expiry_base: 'from_activation',
|
||||
real_data_mb: 0,
|
||||
@@ -1345,10 +1345,10 @@
|
||||
form.package_name = data.package_name
|
||||
form.series_id = data.series_id
|
||||
form.package_type = data.package_type
|
||||
form.calendar_type = data.calendar_type || undefined
|
||||
form.calendar_type = data.calendar_type ?? null
|
||||
form.duration_days = data.duration_days
|
||||
form.duration_months = data.duration_months
|
||||
form.data_reset_cycle = data.data_reset_cycle || undefined
|
||||
form.data_reset_cycle = data.data_reset_cycle ?? null
|
||||
form.enable_virtual_data = data.enable_virtual_data || false
|
||||
form.expiry_base = data.expiry_base || 'from_activation'
|
||||
form.real_data_mb = data.real_data_mb || 0
|
||||
@@ -1384,10 +1384,10 @@
|
||||
form.package_name = ''
|
||||
form.series_id = undefined
|
||||
form.package_type = ''
|
||||
form.calendar_type = undefined
|
||||
form.calendar_type = null
|
||||
form.duration_days = undefined
|
||||
form.duration_months = 1
|
||||
form.data_reset_cycle = undefined
|
||||
form.data_reset_cycle = null
|
||||
form.enable_virtual_data = false
|
||||
form.expiry_base = 'from_activation'
|
||||
form.real_data_mb = 0
|
||||
@@ -1428,10 +1428,10 @@
|
||||
form.package_name = ''
|
||||
form.series_id = undefined
|
||||
form.package_type = ''
|
||||
form.calendar_type = undefined
|
||||
form.calendar_type = null
|
||||
form.duration_days = undefined
|
||||
form.duration_months = 1
|
||||
form.data_reset_cycle = undefined
|
||||
form.data_reset_cycle = null
|
||||
form.enable_virtual_data = false
|
||||
form.expiry_base = 'from_activation'
|
||||
form.real_data_mb = 0
|
||||
@@ -1483,6 +1483,8 @@
|
||||
package_type: form.package_type,
|
||||
duration_months: form.duration_months,
|
||||
cost_price: costPriceInCents,
|
||||
calendar_type: form.calendar_type ?? null,
|
||||
data_reset_cycle: form.data_reset_cycle ?? null,
|
||||
is_gift: form.is_gift
|
||||
}
|
||||
|
||||
@@ -1494,12 +1496,6 @@
|
||||
if (form.series_id !== undefined) {
|
||||
data.series_id = form.series_id
|
||||
}
|
||||
if (form.calendar_type) {
|
||||
data.calendar_type = form.calendar_type
|
||||
}
|
||||
if (form.data_reset_cycle) {
|
||||
data.data_reset_cycle = form.data_reset_cycle
|
||||
}
|
||||
// 只有 calendar_type 为 by_day 时才传 duration_days
|
||||
if (form.calendar_type === 'by_day' && form.duration_days !== undefined) {
|
||||
data.duration_days = form.duration_days
|
||||
|
||||
@@ -143,45 +143,23 @@
|
||||
|
||||
<!-- 套餐列表 -->
|
||||
<div v-if="detailData.packages && detailData.packages.length > 0" class="package-table">
|
||||
<ElTable :data="detailData.packages" border stripe>
|
||||
<ElTableColumn prop="package_name" label="套餐名称" />
|
||||
<ElTableColumn prop="package_code" label="套餐编码" />
|
||||
<ElTableColumn label="成本价">
|
||||
<template #default="{ row }">
|
||||
<span class="amount-value">¥{{ (row.cost_price / 100).toFixed(2) }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="套餐默认生效条件">
|
||||
<template #default="{ row }">
|
||||
{{ row.default_expiry_base_name || '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="覆盖生效条件">
|
||||
<template #default="{ row }">
|
||||
{{ row.expiry_base_override_name || '跟随套餐默认' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="最终生效条件">
|
||||
<template #default="{ row }">
|
||||
{{ row.effective_expiry_base_name || '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="上架状态" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElTag v-if="row.shelf_status === 1" type="success" size="small">上架</ElTag>
|
||||
<ElTag v-else-if="row.shelf_status === 2" type="info" size="small">下架</ElTag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElTag v-if="row.status === 1" type="success" size="small">启用</ElTag>
|
||||
<ElTag v-else-if="row.status === 2" type="danger" size="small">禁用</ElTag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="操作" width="150" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<SeriesGrantPackageTable
|
||||
:data="detailData.packages"
|
||||
:loading="loading"
|
||||
:show-actions="true"
|
||||
:actions-width="240"
|
||||
>
|
||||
<template #actions="{ row }">
|
||||
<div class="package-table-actions">
|
||||
<ElButton
|
||||
v-if="canUpdateExpiryBase"
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
@click="showExpiryBaseDialog(row)"
|
||||
>
|
||||
修改生效条件
|
||||
</ElButton>
|
||||
<ElButton
|
||||
type="primary"
|
||||
size="small"
|
||||
@@ -200,9 +178,9 @@
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
</div>
|
||||
</template>
|
||||
</SeriesGrantPackageTable>
|
||||
</div>
|
||||
<ElEmpty v-else description="暂无套餐" :image-size="80" />
|
||||
</div>
|
||||
@@ -214,95 +192,58 @@
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
|
||||
<!-- 添加/编辑套餐对话框 -->
|
||||
<ElDialog
|
||||
<SeriesGrantPackageDialog
|
||||
v-model="packageDialogVisible"
|
||||
:title="packageDialogType === 'add' ? '添加套餐' : '编辑套餐'"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
v-model:form="packageForm"
|
||||
:dialog-type="packageDialogType"
|
||||
:rules="packageRules"
|
||||
:available-packages="availablePackages"
|
||||
:package-loading="packageLoading"
|
||||
:submit-loading="submitLoading"
|
||||
:can-update-expiry-base="canUpdateExpiryBase"
|
||||
:has-series="Boolean(detailData?.series_id)"
|
||||
@search="searchPackages"
|
||||
@submit="handleSavePackage"
|
||||
@closed="handlePackageDialogClosed"
|
||||
/>
|
||||
|
||||
<ElDialog
|
||||
v-model="expiryDialogVisible"
|
||||
title="修改生效条件"
|
||||
width="460px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<ElForm ref="packageFormRef" :model="packageForm" :rules="packageRules" label-width="100px">
|
||||
<!-- 添加模式:选择套餐 -->
|
||||
<ElFormItem label="选择套餐" prop="package_id" v-if="packageDialogType === 'add'">
|
||||
<ElSelect
|
||||
v-model="packageForm.package_id"
|
||||
placeholder="请选择套餐"
|
||||
style="width: 100%"
|
||||
filterable
|
||||
remote
|
||||
:remote-method="searchPackages"
|
||||
:loading="packageLoading"
|
||||
clearable
|
||||
>
|
||||
<template
|
||||
v-if="availablePackages.length === 0 && !packageLoading && detailData?.series_id"
|
||||
>
|
||||
<ElOption disabled value="" label="该系列没有可选套餐" />
|
||||
</template>
|
||||
<ElOption
|
||||
v-for="pkg in availablePackages"
|
||||
:key="pkg.id"
|
||||
:label="`${pkg.package_name} (${pkg.package_code})`"
|
||||
:value="pkg.id"
|
||||
:disabled="pkg.is_authorized"
|
||||
/>
|
||||
</ElSelect>
|
||||
<ElForm label-width="130px">
|
||||
<ElFormItem label="套餐名称">
|
||||
<span class="ellipsis-value" :title="expiryForm.package_name || ''">
|
||||
{{ expiryForm.package_name || '-' }}
|
||||
</span>
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 编辑模式:显示套餐信息 -->
|
||||
<ElFormItem label="套餐名称" v-if="packageDialogType === 'edit'">
|
||||
<span>{{ packageForm.package_name }}</span>
|
||||
<ElFormItem label="套餐编码">
|
||||
<span class="ellipsis-value" :title="expiryForm.package_code || ''">
|
||||
{{ expiryForm.package_code || '-' }}
|
||||
</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="套餐编码" v-if="packageDialogType === 'edit'">
|
||||
<span>{{ packageForm.package_code }}</span>
|
||||
<ElFormItem label="套餐默认生效条件">
|
||||
{{ expiryForm.default_expiry_base_name || '-' }}
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 成本价 -->
|
||||
<ElFormItem label="成本价(元)" prop="cost_price_yuan">
|
||||
<ElInputNumber
|
||||
v-model="packageForm.cost_price_yuan"
|
||||
:precision="2"
|
||||
:step="0.01"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
placeholder="请输入成本价"
|
||||
/>
|
||||
<div v-if="packageForm.original_cost_price" class="form-tip">
|
||||
请参考{{ packageForm.package_name || '套餐' }} - 套餐成本价: ¥{{
|
||||
packageForm.original_cost_price.toFixed(2)
|
||||
}}
|
||||
</div>
|
||||
</ElFormItem>
|
||||
<ElFormItem v-if="packageDialogType === 'add' || canUpdateExpiryBase" label="生效条件">
|
||||
<ElSelect
|
||||
v-model="packageForm.expiry_base_override"
|
||||
placeholder="请选择生效条件"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElFormItem label="覆盖生效条件">
|
||||
<ElSelect v-model="expiryForm.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>
|
||||
<template v-if="packageDialogType === 'edit'">
|
||||
<ElFormItem label="套餐默认生效条件">
|
||||
{{ packageForm.default_expiry_base_name || '-' }}
|
||||
</ElFormItem>
|
||||
<ElFormItem label="覆盖生效条件">
|
||||
{{ packageForm.expiry_base_override_name || '跟随套餐默认' }}
|
||||
</ElFormItem>
|
||||
<ElFormItem label="最终生效条件">
|
||||
{{ packageForm.effective_expiry_base_name || '-' }}
|
||||
</ElFormItem>
|
||||
</template>
|
||||
<ElFormItem label="最终生效条件">
|
||||
{{ expiryForm.effective_expiry_base_name || '-' }}
|
||||
</ElFormItem>
|
||||
<div class="form-tip">仅影响后续新订单,不影响已购买套餐</div>
|
||||
</ElForm>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="packageDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleSavePackage" :loading="submitLoading">
|
||||
<ElButton @click="expiryDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" :loading="expirySubmitLoading" @click="saveExpiryBase">
|
||||
保存
|
||||
</ElButton>
|
||||
</div>
|
||||
@@ -330,10 +271,8 @@
|
||||
ElDialog,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElInputNumber,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
FormInstance,
|
||||
FormRules
|
||||
} from 'element-plus'
|
||||
import { ArrowLeft, Loading } from '@element-plus/icons-vue'
|
||||
@@ -354,9 +293,13 @@
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { getEnableStatusText, JULY_PERMISSIONS } from '@/config/constants'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import SeriesGrantPackageTable from '@/components/business/SeriesGrantPackageTable.vue'
|
||||
import SeriesGrantPackageDialog from '@/components/business/SeriesGrantPackageDialog.vue'
|
||||
import {
|
||||
mergeGrantPackageCandidates,
|
||||
type GrantPackageCandidate
|
||||
type GrantPackageCandidate,
|
||||
type PackageCostFormItem,
|
||||
type SeriesGrantPackageForm
|
||||
} from '@/utils/business/seriesGrantPackage'
|
||||
|
||||
defineOptions({ name: 'SeriesGrantsDetail' })
|
||||
@@ -374,61 +317,79 @@
|
||||
const packageDialogVisible = ref(false)
|
||||
const packageDialogType = ref<'add' | 'edit'>('add')
|
||||
const availablePackages = ref<GrantPackageCandidate[]>([])
|
||||
const packageFormRef = ref<FormInstance>()
|
||||
const expiryDialogVisible = ref(false)
|
||||
const expirySubmitLoading = ref(false)
|
||||
|
||||
type ExpiryBaseSelection = PackageAllocationExpiryBaseOverride | 'default'
|
||||
|
||||
// 套餐表单
|
||||
const packageForm = ref<{
|
||||
type ExpiryFormState = {
|
||||
package_id?: number
|
||||
allocation_id?: number
|
||||
package_name?: string
|
||||
package_code?: string
|
||||
original_cost_price?: number
|
||||
cost_price_yuan: number
|
||||
expiry_base_override: ExpiryBaseSelection
|
||||
initial_expiry_base_override: ExpiryBaseSelection
|
||||
default_expiry_base_name?: string | null
|
||||
expiry_base_override: ExpiryBaseSelection
|
||||
expiry_base_override_name?: string | null
|
||||
effective_expiry_base_name?: string | null
|
||||
}>({
|
||||
}
|
||||
|
||||
const expiryForm = ref<ExpiryFormState>({ expiry_base_override: 'default' })
|
||||
|
||||
const createDefaultPackageForm = (): SeriesGrantPackageForm => ({
|
||||
package_id: undefined,
|
||||
allocation_id: undefined,
|
||||
package_ids: [],
|
||||
package_name: undefined,
|
||||
package_code: undefined,
|
||||
cost_price_yuan: 0,
|
||||
expiry_base_override: 'default',
|
||||
initial_expiry_base_override: 'default'
|
||||
initial_expiry_base_override: 'default',
|
||||
packages: []
|
||||
})
|
||||
const packageForm = ref<SeriesGrantPackageForm>(createDefaultPackageForm())
|
||||
|
||||
// 表单验证规则
|
||||
const packageRules = computed<FormRules>(() => ({
|
||||
package_id: [
|
||||
{ required: packageDialogType.value === 'add', message: '请选择套餐', trigger: 'change' }
|
||||
package_ids: [
|
||||
{
|
||||
validator: (_rule, value, callback) => {
|
||||
if (packageDialogType.value !== 'add') {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
if (!Array.isArray(value) || value.length === 0) {
|
||||
callback(new Error('请至少选择一个套餐'))
|
||||
} else if (value.length > 100) {
|
||||
callback(new Error('最多选择100个套餐'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
cost_price_yuan: [
|
||||
{ required: true, message: '请输入成本价', trigger: 'blur' },
|
||||
{ type: 'number', min: 0, message: '成本价不能小于0', trigger: 'blur' }
|
||||
{
|
||||
required: packageDialogType.value === 'edit',
|
||||
message: '请输入成本价',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
validator: (_rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback(new Error('请输入成本价'))
|
||||
} else if (value < 0) {
|
||||
callback(new Error('成本价不能小于0'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}))
|
||||
|
||||
// 梯度配置表格数据(合并授权数据和系列数据)
|
||||
watch(
|
||||
() => packageForm.value.package_id,
|
||||
(packageId) => {
|
||||
if (packageDialogType.value !== 'add' || !packageId) return
|
||||
|
||||
const selectedPackage = availablePackages.value.find((pkg) => pkg.id === packageId)
|
||||
if (!selectedPackage) return
|
||||
|
||||
const originalCostPrice =
|
||||
selectedPackage.cost_price !== undefined && selectedPackage.cost_price !== null
|
||||
? selectedPackage.cost_price / 100
|
||||
: 0
|
||||
|
||||
packageForm.value.package_name = selectedPackage.package_name
|
||||
packageForm.value.package_code = selectedPackage.package_code
|
||||
packageForm.value.original_cost_price = originalCostPrice
|
||||
packageForm.value.cost_price_yuan = originalCostPrice
|
||||
}
|
||||
)
|
||||
|
||||
const tierTableData = computed(() => {
|
||||
if (
|
||||
!detailData.value?.commission_tiers ||
|
||||
@@ -507,19 +468,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 显示添加套餐对话框
|
||||
const assignPackageForm = (values: Partial<SeriesGrantPackageForm> = {}) => {
|
||||
Object.assign(packageForm.value, createDefaultPackageForm(), values)
|
||||
}
|
||||
|
||||
const resolvePackagePricing = (row?: GrantPackageInfo) => {
|
||||
const pkgOption = availablePackages.value.find((pkg) => pkg.id === row?.package_id)
|
||||
const currentCostPrice = row?.cost_price !== undefined ? row.cost_price / 100 : 0
|
||||
|
||||
return {
|
||||
original_cost_price:
|
||||
pkgOption?.cost_price !== undefined && pkgOption.cost_price !== null
|
||||
? pkgOption.cost_price / 100
|
||||
: currentCostPrice,
|
||||
suggested_retail_price:
|
||||
pkgOption?.suggested_retail_price !== undefined && pkgOption.suggested_retail_price !== null
|
||||
? pkgOption.suggested_retail_price / 100
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 显示添加套餐对话框
|
||||
const showAddPackageDialog = () => {
|
||||
packageDialogType.value = 'add'
|
||||
packageForm.value = {
|
||||
package_id: undefined,
|
||||
allocation_id: undefined,
|
||||
package_name: undefined,
|
||||
package_code: undefined,
|
||||
original_cost_price: undefined,
|
||||
cost_price_yuan: 0,
|
||||
expiry_base_override: 'default',
|
||||
initial_expiry_base_override: 'default'
|
||||
}
|
||||
assignPackageForm({ package_ids: [], packages: [] })
|
||||
|
||||
// 加载可用套餐
|
||||
if (detailData.value?.series_id) {
|
||||
@@ -532,23 +505,44 @@
|
||||
// 显示编辑套餐对话框
|
||||
const showEditPackageDialog = (row: GrantPackageInfo) => {
|
||||
packageDialogType.value = 'edit'
|
||||
packageForm.value = {
|
||||
const packagePricing = resolvePackagePricing(row)
|
||||
|
||||
assignPackageForm({
|
||||
package_id: row.package_id,
|
||||
allocation_id: row.allocation_id,
|
||||
package_name: row.package_name,
|
||||
package_code: row.package_code,
|
||||
original_cost_price: row.cost_price / 100,
|
||||
original_cost_price: packagePricing.original_cost_price,
|
||||
cost_price_yuan: row.cost_price / 100,
|
||||
suggested_retail_price: packagePricing.suggested_retail_price,
|
||||
expiry_base_override: row.expiry_base_override ?? 'default',
|
||||
initial_expiry_base_override: row.expiry_base_override ?? 'default',
|
||||
default_expiry_base_name: row.default_expiry_base_name,
|
||||
expiry_base_override_name: row.expiry_base_override_name,
|
||||
effective_expiry_base_name: row.effective_expiry_base_name
|
||||
}
|
||||
})
|
||||
|
||||
packageDialogVisible.value = true
|
||||
}
|
||||
|
||||
const showExpiryBaseDialog = (row: GrantPackageInfo) => {
|
||||
if (row.allocation_id === undefined || row.allocation_id === null) {
|
||||
ElMessage.error('该套餐分配记录缺少套餐分配 ID,无法修改生效条件')
|
||||
return
|
||||
}
|
||||
expiryForm.value = {
|
||||
package_id: row.package_id,
|
||||
allocation_id: row.allocation_id,
|
||||
package_name: row.package_name,
|
||||
package_code: row.package_code,
|
||||
default_expiry_base_name: row.default_expiry_base_name,
|
||||
expiry_base_override: row.expiry_base_override ?? 'default',
|
||||
expiry_base_override_name: row.expiry_base_override_name,
|
||||
effective_expiry_base_name: row.effective_expiry_base_name
|
||||
}
|
||||
expiryDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 加载可用套餐
|
||||
const loadAvailablePackages = async (packageName?: string) => {
|
||||
if (!detailData.value?.series_id) return
|
||||
@@ -596,79 +590,139 @@
|
||||
}
|
||||
}
|
||||
|
||||
const createPackageCostFormItem = (packageId: number): PackageCostFormItem => {
|
||||
const selectedPackage = availablePackages.value.find((pkg) => pkg.id === packageId)
|
||||
const originalCostPrice = selectedPackage?.cost_price ? selectedPackage.cost_price / 100 : 0
|
||||
return {
|
||||
package_id: packageId,
|
||||
package_name: selectedPackage?.package_name,
|
||||
package_code: selectedPackage?.package_code,
|
||||
cost_price_yuan: originalCostPrice,
|
||||
original_cost_price: originalCostPrice,
|
||||
suggested_retail_price:
|
||||
selectedPackage?.suggested_retail_price !== undefined &&
|
||||
selectedPackage.suggested_retail_price !== null
|
||||
? selectedPackage.suggested_retail_price / 100
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
|
||||
const getPackageCostPriceMax = (pkg?: {
|
||||
original_cost_price?: number
|
||||
suggested_retail_price?: number | null
|
||||
}) => {
|
||||
if (pkg?.suggested_retail_price !== undefined && pkg.suggested_retail_price !== null) {
|
||||
return Number((pkg.suggested_retail_price * 1.5).toFixed(2))
|
||||
}
|
||||
if (pkg?.original_cost_price !== undefined && pkg.original_cost_price !== null) {
|
||||
return Number((pkg.original_cost_price * 1.5).toFixed(2))
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
watch(
|
||||
() => packageForm.value.package_ids,
|
||||
(packageIds) => {
|
||||
if (packageDialogType.value !== 'add') return
|
||||
const existingItems = new Map(
|
||||
packageForm.value.packages.map((item) => [item.package_id, item])
|
||||
)
|
||||
packageForm.value.packages = packageIds.map(
|
||||
(packageId) => existingItems.get(packageId) || createPackageCostFormItem(packageId)
|
||||
)
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 保存套餐
|
||||
const handleSavePackage = async () => {
|
||||
if (!packageFormRef.value || !detailData.value) return
|
||||
if (!detailData.value) return
|
||||
|
||||
const grantId = detailData.value.id // 保存到常量中避免类型检查问题
|
||||
await packageFormRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
|
||||
submitLoading.value = true
|
||||
try {
|
||||
if (packageDialogType.value === 'add') {
|
||||
await ShopSeriesGrantService.manageGrantPackages(grantId, {
|
||||
expiry_base_override:
|
||||
packageForm.value.expiry_base_override === 'default'
|
||||
? null
|
||||
: packageForm.value.expiry_base_override,
|
||||
packages: [
|
||||
{
|
||||
package_id: packageForm.value.package_id!,
|
||||
cost_price: Math.round(packageForm.value.cost_price_yuan * 100)
|
||||
}
|
||||
]
|
||||
})
|
||||
} else {
|
||||
const expiryBaseChanged =
|
||||
packageForm.value.expiry_base_override !==
|
||||
packageForm.value.initial_expiry_base_override
|
||||
if (
|
||||
expiryBaseChanged &&
|
||||
(packageForm.value.allocation_id === undefined ||
|
||||
packageForm.value.allocation_id === null)
|
||||
) {
|
||||
ElMessage.error('该套餐分配记录缺少套餐分配 ID,无法修改生效条件')
|
||||
return
|
||||
const grantId = detailData.value.id
|
||||
submitLoading.value = true
|
||||
try {
|
||||
if (packageDialogType.value === 'add') {
|
||||
if (packageForm.value.packages.length === 0) {
|
||||
ElMessage.error('请至少选择一个套餐')
|
||||
return
|
||||
}
|
||||
const packages: GrantPackageItem[] = packageForm.value.packages.map((pkg) => {
|
||||
const currentCostPrice = Number(pkg.cost_price_yuan)
|
||||
if (!Number.isFinite(currentCostPrice) || currentCostPrice < 0) {
|
||||
throw new Error(`套餐 ${pkg.package_name || pkg.package_id} 的成本价无效`)
|
||||
}
|
||||
|
||||
const packages: GrantPackageItem[] = [
|
||||
{
|
||||
package_id: packageForm.value.package_id!,
|
||||
cost_price: Math.round(packageForm.value.cost_price_yuan * 100)
|
||||
}
|
||||
]
|
||||
if (expiryBaseChanged) {
|
||||
await ShopPackageAllocationService.updateShopPackageAllocationExpiryBase(
|
||||
packageForm.value.allocation_id!,
|
||||
{
|
||||
expiry_base_override:
|
||||
packageForm.value.expiry_base_override === 'default'
|
||||
? null
|
||||
: packageForm.value.expiry_base_override
|
||||
}
|
||||
const maxCostPrice = getPackageCostPriceMax(pkg)
|
||||
if (maxCostPrice !== undefined && currentCostPrice > maxCostPrice) {
|
||||
throw new Error(
|
||||
`套餐 ${pkg.package_name || pkg.package_id} 的成本价不能高于 ¥${maxCostPrice.toFixed(2)}`
|
||||
)
|
||||
}
|
||||
await ShopSeriesGrantService.manageGrantPackages(grantId, {
|
||||
expiry_base_override:
|
||||
packageForm.value.expiry_base_override === 'default'
|
||||
? null
|
||||
: packageForm.value.expiry_base_override,
|
||||
packages
|
||||
})
|
||||
if (pkg.original_cost_price !== undefined && currentCostPrice < pkg.original_cost_price) {
|
||||
throw new Error(
|
||||
`套餐 ${pkg.package_name || pkg.package_id} 的成本价不能低于 ¥${pkg.original_cost_price.toFixed(2)}`
|
||||
)
|
||||
}
|
||||
return {
|
||||
package_id: pkg.package_id,
|
||||
cost_price: Math.round(currentCostPrice * 100)
|
||||
}
|
||||
})
|
||||
await ShopSeriesGrantService.manageGrantPackages(grantId, {
|
||||
expiry_base_override:
|
||||
packageForm.value.expiry_base_override === 'default'
|
||||
? null
|
||||
: packageForm.value.expiry_base_override,
|
||||
packages
|
||||
})
|
||||
} else {
|
||||
const currentCostPriceYuan = Number(packageForm.value.cost_price_yuan)
|
||||
if (Number.isNaN(currentCostPriceYuan)) {
|
||||
throw new Error('当前成本价无效,无法保存')
|
||||
}
|
||||
const costPrice = Math.round(currentCostPriceYuan * 100)
|
||||
const expiryBaseChanged =
|
||||
packageForm.value.expiry_base_override !== packageForm.value.initial_expiry_base_override
|
||||
if (
|
||||
expiryBaseChanged &&
|
||||
(packageForm.value.allocation_id === undefined ||
|
||||
packageForm.value.allocation_id === null)
|
||||
) {
|
||||
ElMessage.error('该套餐分配记录缺少套餐分配 ID,无法修改生效条件')
|
||||
return
|
||||
}
|
||||
ElMessage.success(packageDialogType.value === 'add' ? '添加成功' : '更新成功')
|
||||
packageDialogVisible.value = false
|
||||
|
||||
// 刷新详情数据
|
||||
await fetchDetail()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
console.log(packageDialogType.value === 'add' ? '添加失败' : '更新失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
const packages: GrantPackageItem[] = [
|
||||
{ package_id: packageForm.value.package_id!, cost_price: costPrice }
|
||||
]
|
||||
if (expiryBaseChanged) {
|
||||
await ShopPackageAllocationService.updateShopPackageAllocationExpiryBase(
|
||||
packageForm.value.allocation_id!,
|
||||
{
|
||||
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' ? '添加成功' : '更新成功')
|
||||
packageDialogVisible.value = false
|
||||
|
||||
// 刷新详情数据
|
||||
await fetchDetail()
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '保存套餐失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除套餐
|
||||
@@ -690,7 +744,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('删除成功')
|
||||
|
||||
// 刷新详情数据
|
||||
@@ -707,17 +764,35 @@
|
||||
})
|
||||
}
|
||||
|
||||
const saveExpiryBase = async () => {
|
||||
if (expiryForm.value.allocation_id === undefined || expiryForm.value.allocation_id === null) {
|
||||
return
|
||||
}
|
||||
|
||||
expirySubmitLoading.value = true
|
||||
try {
|
||||
const response = await ShopPackageAllocationService.updateShopPackageAllocationExpiryBase(
|
||||
expiryForm.value.allocation_id,
|
||||
{
|
||||
expiry_base_override:
|
||||
expiryForm.value.expiry_base_override === 'default'
|
||||
? null
|
||||
: expiryForm.value.expiry_base_override
|
||||
}
|
||||
)
|
||||
if (response.code === 0) {
|
||||
ElMessage.success('生效条件更新成功')
|
||||
expiryDialogVisible.value = false
|
||||
await fetchDetail()
|
||||
}
|
||||
} finally {
|
||||
expirySubmitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭对话框
|
||||
const handlePackageDialogClosed = () => {
|
||||
packageFormRef.value?.resetFields()
|
||||
packageForm.value = {
|
||||
package_name: undefined,
|
||||
package_code: undefined,
|
||||
original_cost_price: undefined,
|
||||
cost_price_yuan: 0,
|
||||
expiry_base_override: 'default',
|
||||
initial_expiry_base_override: 'default'
|
||||
}
|
||||
assignPackageForm()
|
||||
availablePackages.value = []
|
||||
}
|
||||
|
||||
@@ -764,6 +839,13 @@
|
||||
|
||||
.package-table {
|
||||
margin-top: 12px;
|
||||
|
||||
.package-table-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tier-table-wrapper {
|
||||
@@ -794,6 +876,21 @@
|
||||
color: var(--el-color-warning);
|
||||
}
|
||||
|
||||
.ellipsis-value {
|
||||
display: block;
|
||||
width: 260px;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@@ -21,189 +21,60 @@
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
<ArtTable
|
||||
ref="tableRef"
|
||||
row-key="package_id"
|
||||
:loading="loading"
|
||||
<SeriesGrantPackageTable
|
||||
:data="packageList"
|
||||
:marginTop="10"
|
||||
:pagination="false"
|
||||
:actions="getActions"
|
||||
:actionsWidth="240"
|
||||
:inlineActionsCount="3"
|
||||
:loading="loading"
|
||||
:show-actions="true"
|
||||
:actions-width="240"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn
|
||||
prop="package_name"
|
||||
label="套餐名称"
|
||||
minWidth="220"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<ElTableColumn
|
||||
prop="package_code"
|
||||
label="套餐编码"
|
||||
minWidth="180"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<ElTableColumn label="成本价" width="120">
|
||||
<template #default="{ row }">
|
||||
<span class="amount-value">¥{{ (row.cost_price / 100).toFixed(2) }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="套餐默认生效条件" minWidth="150">
|
||||
<template #default="{ row }">
|
||||
{{ row.default_expiry_base_name || '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="覆盖生效条件" minWidth="150">
|
||||
<template #default="{ row }">
|
||||
{{ row.expiry_base_override_name || '跟随套餐默认' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="最终生效条件" minWidth="150">
|
||||
<template #default="{ row }">
|
||||
{{ row.effective_expiry_base_name || '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="上架状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElTag v-if="row.shelf_status === 1" type="success" size="small">上架</ElTag>
|
||||
<ElTag v-else-if="row.shelf_status === 2" type="info" size="small">下架</ElTag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<ElTag v-if="row.status === 1" type="success" size="small">启用</ElTag>
|
||||
<ElTag v-else-if="row.status === 2" type="danger" size="small">禁用</ElTag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<template #actions="{ row }">
|
||||
<div class="package-table-actions">
|
||||
<ElButton
|
||||
v-if="canUpdateExpiryBase"
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
@click="showExpiryBaseDialog(row)"
|
||||
>
|
||||
修改生效条件
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-if="hasAuth('series_grants:edit_packages_list')"
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
@click="showEditPackageDialog(row)"
|
||||
>
|
||||
编辑
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-if="hasAuth('series_grants:delete_packages_list')"
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
@click="handleDeletePackage(row)"
|
||||
>
|
||||
删除
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ArtTable>
|
||||
</SeriesGrantPackageTable>
|
||||
</ElCard>
|
||||
|
||||
<ElDialog
|
||||
<SeriesGrantPackageDialog
|
||||
v-model="packageDialogVisible"
|
||||
:title="packageDialogType === 'add' ? '添加套餐' : '编辑套餐'"
|
||||
width="40%"
|
||||
:close-on-click-modal="false"
|
||||
v-model:form="packageForm"
|
||||
:dialog-type="packageDialogType"
|
||||
:rules="packageRules"
|
||||
:available-packages="availablePackages"
|
||||
:package-loading="packageLoading"
|
||||
:submit-loading="submitLoading"
|
||||
:can-update-expiry-base="canUpdateExpiryBase"
|
||||
:has-series="Boolean(seriesId)"
|
||||
@search="searchAvailablePackages"
|
||||
@submit="handleSavePackage"
|
||||
@closed="handlePackageDialogClosed"
|
||||
>
|
||||
<ElForm ref="packageFormRef" :model="packageForm" :rules="packageRules" label-width="140px">
|
||||
<ElFormItem label="选择套餐" prop="package_ids" v-if="packageDialogType === 'add'">
|
||||
<ElSelect
|
||||
v-model="packageForm.package_ids"
|
||||
placeholder="请选择套餐(可多选)"
|
||||
style="width: 100%"
|
||||
filterable
|
||||
remote
|
||||
:remote-method="searchAvailablePackages"
|
||||
:loading="packageLoading"
|
||||
clearable
|
||||
multiple
|
||||
:multiple-limit="100"
|
||||
>
|
||||
<template v-if="availablePackages.length === 0 && !packageLoading && seriesId">
|
||||
<ElOption disabled value="" label="该系列没有可选套餐" />
|
||||
</template>
|
||||
<ElOption
|
||||
v-for="pkg in availablePackages"
|
||||
:key="pkg.id"
|
||||
:label="`${pkg.package_name} (${pkg.package_code})`"
|
||||
:value="pkg.id"
|
||||
:disabled="pkg.is_authorized"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem
|
||||
v-if="packageDialogType === 'add' && packageForm.packages.length"
|
||||
label="套餐配置"
|
||||
prop="packages"
|
||||
>
|
||||
<div class="package-config-list">
|
||||
<div
|
||||
v-for="pkg in packageForm.packages"
|
||||
:key="pkg.package_id"
|
||||
class="package-config-item"
|
||||
>
|
||||
<div class="package-config-name" :title="pkg.package_name || ''">
|
||||
{{ pkg.package_name || '套餐名称不可用' }}
|
||||
</div>
|
||||
<div class="package-config-cost">
|
||||
<span class="package-config-field-label">套餐成本价(元)</span>
|
||||
<ElInputNumber
|
||||
v-model="pkg.cost_price_yuan"
|
||||
:min="pkg.original_cost_price || 0"
|
||||
:max="getPackageCostPriceMax(pkg)"
|
||||
:precision="2"
|
||||
:step="0.01"
|
||||
:controls="false"
|
||||
placeholder="请输入成本价"
|
||||
style="width: 150px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="套餐名称" v-if="packageDialogType === 'edit'">
|
||||
<span>{{ packageForm.package_name }}</span>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="套餐编码" v-if="packageDialogType === 'edit'">
|
||||
<span>{{ packageForm.package_code }}</span>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="成本价(元)" prop="cost_price_yuan">
|
||||
<ElInputNumber
|
||||
v-model="packageForm.cost_price_yuan"
|
||||
:precision="2"
|
||||
:step="0.01"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
placeholder="请输入成本价"
|
||||
/>
|
||||
<div v-if="packageForm.original_cost_price" class="form-tip">
|
||||
请参考{{ packageForm.package_name || '套餐' }} - 套餐成本价: ¥{{
|
||||
packageForm.original_cost_price.toFixed(2)
|
||||
}}
|
||||
</div>
|
||||
</ElFormItem>
|
||||
<ElFormItem v-if="packageDialogType === 'add' || canUpdateExpiryBase" label="生效条件">
|
||||
<ElSelect
|
||||
v-model="packageForm.expiry_base_override"
|
||||
placeholder="请选择生效条件"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption label="跟随套餐默认" value="default" />
|
||||
<ElOption label="购买即生效" value="from_purchase" />
|
||||
<ElOption label="实名激活时生效" value="from_activation" />
|
||||
</ElSelect>
|
||||
<div class="form-tip">仅影响后续新订单,不影响已购买套餐</div>
|
||||
</ElFormItem>
|
||||
<template v-if="packageDialogType === 'edit'">
|
||||
<ElFormItem label="套餐默认生效条件">
|
||||
{{ packageForm.default_expiry_base_name || '-' }}
|
||||
</ElFormItem>
|
||||
<ElFormItem label="覆盖生效条件">
|
||||
{{ packageForm.expiry_base_override_name || '跟随套餐默认' }}
|
||||
</ElFormItem>
|
||||
<ElFormItem label="最终生效条件">
|
||||
{{ packageForm.effective_expiry_base_name || '-' }}
|
||||
</ElFormItem>
|
||||
</template>
|
||||
</ElForm>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="packageDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleSavePackage" :loading="submitLoading">
|
||||
保存
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
/>
|
||||
|
||||
<ElDialog
|
||||
v-model="expiryDialogVisible"
|
||||
@@ -252,22 +123,18 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch, nextTick } from 'vue'
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
ElCard,
|
||||
ElButton,
|
||||
ElIcon,
|
||||
ElTableColumn,
|
||||
ElTag,
|
||||
ElDialog,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElInputNumber,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElMessage,
|
||||
FormInstance,
|
||||
FormRules
|
||||
} from 'element-plus'
|
||||
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||
@@ -283,9 +150,13 @@
|
||||
} from '@/types/api'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { JULY_PERMISSIONS } from '@/config/constants'
|
||||
import SeriesGrantPackageTable from '@/components/business/SeriesGrantPackageTable.vue'
|
||||
import SeriesGrantPackageDialog from '@/components/business/SeriesGrantPackageDialog.vue'
|
||||
import {
|
||||
mergeGrantPackageCandidates,
|
||||
type GrantPackageCandidate
|
||||
type GrantPackageCandidate,
|
||||
type SeriesGrantPackageForm,
|
||||
type PackageCostFormItem
|
||||
} from '@/utils/business/seriesGrantPackage'
|
||||
|
||||
defineOptions({ name: 'SeriesGrantPackages' })
|
||||
@@ -298,15 +169,11 @@
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const packageLoading = ref(false)
|
||||
const tableRef = ref()
|
||||
|
||||
const grantId = computed(() => Number(route.params.id))
|
||||
const seriesId = ref<number>(0)
|
||||
const grantName = ref<string>('')
|
||||
const packageList = ref<GrantPackageInfo[]>([])
|
||||
const availablePackages = ref<GrantPackageCandidate[]>([])
|
||||
const packageFormRef = ref<FormInstance>()
|
||||
|
||||
const packageDialogVisible = ref(false)
|
||||
const packageDialogType = ref<'add' | 'edit'>('add')
|
||||
const expiryDialogVisible = ref(false)
|
||||
@@ -314,32 +181,6 @@
|
||||
|
||||
type ExpiryBaseSelection = PackageAllocationExpiryBaseOverride | 'default'
|
||||
|
||||
type PackageCostFormItem = {
|
||||
package_id: number
|
||||
package_name?: string
|
||||
package_code?: string
|
||||
cost_price_yuan: number
|
||||
original_cost_price?: number
|
||||
suggested_retail_price?: number
|
||||
}
|
||||
|
||||
type PackageFormState = {
|
||||
package_id?: number
|
||||
allocation_id?: number
|
||||
package_ids: number[]
|
||||
package_name?: string
|
||||
package_code?: string
|
||||
cost_price_yuan: number
|
||||
original_cost_price?: number
|
||||
suggested_retail_price?: number
|
||||
expiry_base_override: ExpiryBaseSelection
|
||||
initial_expiry_base_override: ExpiryBaseSelection
|
||||
default_expiry_base_name?: string | null
|
||||
expiry_base_override_name?: string | null
|
||||
effective_expiry_base_name?: string | null
|
||||
packages: PackageCostFormItem[]
|
||||
}
|
||||
|
||||
type ExpiryFormState = {
|
||||
package_id?: number
|
||||
allocation_id?: number
|
||||
@@ -351,7 +192,7 @@
|
||||
effective_expiry_base_name?: string | null
|
||||
}
|
||||
|
||||
const createDefaultPackageForm = (): PackageFormState => ({
|
||||
const createDefaultPackageForm = (): SeriesGrantPackageForm => ({
|
||||
package_id: undefined,
|
||||
allocation_id: undefined,
|
||||
package_ids: [],
|
||||
@@ -362,10 +203,10 @@
|
||||
initial_expiry_base_override: 'default',
|
||||
packages: []
|
||||
})
|
||||
const packageForm = ref<PackageFormState>(createDefaultPackageForm())
|
||||
const packageForm = ref<SeriesGrantPackageForm>(createDefaultPackageForm())
|
||||
const expiryForm = ref<ExpiryFormState>({ expiry_base_override: 'default' })
|
||||
|
||||
const assignPackageForm = (values: Partial<PackageFormState> = {}) => {
|
||||
const assignPackageForm = (values: Partial<SeriesGrantPackageForm> = {}) => {
|
||||
Object.assign(packageForm.value, createDefaultPackageForm(), values)
|
||||
}
|
||||
|
||||
@@ -426,36 +267,6 @@
|
||||
]
|
||||
}))
|
||||
|
||||
const getActions = (row: GrantPackageInfo) => {
|
||||
const actions: any[] = []
|
||||
|
||||
if (canUpdateExpiryBase) {
|
||||
actions.push({
|
||||
label: '修改生效条件',
|
||||
handler: () => showExpiryBaseDialog(row),
|
||||
type: 'primary'
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('series_grants:edit_packages_list')) {
|
||||
actions.push({
|
||||
label: '编辑',
|
||||
handler: () => showEditPackageDialog(row),
|
||||
type: 'primary'
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('series_grants:delete_packages_list')) {
|
||||
actions.push({
|
||||
label: '删除',
|
||||
handler: () => handleDeletePackage(row),
|
||||
type: 'danger'
|
||||
})
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
router.back()
|
||||
@@ -484,7 +295,6 @@
|
||||
assignPackageForm({ package_ids: [], packages: [] })
|
||||
loadAvailablePackages()
|
||||
packageDialogVisible.value = true
|
||||
nextTick(() => packageFormRef.value?.clearValidate())
|
||||
}
|
||||
|
||||
const showEditPackageDialog = (row: GrantPackageInfo) => {
|
||||
@@ -506,7 +316,6 @@
|
||||
effective_expiry_base_name: row.effective_expiry_base_name
|
||||
})
|
||||
packageDialogVisible.value = true
|
||||
nextTick(() => packageFormRef.value?.clearValidate())
|
||||
}
|
||||
|
||||
const showExpiryBaseDialog = (row: GrantPackageInfo) => {
|
||||
@@ -605,120 +414,90 @@
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => packageForm.value.package_id,
|
||||
(packageId) => {
|
||||
if (packageDialogType.value === 'add' && packageId) {
|
||||
const selectedPackage = availablePackages.value.find((p) => p.id === packageId)
|
||||
if (selectedPackage) {
|
||||
const originalCostPrice = selectedPackage.cost_price
|
||||
? selectedPackage.cost_price / 100
|
||||
: 0
|
||||
const suggestedRetailPrice =
|
||||
selectedPackage.suggested_retail_price !== undefined &&
|
||||
selectedPackage.suggested_retail_price !== null
|
||||
? selectedPackage.suggested_retail_price / 100
|
||||
: undefined
|
||||
packageForm.value.original_cost_price = originalCostPrice
|
||||
packageForm.value.suggested_retail_price = suggestedRetailPrice
|
||||
packageForm.value.cost_price_yuan = originalCostPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const handleSavePackage = async () => {
|
||||
if (!packageFormRef.value || !grantId.value) return
|
||||
if (!grantId.value) return
|
||||
|
||||
await packageFormRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
|
||||
submitLoading.value = true
|
||||
try {
|
||||
submitLoading.value = true
|
||||
try {
|
||||
if (packageDialogType.value === 'add') {
|
||||
if (packageForm.value.packages.length === 0) {
|
||||
ElMessage.error('请至少选择一个套餐')
|
||||
return
|
||||
}
|
||||
const packages: GrantPackageItem[] = packageForm.value.packages.map((pkg) => {
|
||||
const currentCostPrice = Number(pkg.cost_price_yuan)
|
||||
if (!Number.isFinite(currentCostPrice) || currentCostPrice < 0) {
|
||||
throw new Error(`套餐 ${pkg.package_name || pkg.package_id} 的成本价无效`)
|
||||
}
|
||||
const maxCostPrice = getPackageCostPriceMax(pkg)
|
||||
if (maxCostPrice !== undefined && currentCostPrice > maxCostPrice) {
|
||||
throw new Error(
|
||||
`套餐 ${pkg.package_name || pkg.package_id} 的成本价不能高于 ¥${maxCostPrice.toFixed(2)}`
|
||||
)
|
||||
}
|
||||
if (pkg.original_cost_price !== undefined && currentCostPrice < pkg.original_cost_price) {
|
||||
throw new Error(
|
||||
`套餐 ${pkg.package_name || pkg.package_id} 的成本价不能低于 ¥${pkg.original_cost_price.toFixed(2)}`
|
||||
)
|
||||
}
|
||||
return {
|
||||
package_id: pkg.package_id,
|
||||
cost_price: Math.round(currentCostPrice * 100)
|
||||
}
|
||||
})
|
||||
await ShopSeriesGrantService.manageGrantPackages(grantId.value, {
|
||||
expiry_base_override:
|
||||
packageForm.value.expiry_base_override === 'default'
|
||||
? null
|
||||
: packageForm.value.expiry_base_override,
|
||||
packages
|
||||
})
|
||||
} else {
|
||||
const currentCostPriceYuan = Number(packageForm.value.cost_price_yuan)
|
||||
if (Number.isNaN(currentCostPriceYuan)) {
|
||||
throw new Error('当前成本价无效,无法保存')
|
||||
}
|
||||
const costPrice = Math.round(currentCostPriceYuan * 100)
|
||||
if (packageDialogType.value === 'add') {
|
||||
if (packageForm.value.packages.length === 0) {
|
||||
ElMessage.error('请至少选择一个套餐')
|
||||
return
|
||||
}
|
||||
const packages: GrantPackageItem[] = packageForm.value.packages.map((pkg) => {
|
||||
const currentCostPrice = Number(pkg.cost_price_yuan)
|
||||
if (!Number.isFinite(currentCostPrice) || currentCostPrice < 0) {
|
||||
throw new Error(`套餐 ${pkg.package_name || pkg.package_id} 的成本价无效`)
|
||||
}
|
||||
const maxCostPrice = getPackageCostPriceMax(pkg)
|
||||
if (maxCostPrice !== undefined && currentCostPrice > maxCostPrice) {
|
||||
throw new Error(
|
||||
`套餐 ${pkg.package_name || pkg.package_id} 的成本价不能高于 ¥${maxCostPrice.toFixed(2)}`
|
||||
)
|
||||
}
|
||||
if (
|
||||
pkg.original_cost_price !== undefined &&
|
||||
currentCostPrice < pkg.original_cost_price
|
||||
) {
|
||||
throw new Error(
|
||||
`套餐 ${pkg.package_name || pkg.package_id} 的成本价不能低于 ¥${pkg.original_cost_price.toFixed(2)}`
|
||||
)
|
||||
}
|
||||
return {
|
||||
package_id: pkg.package_id,
|
||||
cost_price: Math.round(currentCostPrice * 100)
|
||||
}
|
||||
})
|
||||
await ShopSeriesGrantService.manageGrantPackages(grantId.value, {
|
||||
expiry_base_override:
|
||||
packageForm.value.expiry_base_override === 'default'
|
||||
? null
|
||||
: packageForm.value.expiry_base_override,
|
||||
packages
|
||||
})
|
||||
} else {
|
||||
const expiryBaseChanged =
|
||||
packageForm.value.expiry_base_override !==
|
||||
packageForm.value.initial_expiry_base_override
|
||||
if (
|
||||
expiryBaseChanged &&
|
||||
(packageForm.value.allocation_id === undefined ||
|
||||
packageForm.value.allocation_id === null)
|
||||
) {
|
||||
ElMessage.error('该套餐分配记录缺少套餐分配 ID,无法修改生效条件')
|
||||
return
|
||||
}
|
||||
|
||||
const packages: GrantPackageItem[] = [
|
||||
{ package_id: packageForm.value.package_id!, cost_price: costPrice }
|
||||
]
|
||||
if (expiryBaseChanged) {
|
||||
await ShopPackageAllocationService.updateShopPackageAllocationExpiryBase(
|
||||
packageForm.value.allocation_id!,
|
||||
{
|
||||
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
|
||||
})
|
||||
const expiryBaseChanged =
|
||||
packageForm.value.expiry_base_override !== packageForm.value.initial_expiry_base_override
|
||||
if (
|
||||
expiryBaseChanged &&
|
||||
(packageForm.value.allocation_id === undefined ||
|
||||
packageForm.value.allocation_id === null)
|
||||
) {
|
||||
ElMessage.error('该套餐分配记录缺少套餐分配 ID,无法修改生效条件')
|
||||
return
|
||||
}
|
||||
packageDialogVisible.value = false
|
||||
await fetchPackageList()
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '保存套餐失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
|
||||
const packages: GrantPackageItem[] = [
|
||||
{ package_id: packageForm.value.package_id!, cost_price: costPrice }
|
||||
]
|
||||
if (expiryBaseChanged) {
|
||||
await ShopPackageAllocationService.updateShopPackageAllocationExpiryBase(
|
||||
packageForm.value.allocation_id!,
|
||||
{
|
||||
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
|
||||
await fetchPackageList()
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '保存套餐失败')
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const saveExpiryBase = async () => {
|
||||
@@ -779,7 +558,6 @@
|
||||
}
|
||||
|
||||
const handlePackageDialogClosed = () => {
|
||||
packageFormRef.value?.clearValidate()
|
||||
assignPackageForm()
|
||||
availablePackages.value = []
|
||||
}
|
||||
@@ -827,9 +605,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.amount-value {
|
||||
font-weight: 600;
|
||||
color: var(--el-color-warning);
|
||||
.package-table-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
@@ -838,51 +618,15 @@
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.package-config-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.package-config-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.package-config-name,
|
||||
.ellipsis-value {
|
||||
display: block;
|
||||
width: 260px;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.package-config-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.package-config-cost {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.package-config-field-label {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ellipsis-value {
|
||||
display: block;
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user