1148 lines
39 KiB
Vue
1148 lines
39 KiB
Vue
<template>
|
||
<ArtTableFullScreen>
|
||
<div class="package-series-page" id="table-full-screen">
|
||
<!-- 搜索栏 -->
|
||
<ArtSearchBar
|
||
v-model:filter="searchForm"
|
||
:items="searchFormItems"
|
||
:show-expand="false"
|
||
label-width="85"
|
||
@reset="handleReset"
|
||
@search="handleSearch"
|
||
></ArtSearchBar>
|
||
|
||
<ElCard shadow="never" class="art-table-card">
|
||
<!-- 表格头部 -->
|
||
<ArtTableHeader
|
||
:columnList="columnOptions"
|
||
v-model:columns="columnChecks"
|
||
@refresh="handleRefresh"
|
||
>
|
||
<template #left>
|
||
<ElButton type="primary" @click="showDialog('add')" v-permission="'package_series:add'"
|
||
>新增套餐系列</ElButton
|
||
>
|
||
</template>
|
||
</ArtTableHeader>
|
||
|
||
<!-- 表格 -->
|
||
<ArtTable
|
||
ref="tableRef"
|
||
row-key="id"
|
||
:loading="loading"
|
||
:data="seriesList"
|
||
:currentPage="pagination.page"
|
||
:pageSize="pagination.page_size"
|
||
:total="pagination.total"
|
||
:marginTop="10"
|
||
:row-class-name="getRowClassName"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
@row-contextmenu="handleRowContextMenu"
|
||
@cell-mouse-enter="handleCellMouseEnter"
|
||
@cell-mouse-leave="handleCellMouseLeave"
|
||
>
|
||
<template #default>
|
||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||
</template>
|
||
</ArtTable>
|
||
|
||
<!-- 鼠标悬浮提示 -->
|
||
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
|
||
|
||
<!-- 套餐系列操作右键菜单 -->
|
||
<ArtMenuRight
|
||
ref="seriesOperationMenuRef"
|
||
:menu-items="seriesOperationMenuItems"
|
||
:menu-width="140"
|
||
@select="handleSeriesOperationMenuSelect"
|
||
/>
|
||
|
||
<!-- 新增/编辑对话框 -->
|
||
<ElDialog
|
||
v-model="dialogVisible"
|
||
:title="dialogType === 'add' ? '新增套餐系列' : '编辑套餐系列'"
|
||
width="45%"
|
||
:close-on-click-modal="false"
|
||
>
|
||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||
<ElFormItem label="系列编码" prop="series_code">
|
||
<div style="display: flex; gap: 8px">
|
||
<ElInput
|
||
v-model="form.series_code"
|
||
placeholder="请输入系列编码或点击生成"
|
||
:disabled="dialogType === 'edit'"
|
||
clearable
|
||
style="flex: 1"
|
||
/>
|
||
<CodeGeneratorButton
|
||
v-if="dialogType === 'add'"
|
||
code-type="series"
|
||
@generated="handleCodeGenerated"
|
||
/>
|
||
</div>
|
||
</ElFormItem>
|
||
<ElFormItem label="系列名称" prop="series_name">
|
||
<ElInput v-model="form.series_name" placeholder="请输入系列名称" clearable />
|
||
</ElFormItem>
|
||
<ElFormItem label="系列描述" prop="description">
|
||
<ElInput
|
||
v-model="form.description"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="请输入系列描述(可选)"
|
||
maxlength="500"
|
||
show-word-limit
|
||
/>
|
||
</ElFormItem>
|
||
|
||
<!-- 一次性佣金配置 -->
|
||
<div class="form-section-title">
|
||
<span class="title-text">一次性佣金配置</span>
|
||
</div>
|
||
|
||
<ElFormItem label="启用一次性佣金">
|
||
<ElSwitch
|
||
v-model="form.one_time_commission_config.enable"
|
||
active-text="启用"
|
||
inactive-text="不启用"
|
||
/>
|
||
</ElFormItem>
|
||
|
||
<template v-if="form.one_time_commission_config.enable">
|
||
<!-- 佣金类型 -->
|
||
<ElFormItem label="佣金类型">
|
||
<ElRadioGroup v-model="form.one_time_commission_config.commission_type">
|
||
<ElRadio value="fixed">固定佣金</ElRadio>
|
||
<ElRadio value="tiered">梯度佣金</ElRadio>
|
||
</ElRadioGroup>
|
||
</ElFormItem>
|
||
|
||
<!-- 触发阈值 -->
|
||
<ElFormItem label="触发阈值">
|
||
<ElInputNumber
|
||
v-model="form.one_time_commission_config.threshold"
|
||
:min="0"
|
||
:precision="2"
|
||
placeholder="触发阈值(元)"
|
||
style="width: 100%"
|
||
/>
|
||
<span
|
||
style="color: var(--el-text-color-secondary); font-size: 12px; margin-left: 8px"
|
||
>单位:元</span
|
||
>
|
||
</ElFormItem>
|
||
|
||
<!-- 触发类型 -->
|
||
<ElFormItem label="触发类型">
|
||
<ElRadioGroup v-model="form.one_time_commission_config.trigger_type">
|
||
<ElRadio value="first_recharge">首次充值</ElRadio>
|
||
<ElRadio value="accumulated_recharge">累计充值</ElRadio>
|
||
</ElRadioGroup>
|
||
</ElFormItem>
|
||
|
||
<!-- 固定佣金金额 -->
|
||
<ElFormItem
|
||
v-if="form.one_time_commission_config.commission_type === 'fixed'"
|
||
label="佣金金额"
|
||
>
|
||
<ElInputNumber
|
||
v-model="form.one_time_commission_config.commission_amount"
|
||
:min="0"
|
||
:precision="2"
|
||
placeholder="佣金金额(元)"
|
||
style="width: 100%"
|
||
/>
|
||
<span
|
||
style="color: var(--el-text-color-secondary); font-size: 12px; margin-left: 8px"
|
||
>单位:元</span
|
||
>
|
||
</ElFormItem>
|
||
|
||
<!-- 梯度佣金配置 -->
|
||
<ElFormItem
|
||
v-if="form.one_time_commission_config.commission_type === 'tiered'"
|
||
label="梯度配置"
|
||
>
|
||
<div style="width: 100%">
|
||
<div
|
||
v-for="(tier, index) in form.one_time_commission_config.tiers"
|
||
:key="index"
|
||
style="margin-bottom: 12px"
|
||
>
|
||
<ElCard shadow="hover">
|
||
<div style="display: flex; gap: 12px; align-items: flex-start">
|
||
<div style="flex: 1; display: flex; flex-direction: column; gap: 12px">
|
||
<!-- 第一行:比较运算符和阈值 -->
|
||
<div style="display: flex; gap: 12px">
|
||
<div style="flex: 1">
|
||
<div
|
||
style="
|
||
margin-bottom: 4px;
|
||
font-size: 12px;
|
||
color: var(--el-text-color-regular);
|
||
"
|
||
>比较运算符</div
|
||
>
|
||
<ElSelect
|
||
v-model="tier.operator"
|
||
placeholder="比较运算符"
|
||
style="width: 100%"
|
||
>
|
||
<ElOption label=">=" value=">=" />
|
||
<ElOption label=">" value=">" />
|
||
<ElOption label="<=" value="<=" />
|
||
<ElOption label="<" value="<" />
|
||
</ElSelect>
|
||
</div>
|
||
<div style="flex: 1">
|
||
<div
|
||
style="
|
||
margin-bottom: 4px;
|
||
font-size: 12px;
|
||
color: var(--el-text-color-regular);
|
||
"
|
||
>
|
||
达标阈值{{ tier.dimension === 'sales_amount' ? '(元)' : '' }}
|
||
</div>
|
||
<ElInputNumber
|
||
v-model="tier.threshold"
|
||
:min="0"
|
||
:precision="tier.dimension === 'sales_amount' ? 2 : 0"
|
||
:placeholder="
|
||
tier.dimension === 'sales_amount'
|
||
? '达标阈值(元)'
|
||
: '达标阈值(数量)'
|
||
"
|
||
style="width: 100%"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<!-- 第二行:统计维度和统计范围 -->
|
||
<div style="display: flex; gap: 12px">
|
||
<div style="flex: 1">
|
||
<div
|
||
style="
|
||
margin-bottom: 4px;
|
||
font-size: 12px;
|
||
color: var(--el-text-color-regular);
|
||
"
|
||
>统计维度</div
|
||
>
|
||
<ElSelect
|
||
v-model="tier.dimension"
|
||
placeholder="统计维度"
|
||
style="width: 100%"
|
||
>
|
||
<ElOption label="销量" value="sales_count" />
|
||
<ElOption label="销售额" value="sales_amount" />
|
||
</ElSelect>
|
||
</div>
|
||
<div style="flex: 1">
|
||
<div
|
||
style="
|
||
margin-bottom: 4px;
|
||
font-size: 12px;
|
||
color: var(--el-text-color-regular);
|
||
"
|
||
>统计范围</div
|
||
>
|
||
<ElSelect
|
||
v-model="tier.stat_scope"
|
||
placeholder="统计范围"
|
||
style="width: 100%"
|
||
>
|
||
<ElOption label="仅自己" value="self" />
|
||
<!--<ElOption label="自己+下级" value="self_and_sub" />-->
|
||
</ElSelect>
|
||
</div>
|
||
</div>
|
||
<!-- 第三行:佣金金额 -->
|
||
<div style="display: flex; gap: 12px">
|
||
<div style="flex: 1">
|
||
<div
|
||
style="
|
||
margin-bottom: 4px;
|
||
font-size: 12px;
|
||
color: var(--el-text-color-regular);
|
||
"
|
||
>佣金金额(元)</div
|
||
>
|
||
<ElInputNumber
|
||
v-model="tier.amount"
|
||
:min="0"
|
||
:precision="2"
|
||
placeholder="佣金金额"
|
||
style="width: 100%"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<ElButton
|
||
type="danger"
|
||
:icon="Delete"
|
||
circle
|
||
@click="removeTier(index)"
|
||
style="
|
||
flex-shrink: 0;
|
||
margin-top: 28px;
|
||
width: 32px;
|
||
height: 32px;
|
||
padding: 0;
|
||
"
|
||
/>
|
||
</div>
|
||
</ElCard>
|
||
</div>
|
||
<ElButton type="primary" :icon="Plus" @click="addTier" style="width: 100%">
|
||
添加梯度
|
||
</ElButton>
|
||
</div>
|
||
</ElFormItem>
|
||
|
||
<!-- 强充配置 -->
|
||
<div class="form-section-title">
|
||
<span class="title-text">强充配置</span>
|
||
</div>
|
||
|
||
<ElFormItem label="启用强充">
|
||
<ElSwitch
|
||
v-model="form.one_time_commission_config.enable_force_recharge"
|
||
active-text="启用"
|
||
inactive-text="不启用"
|
||
/>
|
||
</ElFormItem>
|
||
|
||
<template v-if="form.one_time_commission_config.enable_force_recharge">
|
||
<ElFormItem label="强充金额">
|
||
<ElInputNumber
|
||
v-model="form.one_time_commission_config.force_amount"
|
||
:min="0"
|
||
:precision="2"
|
||
placeholder="强充金额(元)"
|
||
style="width: 100%"
|
||
/>
|
||
<span
|
||
style="color: var(--el-text-color-secondary); font-size: 12px; margin-left: 8px"
|
||
>单位:元</span
|
||
>
|
||
</ElFormItem>
|
||
|
||
<ElFormItem label="强充计算类型">
|
||
<ElRadioGroup v-model="form.one_time_commission_config.force_calc_type">
|
||
<ElRadio value="fixed">固定</ElRadio>
|
||
<ElRadio value="dynamic">动态</ElRadio>
|
||
</ElRadioGroup>
|
||
</ElFormItem>
|
||
</template>
|
||
|
||
<!-- 时效配置 -->
|
||
<div class="form-section-title">
|
||
<span class="title-text">时效配置</span>
|
||
</div>
|
||
|
||
<ElFormItem label="时效类型">
|
||
<ElRadioGroup v-model="form.one_time_commission_config.validity_type">
|
||
<ElRadio value="permanent">永久</ElRadio>
|
||
<ElRadio value="fixed_date">固定日期</ElRadio>
|
||
<ElRadio value="relative">相对时长</ElRadio>
|
||
</ElRadioGroup>
|
||
</ElFormItem>
|
||
|
||
<ElFormItem
|
||
v-if="form.one_time_commission_config.validity_type === 'fixed_date'"
|
||
label="有效期至"
|
||
>
|
||
<ElDatePicker
|
||
v-model="form.one_time_commission_config.validity_value"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
style="width: 100%"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
</ElFormItem>
|
||
|
||
<ElFormItem
|
||
v-if="form.one_time_commission_config.validity_type === 'relative'"
|
||
label="有效月数"
|
||
>
|
||
<ElInputNumber
|
||
v-model="form.one_time_commission_config.validity_value"
|
||
:min="1"
|
||
:precision="0"
|
||
placeholder="有效月数"
|
||
style="width: 100%"
|
||
/>
|
||
<span
|
||
style="color: var(--el-text-color-secondary); font-size: 12px; margin-left: 8px"
|
||
>单位:月</span
|
||
>
|
||
</ElFormItem>
|
||
</template>
|
||
</ElForm>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<ElButton @click="dialogVisible = false">取消</ElButton>
|
||
<ElButton type="primary" @click="handleSubmit(formRef)" :loading="submitLoading">
|
||
提交
|
||
</ElButton>
|
||
</div>
|
||
</template>
|
||
</ElDialog>
|
||
</ElCard>
|
||
</div>
|
||
</ArtTableFullScreen>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { h } from 'vue'
|
||
import { PackageSeriesService } from '@/api/modules'
|
||
import { ElMessage, ElMessageBox, ElTag, ElSwitch, ElButton } from 'element-plus'
|
||
import { Plus, Delete } from '@element-plus/icons-vue'
|
||
import type { FormInstance, FormRules } from 'element-plus'
|
||
import type { PackageSeriesResponse } from '@/types/api'
|
||
import type { SearchFormItem } from '@/types'
|
||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||
import { useAuth } from '@/composables/useAuth'
|
||
import { useTableContextMenu } from '@/composables/useTableContextMenu'
|
||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
|
||
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
|
||
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
||
import CodeGeneratorButton from '@/components/business/CodeGeneratorButton.vue'
|
||
import { formatDateTime } from '@/utils/business/format'
|
||
import {
|
||
CommonStatus,
|
||
getStatusText,
|
||
frontendStatusToApi,
|
||
apiStatusToFrontend
|
||
} from '@/config/constants'
|
||
import { useRouter } from 'vue-router'
|
||
|
||
defineOptions({ name: 'PackageSeries' })
|
||
|
||
const { hasAuth } = useAuth()
|
||
const router = useRouter()
|
||
|
||
const dialogVisible = ref(false)
|
||
const loading = ref(false)
|
||
const submitLoading = ref(false)
|
||
const tableRef = ref()
|
||
const formRef = ref<FormInstance>()
|
||
|
||
// 右键菜单
|
||
const seriesOperationMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||
const currentOperatingSeries = ref<PackageSeriesResponse | null>(null)
|
||
|
||
// 搜索表单初始值
|
||
const initialSearchState = {
|
||
series_name: '',
|
||
status: undefined as number | undefined,
|
||
enable_one_time_commission: undefined as boolean | undefined
|
||
}
|
||
|
||
// 搜索表单
|
||
const searchForm = reactive({ ...initialSearchState })
|
||
|
||
// 搜索表单配置
|
||
const searchFormItems: SearchFormItem[] = [
|
||
{
|
||
label: '系列名称',
|
||
prop: 'series_name',
|
||
type: 'input',
|
||
config: {
|
||
clearable: true,
|
||
placeholder: '请输入系列名称'
|
||
}
|
||
},
|
||
{
|
||
label: '状态',
|
||
prop: 'status',
|
||
type: 'select',
|
||
config: {
|
||
clearable: true,
|
||
placeholder: '请选择状态'
|
||
},
|
||
options: () => [
|
||
{ label: '启用', value: 1 },
|
||
{ label: '禁用', value: 2 }
|
||
]
|
||
},
|
||
{
|
||
label: '一次性佣金',
|
||
prop: 'enable_one_time_commission',
|
||
type: 'select',
|
||
config: {
|
||
clearable: true,
|
||
placeholder: '请选择'
|
||
},
|
||
options: () => [
|
||
{ label: '已启用', value: true },
|
||
{ label: '未启用', value: false }
|
||
]
|
||
}
|
||
]
|
||
|
||
// 分页
|
||
const pagination = reactive({
|
||
page: 1,
|
||
page_size: 20,
|
||
total: 0
|
||
})
|
||
|
||
// 列配置
|
||
const columnOptions = [
|
||
{ label: '系列编码', prop: 'series_code' },
|
||
{ label: '系列名称', prop: 'series_name' },
|
||
{ label: '描述', prop: 'description' },
|
||
{ label: '一次性佣金', prop: 'enable_one_time_commission' },
|
||
{ label: '佣金类型', prop: 'commission_type' },
|
||
{ label: '触发阈值', prop: 'commission_threshold' },
|
||
{ label: '触发类型', prop: 'trigger_type' },
|
||
{ label: '强充状态', prop: 'enable_force_recharge' },
|
||
{ label: '强充金额', prop: 'force_amount' },
|
||
{ label: '强充计算类型', prop: 'force_calc_type' },
|
||
{ label: '时效类型', prop: 'validity_type' },
|
||
{ label: '状态', prop: 'status' },
|
||
{ label: '创建时间', prop: 'created_at' }
|
||
]
|
||
|
||
// 表单验证规则
|
||
const rules = reactive<FormRules>({
|
||
series_code: [
|
||
{ required: true, message: '请输入系列编码', trigger: 'blur' },
|
||
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
|
||
],
|
||
series_name: [
|
||
{ required: true, message: '请输入系列名称', trigger: 'blur' },
|
||
{ min: 1, max: 255, message: '长度在 1 到 255 个字符', trigger: 'blur' }
|
||
],
|
||
description: [{ max: 500, message: '描述不能超过 500 个字符', trigger: 'blur' }]
|
||
})
|
||
|
||
// 表单数据
|
||
const form = reactive<any>({
|
||
id: 0,
|
||
series_code: '',
|
||
series_name: '',
|
||
description: '',
|
||
one_time_commission_config: {
|
||
enable: false,
|
||
commission_type: 'fixed',
|
||
commission_amount: undefined,
|
||
threshold: undefined,
|
||
trigger_type: 'first_recharge',
|
||
tiers: [],
|
||
enable_force_recharge: false,
|
||
force_amount: undefined,
|
||
force_calc_type: 'fixed',
|
||
validity_type: 'permanent',
|
||
validity_value: undefined
|
||
}
|
||
})
|
||
|
||
const seriesList = ref<PackageSeriesResponse[]>([])
|
||
const dialogType = ref('add')
|
||
|
||
// 动态列配置
|
||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||
{
|
||
prop: 'series_code',
|
||
label: '系列编码',
|
||
width: 200,
|
||
showOverflowTooltip: true
|
||
},
|
||
{
|
||
prop: 'series_name',
|
||
label: '系列名称',
|
||
minWidth: 150,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
return h(
|
||
'span',
|
||
{
|
||
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||
onClick: (e: MouseEvent) => {
|
||
e.stopPropagation()
|
||
handleNameClick(row)
|
||
}
|
||
},
|
||
row.series_name
|
||
)
|
||
}
|
||
},
|
||
|
||
{
|
||
prop: 'enable_one_time_commission',
|
||
label: '一次性佣金',
|
||
width: 110,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
if (!row.one_time_commission_config) {
|
||
return h(ElTag, { type: 'info', size: 'small' }, () => '未配置')
|
||
}
|
||
return h(
|
||
ElTag,
|
||
{
|
||
type: row.one_time_commission_config.enable ? 'success' : 'info',
|
||
size: 'small'
|
||
},
|
||
() => (row.one_time_commission_config.enable ? '已启用' : '未启用')
|
||
)
|
||
}
|
||
},
|
||
{
|
||
prop: 'commission_type',
|
||
label: '佣金类型',
|
||
width: 100,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
if (!row.one_time_commission_config?.commission_type) {
|
||
return '-'
|
||
}
|
||
const typeMap = {
|
||
fixed: '固定',
|
||
tiered: '梯度'
|
||
}
|
||
return typeMap[row.one_time_commission_config.commission_type] || '-'
|
||
}
|
||
},
|
||
{
|
||
prop: 'commission_threshold',
|
||
label: '触发阈值',
|
||
width: 120,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
if (!row.one_time_commission_config?.threshold) {
|
||
return '-'
|
||
}
|
||
return `¥${(row.one_time_commission_config.threshold / 100).toFixed(2)}`
|
||
}
|
||
},
|
||
{
|
||
prop: 'trigger_type',
|
||
label: '触发类型',
|
||
width: 110,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
if (!row.one_time_commission_config?.trigger_type) {
|
||
return '-'
|
||
}
|
||
const typeMap = {
|
||
first_recharge: '首次充值',
|
||
accumulated_recharge: '累计充值'
|
||
}
|
||
return typeMap[row.one_time_commission_config.trigger_type] || '-'
|
||
}
|
||
},
|
||
{
|
||
prop: 'enable_force_recharge',
|
||
label: '强充状态',
|
||
width: 100,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
if (!row.one_time_commission_config) {
|
||
return '-'
|
||
}
|
||
return h(
|
||
ElTag,
|
||
{
|
||
type: row.one_time_commission_config.enable_force_recharge ? 'warning' : 'info',
|
||
size: 'small'
|
||
},
|
||
() => (row.one_time_commission_config.enable_force_recharge ? '已启用' : '未启用')
|
||
)
|
||
}
|
||
},
|
||
{
|
||
prop: 'force_amount',
|
||
label: '强充金额',
|
||
width: 120,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
if (!row.one_time_commission_config?.force_amount) {
|
||
return '-'
|
||
}
|
||
return h(
|
||
'span',
|
||
{ style: 'color: #f56c6c; font-weight: bold;' },
|
||
`¥${(row.one_time_commission_config.force_amount / 100).toFixed(2)}`
|
||
)
|
||
}
|
||
},
|
||
{
|
||
prop: 'force_calc_type',
|
||
label: '强充计算类型',
|
||
width: 120,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
if (!row.one_time_commission_config?.force_calc_type) {
|
||
return '-'
|
||
}
|
||
const typeMap = {
|
||
fixed: '固定',
|
||
dynamic: '动态'
|
||
}
|
||
return typeMap[row.one_time_commission_config.force_calc_type] || '-'
|
||
}
|
||
},
|
||
{
|
||
prop: 'validity_type',
|
||
label: '时效类型',
|
||
width: 180,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
if (!row.one_time_commission_config?.validity_type) {
|
||
return '-'
|
||
}
|
||
const typeMap = {
|
||
permanent: '永久',
|
||
fixed_date: '固定日期',
|
||
relative: '相对时长'
|
||
}
|
||
const validityType = typeMap[row.one_time_commission_config.validity_type] || '-'
|
||
|
||
// 如果有时效值,显示详情
|
||
if (row.one_time_commission_config.validity_value) {
|
||
if (row.one_time_commission_config.validity_type === 'relative') {
|
||
return `${validityType}(${row.one_time_commission_config.validity_value}月)`
|
||
} else if (row.one_time_commission_config.validity_type === 'fixed_date') {
|
||
return `${validityType}(${row.one_time_commission_config.validity_value})`
|
||
}
|
||
}
|
||
|
||
return validityType
|
||
}
|
||
},
|
||
{
|
||
prop: 'status',
|
||
label: '状态',
|
||
width: 100,
|
||
formatter: (row: PackageSeriesResponse) => {
|
||
const frontendStatus = apiStatusToFrontend(row.status)
|
||
return h(ElSwitch, {
|
||
modelValue: frontendStatus,
|
||
activeValue: CommonStatus.ENABLED,
|
||
inactiveValue: CommonStatus.DISABLED,
|
||
activeText: getStatusText(CommonStatus.ENABLED),
|
||
inactiveText: getStatusText(CommonStatus.DISABLED),
|
||
inlinePrompt: true,
|
||
disabled: !hasAuth('package_series:update_status'),
|
||
'onUpdate:modelValue': (val: string | number | boolean) =>
|
||
handleStatusChange(row, val as number)
|
||
})
|
||
}
|
||
},
|
||
{
|
||
prop: 'description',
|
||
label: '描述',
|
||
minWidth: 200,
|
||
showOverflowTooltip: true
|
||
},
|
||
{
|
||
prop: 'created_at',
|
||
label: '创建时间',
|
||
width: 180,
|
||
formatter: (row: PackageSeriesResponse) => formatDateTime(row.created_at)
|
||
}
|
||
])
|
||
|
||
onMounted(() => {
|
||
getTableData()
|
||
})
|
||
|
||
// 套餐系列操作菜单项配置
|
||
const seriesOperationMenuItems = computed((): MenuItemType[] => {
|
||
const items: MenuItemType[] = []
|
||
|
||
// 编辑
|
||
if (hasAuth('package_series:edit')) {
|
||
items.push({
|
||
key: 'edit',
|
||
label: '编辑'
|
||
})
|
||
}
|
||
|
||
// 删除
|
||
if (hasAuth('package_series:delete')) {
|
||
items.push({
|
||
key: 'delete',
|
||
label: '删除'
|
||
})
|
||
}
|
||
|
||
return items
|
||
})
|
||
|
||
// 显示套餐系列操作右键菜单
|
||
const showSeriesOperationMenu = (e: MouseEvent, row: PackageSeriesResponse) => {
|
||
e.preventDefault()
|
||
e.stopPropagation()
|
||
currentOperatingSeries.value = row
|
||
seriesOperationMenuRef.value?.show(e)
|
||
}
|
||
|
||
// 处理表格行右键菜单
|
||
const handleRowContextMenu = (row: PackageSeriesResponse, column: any, event: MouseEvent) => {
|
||
// 如果用户有编辑或删除权限,显示右键菜单
|
||
if (hasAuth('package_series:edit') || hasAuth('package_series:delete')) {
|
||
showSeriesOperationMenu(event, row)
|
||
}
|
||
}
|
||
|
||
// 处理套餐系列操作菜单选择
|
||
const handleSeriesOperationMenuSelect = (item: MenuItemType) => {
|
||
if (!currentOperatingSeries.value) return
|
||
|
||
switch (item.key) {
|
||
case 'edit':
|
||
showDialog('edit', currentOperatingSeries.value)
|
||
break
|
||
case 'delete':
|
||
deleteSeries(currentOperatingSeries.value)
|
||
break
|
||
}
|
||
}
|
||
|
||
// 获取套餐系列列表
|
||
const getTableData = async () => {
|
||
loading.value = true
|
||
try {
|
||
const params = {
|
||
page: pagination.page,
|
||
page_size: pagination.page_size,
|
||
series_name: searchForm.series_name || undefined,
|
||
status: searchForm.status || undefined,
|
||
enable_one_time_commission: searchForm.enable_one_time_commission ?? undefined
|
||
}
|
||
const res = await PackageSeriesService.getPackageSeries(params)
|
||
if (res.code === 0) {
|
||
seriesList.value = res.data.items
|
||
pagination.total = res.data.total
|
||
}
|
||
} catch (error) {
|
||
console.error(error)
|
||
ElMessage.error('获取套餐系列列表失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 重置搜索
|
||
const handleReset = () => {
|
||
Object.assign(searchForm, { ...initialSearchState })
|
||
pagination.page = 1
|
||
getTableData()
|
||
}
|
||
|
||
// 搜索
|
||
const handleSearch = () => {
|
||
pagination.page = 1
|
||
getTableData()
|
||
}
|
||
|
||
// 刷新表格
|
||
const handleRefresh = () => {
|
||
getTableData()
|
||
}
|
||
|
||
// 处理表格分页变化
|
||
const handleSizeChange = (newPageSize: number) => {
|
||
pagination.page_size = newPageSize
|
||
getTableData()
|
||
}
|
||
|
||
const handleCurrentChange = (newCurrentPage: number) => {
|
||
pagination.page = newCurrentPage
|
||
getTableData()
|
||
}
|
||
|
||
// 显示新增/编辑对话框
|
||
const showDialog = (type: string, row?: PackageSeriesResponse) => {
|
||
dialogVisible.value = true
|
||
dialogType.value = type
|
||
|
||
if (type === 'edit' && row) {
|
||
form.id = row.id
|
||
form.series_code = row.series_code
|
||
form.series_name = row.series_name
|
||
form.description = row.description || ''
|
||
|
||
// 填充佣金配置
|
||
if (row.one_time_commission_config) {
|
||
// 转换梯度配置:分 -> 元
|
||
const convertedTiers = (row.one_time_commission_config.tiers || []).map((tier: any) => ({
|
||
...tier,
|
||
// 只有销售额维度的阈值需要转换
|
||
threshold:
|
||
tier.dimension === 'sales_amount' && tier.threshold != null
|
||
? tier.threshold / 100
|
||
: tier.threshold,
|
||
// 佣金金额转换
|
||
amount: tier.amount != null ? tier.amount / 100 : tier.amount
|
||
}))
|
||
|
||
form.one_time_commission_config = {
|
||
enable: row.one_time_commission_config.enable || false,
|
||
commission_type: row.one_time_commission_config.commission_type || 'fixed',
|
||
// 固定佣金金额:分 -> 元
|
||
commission_amount:
|
||
row.one_time_commission_config.commission_amount != null
|
||
? row.one_time_commission_config.commission_amount / 100
|
||
: undefined,
|
||
// 触发阈值:分 -> 元
|
||
threshold:
|
||
row.one_time_commission_config.threshold != null
|
||
? row.one_time_commission_config.threshold / 100
|
||
: undefined,
|
||
trigger_type: row.one_time_commission_config.trigger_type || 'first_recharge',
|
||
tiers: convertedTiers,
|
||
enable_force_recharge: row.one_time_commission_config.enable_force_recharge || false,
|
||
// 强充金额:分 -> 元
|
||
force_amount:
|
||
row.one_time_commission_config.force_amount != null
|
||
? row.one_time_commission_config.force_amount / 100
|
||
: undefined,
|
||
force_calc_type: row.one_time_commission_config.force_calc_type || 'fixed',
|
||
validity_type: row.one_time_commission_config.validity_type || 'permanent',
|
||
validity_value: row.one_time_commission_config.validity_value
|
||
}
|
||
} else {
|
||
// 重置为默认值
|
||
form.one_time_commission_config = {
|
||
enable: false,
|
||
commission_type: 'fixed',
|
||
commission_amount: undefined,
|
||
threshold: undefined,
|
||
trigger_type: 'first_recharge',
|
||
tiers: [],
|
||
enable_force_recharge: false,
|
||
force_amount: undefined,
|
||
force_calc_type: 'fixed',
|
||
validity_type: 'permanent',
|
||
validity_value: undefined
|
||
}
|
||
}
|
||
} else {
|
||
form.id = 0
|
||
form.series_code = ''
|
||
form.series_name = ''
|
||
form.description = ''
|
||
form.one_time_commission_config = {
|
||
enable: false,
|
||
commission_type: 'fixed',
|
||
commission_amount: undefined,
|
||
threshold: undefined,
|
||
trigger_type: 'first_recharge',
|
||
tiers: [],
|
||
enable_force_recharge: false,
|
||
force_amount: undefined,
|
||
force_calc_type: 'fixed',
|
||
validity_type: 'permanent',
|
||
validity_value: undefined
|
||
}
|
||
}
|
||
|
||
// 重置表单验证状态
|
||
nextTick(() => {
|
||
formRef.value?.clearValidate()
|
||
})
|
||
}
|
||
|
||
// 处理编码生成
|
||
const handleCodeGenerated = (code: string) => {
|
||
form.series_code = code
|
||
// 清除该字段的验证错误提示
|
||
nextTick(() => {
|
||
formRef.value?.clearValidate('series_code')
|
||
})
|
||
}
|
||
|
||
// 删除套餐系列
|
||
const deleteSeries = (row: PackageSeriesResponse) => {
|
||
ElMessageBox.confirm(`确定删除套餐系列 ${row.series_name} 吗?`, '删除确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'error'
|
||
})
|
||
.then(async () => {
|
||
try {
|
||
await PackageSeriesService.deletePackageSeries(row.id)
|
||
ElMessage.success('删除成功')
|
||
await getTableData()
|
||
} catch (error) {
|
||
console.error(error)
|
||
}
|
||
})
|
||
.catch(() => {
|
||
// 用户取消删除
|
||
})
|
||
}
|
||
|
||
// 提交表单
|
||
const handleSubmit = async (formEl: FormInstance | undefined) => {
|
||
if (!formEl) return
|
||
|
||
await formEl.validate(async (valid) => {
|
||
if (valid) {
|
||
submitLoading.value = true
|
||
try {
|
||
// 构建请求数据
|
||
const data: any = {
|
||
series_name: form.series_name,
|
||
description: form.description || undefined,
|
||
enable_one_time_commission: form.one_time_commission_config.enable
|
||
}
|
||
|
||
// 新增模式下才包含 series_code
|
||
if (dialogType.value === 'add') {
|
||
data.series_code = form.series_code
|
||
}
|
||
|
||
// 构建佣金配置对象
|
||
const commissionConfig: any = {
|
||
enable: form.one_time_commission_config.enable
|
||
}
|
||
|
||
// 只有启用时才添加其他配置
|
||
if (form.one_time_commission_config.enable) {
|
||
commissionConfig.commission_type = form.one_time_commission_config.commission_type
|
||
// 触发阈值:元 -> 分
|
||
commissionConfig.threshold =
|
||
form.one_time_commission_config.threshold != null
|
||
? Math.round(form.one_time_commission_config.threshold * 100)
|
||
: undefined
|
||
commissionConfig.trigger_type = form.one_time_commission_config.trigger_type
|
||
|
||
// 添加固定佣金金额:元 -> 分
|
||
if (form.one_time_commission_config.commission_type === 'fixed') {
|
||
commissionConfig.commission_amount =
|
||
form.one_time_commission_config.commission_amount != null
|
||
? Math.round(form.one_time_commission_config.commission_amount * 100)
|
||
: undefined
|
||
}
|
||
|
||
// 添加梯度配置:元 -> 分
|
||
if (form.one_time_commission_config.commission_type === 'tiered') {
|
||
const convertedTiers = form.one_time_commission_config.tiers.map((tier: any) => ({
|
||
...tier,
|
||
// 只有销售额维度的阈值需要转换
|
||
threshold:
|
||
tier.dimension === 'sales_amount' && tier.threshold != null
|
||
? Math.round(tier.threshold * 100)
|
||
: tier.threshold,
|
||
// 佣金金额转换:元 -> 分
|
||
amount: tier.amount != null ? Math.round(tier.amount * 100) : tier.amount
|
||
}))
|
||
|
||
commissionConfig.tiers = convertedTiers.length > 0 ? convertedTiers : null
|
||
}
|
||
|
||
// 添加强充配置:元 -> 分
|
||
if (form.one_time_commission_config.enable_force_recharge) {
|
||
commissionConfig.enable_force_recharge = true
|
||
commissionConfig.force_amount =
|
||
form.one_time_commission_config.force_amount != null
|
||
? Math.round(form.one_time_commission_config.force_amount * 100)
|
||
: undefined
|
||
commissionConfig.force_calc_type = form.one_time_commission_config.force_calc_type
|
||
}
|
||
|
||
// 添加时效配置
|
||
commissionConfig.validity_type = form.one_time_commission_config.validity_type
|
||
if (form.one_time_commission_config.validity_type !== 'permanent') {
|
||
// relative 类型的 validity_value 需要转换为字符串
|
||
if (form.one_time_commission_config.validity_type === 'relative') {
|
||
commissionConfig.validity_value = String(
|
||
form.one_time_commission_config.validity_value
|
||
)
|
||
} else {
|
||
commissionConfig.validity_value = form.one_time_commission_config.validity_value
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加佣金配置到请求数据
|
||
data.one_time_commission_config = commissionConfig
|
||
|
||
if (dialogType.value === 'add') {
|
||
await PackageSeriesService.createPackageSeries(data)
|
||
ElMessage.success('新增成功')
|
||
} else {
|
||
await PackageSeriesService.updatePackageSeries(form.id, data)
|
||
ElMessage.success('修改成功')
|
||
}
|
||
|
||
dialogVisible.value = false
|
||
formEl.resetFields()
|
||
await getTableData()
|
||
} catch (error) {
|
||
console.error(error)
|
||
} finally {
|
||
submitLoading.value = false
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 状态切换
|
||
const handleStatusChange = async (row: PackageSeriesResponse, newFrontendStatus: number) => {
|
||
const oldStatus = row.status
|
||
const newApiStatus = frontendStatusToApi(newFrontendStatus)
|
||
// 先更新UI(将后端状态转换)
|
||
row.status = newApiStatus
|
||
try {
|
||
await PackageSeriesService.updatePackageSeriesStatus(row.id, newApiStatus)
|
||
ElMessage.success('状态切换成功')
|
||
} catch (error) {
|
||
// 切换失败,恢复原状态
|
||
row.status = oldStatus
|
||
console.error(error)
|
||
}
|
||
}
|
||
|
||
// 添加梯度
|
||
const addTier = () => {
|
||
form.one_time_commission_config.tiers.push({
|
||
operator: '>=',
|
||
threshold: undefined,
|
||
dimension: 'sales_count',
|
||
amount: undefined,
|
||
stat_scope: 'self'
|
||
})
|
||
}
|
||
|
||
// 删除梯度
|
||
const removeTier = (index: number) => {
|
||
form.one_time_commission_config.tiers.splice(index, 1)
|
||
}
|
||
|
||
// 查看详情
|
||
const handleViewDetail = (row: PackageSeriesResponse) => {
|
||
router.push(`/package-management/package-series/detail/${row.id}`)
|
||
}
|
||
|
||
// 处理名称点击
|
||
const handleNameClick = (row: PackageSeriesResponse) => {
|
||
if (hasAuth('package_series:detail')) {
|
||
handleViewDetail(row)
|
||
} else {
|
||
ElMessage.warning('您没有查看详情的权限')
|
||
}
|
||
}
|
||
|
||
// 使用表格右键菜单功能
|
||
const {
|
||
showContextMenuHint,
|
||
hintPosition,
|
||
getRowClassName,
|
||
handleCellMouseEnter,
|
||
handleCellMouseLeave
|
||
} = useTableContextMenu()
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.package-series-page {
|
||
// 可以添加特定样式
|
||
}
|
||
|
||
.dialog-footer {
|
||
text-align: right;
|
||
}
|
||
|
||
:deep(.el-table__row.table-row-with-context-menu) {
|
||
cursor: pointer;
|
||
}
|
||
</style>
|