949 lines
28 KiB
Vue
949 lines
28 KiB
Vue
<template>
|
|
<ArtTableFullScreen>
|
|
<div class="package-list-page" id="table-full-screen">
|
|
<!-- 搜索栏 -->
|
|
<ArtSearchBar
|
|
v-model:filter="searchForm"
|
|
:items="searchFormItems"
|
|
:show-expand="true"
|
|
@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:add'"
|
|
>新增套餐</ElButton
|
|
>
|
|
</template>
|
|
</ArtTableHeader>
|
|
|
|
<!-- 表格 -->
|
|
<ArtTable
|
|
ref="tableRef"
|
|
row-key="id"
|
|
:loading="loading"
|
|
:data="packageList"
|
|
:currentPage="pagination.page"
|
|
:pageSize="pagination.page_size"
|
|
:total="pagination.total"
|
|
:marginTop="10"
|
|
@size-change="handleSizeChange"
|
|
@current-change="handleCurrentChange"
|
|
@row-contextmenu="handleRowContextMenu"
|
|
>
|
|
<template #default>
|
|
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
|
</template>
|
|
</ArtTable>
|
|
|
|
<!-- 右键菜单 -->
|
|
<ArtMenuRight
|
|
ref="contextMenuRef"
|
|
:menu-items="contextMenuItems"
|
|
:menu-width="120"
|
|
@select="handleContextMenuSelect"
|
|
/>
|
|
|
|
<!-- 新增/编辑对话框 -->
|
|
<ElDialog
|
|
v-model="dialogVisible"
|
|
:title="dialogType === 'add' ? '新增套餐' : '编辑套餐'"
|
|
width="600px"
|
|
:close-on-click-modal="false"
|
|
@closed="handleDialogClosed"
|
|
>
|
|
<ElForm ref="formRef" :model="form" :rules="rules" label-width="150px">
|
|
<ElFormItem label="套餐编码" prop="package_code">
|
|
<div style="display: flex; gap: 8px">
|
|
<ElInput
|
|
v-model="form.package_code"
|
|
placeholder="请输入套餐编码或点击生成"
|
|
:disabled="dialogType === 'edit'"
|
|
clearable
|
|
style="flex: 1"
|
|
/>
|
|
<ElButton v-if="dialogType === 'add'" @click="handleGeneratePackageCode">
|
|
生成编码
|
|
</ElButton>
|
|
</div>
|
|
</ElFormItem>
|
|
<ElFormItem label="套餐名称" prop="package_name">
|
|
<ElInput v-model="form.package_name" placeholder="请输入套餐名称" clearable />
|
|
</ElFormItem>
|
|
<ElFormItem label="所属系列" prop="series_id">
|
|
<ElSelect
|
|
v-model="form.series_id"
|
|
placeholder="请选择套餐系列"
|
|
style="width: 100%"
|
|
filterable
|
|
remote
|
|
:remote-method="searchSeries"
|
|
:loading="seriesLoading"
|
|
clearable
|
|
>
|
|
<ElOption
|
|
v-for="series in seriesOptions"
|
|
:key="series.id"
|
|
:label="series.series_name"
|
|
:value="series.id"
|
|
/>
|
|
</ElSelect>
|
|
</ElFormItem>
|
|
<ElFormItem label="套餐类型" prop="package_type">
|
|
<ElSelect
|
|
v-model="form.package_type"
|
|
placeholder="请选择套餐类型"
|
|
style="width: 100%"
|
|
>
|
|
<ElOption
|
|
v-for="option in PACKAGE_TYPE_OPTIONS"
|
|
:key="option.value"
|
|
:label="option.label"
|
|
:value="option.value"
|
|
/>
|
|
</ElSelect>
|
|
</ElFormItem>
|
|
<ElFormItem label="套餐周期类型" prop="calendar_type">
|
|
<ElSelect
|
|
v-model="form.calendar_type"
|
|
placeholder="请选择套餐周期类型"
|
|
style="width: 100%"
|
|
clearable
|
|
>
|
|
<ElOption label="自然月" value="natural_month" />
|
|
<ElOption label="按天" value="by_day" />
|
|
</ElSelect>
|
|
</ElFormItem>
|
|
<ElFormItem label="有效期(月)" prop="duration_months">
|
|
<ElInputNumber
|
|
v-model="form.duration_months"
|
|
:min="1"
|
|
:max="120"
|
|
:controls="false"
|
|
style="width: 100%"
|
|
placeholder="请输入有效期(月)"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem
|
|
v-if="form.calendar_type === 'by_day'"
|
|
label="套餐天数"
|
|
prop="duration_days"
|
|
>
|
|
<ElInputNumber
|
|
v-model="form.duration_days"
|
|
:min="1"
|
|
:max="3650"
|
|
:controls="false"
|
|
style="width: 100%"
|
|
placeholder="请输入套餐天数"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem label="流量重置周期" prop="data_reset_cycle">
|
|
<ElSelect
|
|
v-model="form.data_reset_cycle"
|
|
placeholder="请选择流量重置周期"
|
|
style="width: 100%"
|
|
clearable
|
|
>
|
|
<ElOption label="每日" value="daily" />
|
|
<ElOption label="每月" value="monthly" />
|
|
<ElOption label="每年" value="yearly" />
|
|
<ElOption label="不重置" value="none" />
|
|
</ElSelect>
|
|
</ElFormItem>
|
|
<ElFormItem label="真流量额度(MB)" prop="real_data_mb">
|
|
<ElInputNumber
|
|
v-model="form.real_data_mb"
|
|
:min="0"
|
|
:controls="false"
|
|
style="width: 100%"
|
|
placeholder="请输入真流量额度"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem label="启用虚流量">
|
|
<ElSwitch
|
|
v-model="form.enable_virtual_data"
|
|
active-text="启用"
|
|
inactive-text="不启用"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem
|
|
label="虚流量额度(MB)"
|
|
prop="virtual_data_mb"
|
|
v-if="form.enable_virtual_data"
|
|
>
|
|
<ElInputNumber
|
|
v-model="form.virtual_data_mb"
|
|
:min="0"
|
|
:controls="false"
|
|
style="width: 100%"
|
|
placeholder="请输入虚流量额度"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem label="启用实名激活">
|
|
<ElSwitch
|
|
v-model="form.enable_realname_activation"
|
|
active-text="启用"
|
|
inactive-text="不启用"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem label="成本价(元)" prop="cost_price">
|
|
<ElInputNumber
|
|
v-model="form.cost_price"
|
|
:min="0"
|
|
:precision="2"
|
|
:step="0.01"
|
|
:controls="false"
|
|
style="width: 100%"
|
|
placeholder="请输入成本价"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem label="建议售价(元)" prop="suggested_retail_price">
|
|
<ElInputNumber
|
|
v-model="form.suggested_retail_price"
|
|
:min="0"
|
|
:precision="2"
|
|
:step="0.01"
|
|
:controls="false"
|
|
style="width: 100%"
|
|
placeholder="请输入建议售价(可选)"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem label="套餐描述" prop="description">
|
|
<ElInput
|
|
v-model="form.description"
|
|
type="textarea"
|
|
:rows="3"
|
|
placeholder="请输入套餐描述(可选)"
|
|
maxlength="500"
|
|
show-word-limit
|
|
/>
|
|
</ElFormItem>
|
|
</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 { useRouter } from 'vue-router'
|
|
import { PackageManageService, PackageSeriesService } from '@/api/modules'
|
|
import { ElMessage, ElMessageBox, ElTag, ElSwitch, ElButton } from 'element-plus'
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
import type { PackageResponse, SeriesSelectOption } from '@/types/api'
|
|
import type { SearchFormItem } from '@/types'
|
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
|
import { useAuth } from '@/composables/useAuth'
|
|
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
|
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
|
|
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
|
import { formatDateTime } from '@/utils/business/format'
|
|
import { RoutesAlias } from '@/router/routesAlias'
|
|
import {
|
|
CommonStatus,
|
|
getStatusText,
|
|
frontendStatusToApi,
|
|
apiStatusToFrontend,
|
|
PACKAGE_TYPE_OPTIONS,
|
|
getPackageTypeLabel,
|
|
getPackageTypeTag
|
|
} from '@/config/constants'
|
|
import { generatePackageCode } from '@/utils/codeGenerator'
|
|
|
|
defineOptions({ name: 'PackageList' })
|
|
|
|
const { hasAuth } = useAuth()
|
|
const router = useRouter()
|
|
|
|
const dialogVisible = ref(false)
|
|
const loading = ref(false)
|
|
const submitLoading = ref(false)
|
|
const seriesLoading = ref(false)
|
|
const tableRef = ref()
|
|
const formRef = ref<FormInstance>()
|
|
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
|
const currentRow = ref<PackageResponse | null>(null)
|
|
const seriesOptions = ref<SeriesSelectOption[]>([])
|
|
const searchSeriesOptions = ref<SeriesSelectOption[]>([])
|
|
|
|
// 搜索表单初始值
|
|
const initialSearchState = {
|
|
package_name: '',
|
|
series_id: undefined as number | undefined,
|
|
package_type: undefined as string | undefined,
|
|
shelf_status: undefined as number | undefined,
|
|
status: undefined as number | undefined
|
|
}
|
|
|
|
// 搜索表单
|
|
const searchForm = reactive({ ...initialSearchState })
|
|
|
|
// 搜索表单配置
|
|
const searchFormItems = computed<SearchFormItem[]>(() => [
|
|
{
|
|
label: '套餐名称',
|
|
prop: 'package_name',
|
|
type: 'input',
|
|
config: {
|
|
clearable: true,
|
|
placeholder: '请输入套餐名称'
|
|
}
|
|
},
|
|
{
|
|
label: '套餐系列',
|
|
prop: 'series_id',
|
|
type: 'select',
|
|
config: {
|
|
clearable: true,
|
|
filterable: true,
|
|
remote: true,
|
|
remoteMethod: handleSearchSeries,
|
|
loading: seriesLoading.value,
|
|
placeholder: '请选择或搜索套餐系列'
|
|
},
|
|
options: () =>
|
|
searchSeriesOptions.value.map((s) => ({
|
|
label: s.series_name,
|
|
value: s.id
|
|
}))
|
|
},
|
|
{
|
|
label: '套餐类型',
|
|
prop: 'package_type',
|
|
type: 'select',
|
|
config: {
|
|
clearable: true,
|
|
placeholder: '请选择套餐类型'
|
|
},
|
|
options: () =>
|
|
PACKAGE_TYPE_OPTIONS.map((o) => ({
|
|
label: o.label,
|
|
value: o.value
|
|
}))
|
|
},
|
|
{
|
|
label: '上架状态',
|
|
prop: 'shelf_status',
|
|
type: 'select',
|
|
config: {
|
|
clearable: true,
|
|
placeholder: '请选择上架状态'
|
|
},
|
|
options: () => [
|
|
{ label: '上架', value: 1 },
|
|
{ label: '下架', value: 2 }
|
|
]
|
|
},
|
|
{
|
|
label: '状态',
|
|
prop: 'status',
|
|
type: 'select',
|
|
config: {
|
|
clearable: true,
|
|
placeholder: '请选择状态'
|
|
},
|
|
options: () => [
|
|
{ label: '启用', value: 1 },
|
|
{ label: '禁用', value: 2 }
|
|
]
|
|
}
|
|
])
|
|
|
|
// 分页
|
|
const pagination = reactive({
|
|
page: 1,
|
|
page_size: 20,
|
|
total: 0
|
|
})
|
|
|
|
// 列配置
|
|
const columnOptions = [
|
|
{ label: '套餐编码', prop: 'package_code' },
|
|
{ label: '套餐名称', prop: 'package_name' },
|
|
{ label: '所属系列', prop: 'series_name' },
|
|
{ label: '套餐类型', prop: 'package_type' },
|
|
{ label: '真流量', prop: 'real_data_mb' },
|
|
{ label: '虚流量', prop: 'virtual_data_mb' },
|
|
{ label: '有效期', prop: 'duration_months' },
|
|
{ label: '成本价', prop: 'cost_price' },
|
|
{ label: '建议售价', prop: 'suggested_retail_price' },
|
|
{ label: '上架状态', prop: 'shelf_status' },
|
|
{ label: '状态', prop: 'status' },
|
|
{ label: '创建时间', prop: 'created_at' }
|
|
]
|
|
|
|
// 表单验证规则
|
|
const rules = computed<FormRules>(() => {
|
|
const baseRules: FormRules = {
|
|
package_code: [
|
|
{ required: true, message: '请输入套餐编码', trigger: 'blur' },
|
|
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
|
|
],
|
|
package_name: [
|
|
{ required: true, message: '请输入套餐名称', trigger: 'blur' },
|
|
{ min: 1, max: 255, message: '长度在 1 到 255 个字符', trigger: 'blur' }
|
|
],
|
|
package_type: [{ required: true, message: '请选择套餐类型', trigger: 'change' }],
|
|
duration_months: [
|
|
{ required: true, message: '请输入有效期', trigger: 'blur' },
|
|
{ type: 'number', min: 1, max: 120, message: '有效期范围 1-120 月', trigger: 'blur' }
|
|
],
|
|
cost_price: [{ required: true, message: '请输入成本价', trigger: 'blur' }]
|
|
}
|
|
|
|
// 如果启用虚流量,则虚流量额度为必填
|
|
if (form.enable_virtual_data) {
|
|
baseRules.virtual_data_mb = [{ required: true, message: '请输入虚流量额度', trigger: 'blur' }]
|
|
}
|
|
|
|
// 如果套餐周期类型是按天,则套餐天数为必填
|
|
if (form.calendar_type === 'by_day') {
|
|
baseRules.duration_days = [
|
|
{ required: true, message: '请输入套餐天数', trigger: 'blur' },
|
|
{ type: 'number', min: 1, max: 3650, message: '套餐天数范围 1-3650 天', trigger: 'blur' }
|
|
]
|
|
}
|
|
|
|
return baseRules
|
|
})
|
|
|
|
// 表单数据
|
|
const form = reactive<any>({
|
|
id: 0,
|
|
package_code: '',
|
|
package_name: '',
|
|
series_id: undefined,
|
|
package_type: '',
|
|
calendar_type: undefined,
|
|
duration_days: undefined,
|
|
duration_months: 1,
|
|
data_reset_cycle: undefined,
|
|
enable_virtual_data: false,
|
|
enable_realname_activation: false,
|
|
real_data_mb: 0,
|
|
virtual_data_mb: 0,
|
|
cost_price: 0,
|
|
suggested_retail_price: undefined,
|
|
description: ''
|
|
})
|
|
|
|
const packageList = ref<PackageResponse[]>([])
|
|
const dialogType = ref('add')
|
|
|
|
// 动态列配置
|
|
const { columnChecks, columns } = useCheckedColumns(() => [
|
|
{
|
|
prop: 'package_code',
|
|
label: '套餐编码',
|
|
showOverflowTooltip: true,
|
|
width: 210
|
|
},
|
|
{
|
|
prop: 'package_name',
|
|
label: '套餐名称',
|
|
minWidth: 160,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
prop: 'series_name',
|
|
label: '所属系列',
|
|
width: 160,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
prop: 'package_type',
|
|
label: '套餐类型',
|
|
width: 100,
|
|
formatter: (row: PackageResponse) => {
|
|
return h(ElTag, { type: getPackageTypeTag(row.package_type), size: 'small' }, () =>
|
|
getPackageTypeLabel(row.package_type)
|
|
)
|
|
}
|
|
},
|
|
{
|
|
prop: 'real_data_mb',
|
|
label: '真流量',
|
|
width: 100,
|
|
formatter: (row: PackageResponse) => `${row.real_data_mb}MB`
|
|
},
|
|
{
|
|
prop: 'virtual_data_mb',
|
|
label: '虚流量',
|
|
width: 100,
|
|
formatter: (row: PackageResponse) => `${row.virtual_data_mb}MB`
|
|
},
|
|
{
|
|
prop: 'duration_months',
|
|
label: '有效期',
|
|
width: 100,
|
|
formatter: (row: PackageResponse) => `${row.duration_months}月`
|
|
},
|
|
{
|
|
prop: 'cost_price',
|
|
label: '成本价',
|
|
width: 100,
|
|
formatter: (row: PackageResponse) => `¥${(row.cost_price / 100).toFixed(2)}`
|
|
},
|
|
{
|
|
prop: 'suggested_retail_price',
|
|
label: '建议售价',
|
|
width: 100,
|
|
formatter: (row: PackageResponse) =>
|
|
row.suggested_retail_price ? `¥${(row.suggested_retail_price / 100).toFixed(2)}` : '-'
|
|
},
|
|
{
|
|
prop: 'shelf_status',
|
|
label: '上架状态',
|
|
width: 100,
|
|
formatter: (row: PackageResponse) => {
|
|
return h(ElSwitch, {
|
|
modelValue: row.shelf_status === 1,
|
|
activeText: '上架',
|
|
inactiveText: '下架',
|
|
inlinePrompt: true,
|
|
disabled: !hasAuth('package:update_away'),
|
|
'onUpdate:modelValue': (val: string | number | boolean) =>
|
|
handleShelfStatusChange(row, val ? 1 : 2)
|
|
})
|
|
}
|
|
},
|
|
{
|
|
prop: 'status',
|
|
label: '状态',
|
|
width: 100,
|
|
formatter: (row: PackageResponse) => {
|
|
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:update_status'),
|
|
'onUpdate:modelValue': (val: string | number | boolean) =>
|
|
handleStatusChange(row, val as number)
|
|
})
|
|
}
|
|
},
|
|
{
|
|
prop: 'created_at',
|
|
label: '创建时间',
|
|
width: 180,
|
|
formatter: (row: PackageResponse) => formatDateTime(row.created_at)
|
|
}
|
|
])
|
|
|
|
// 右键菜单项配置
|
|
const contextMenuItems = computed((): MenuItemType[] => {
|
|
const items: MenuItemType[] = []
|
|
|
|
items.push({
|
|
key: 'detail',
|
|
label: '详情'
|
|
})
|
|
|
|
if (hasAuth('package:edit')) {
|
|
items.push({
|
|
key: 'edit',
|
|
label: '编辑'
|
|
})
|
|
}
|
|
|
|
if (hasAuth('package:delete')) {
|
|
items.push({
|
|
key: 'delete',
|
|
label: '删除'
|
|
})
|
|
}
|
|
|
|
return items
|
|
})
|
|
|
|
// 监听虚流量开关变化,关闭时重置虚流量额度
|
|
watch(
|
|
() => form.enable_virtual_data,
|
|
(enabled) => {
|
|
if (!enabled) {
|
|
form.virtual_data_mb = 0
|
|
}
|
|
}
|
|
)
|
|
|
|
onMounted(() => {
|
|
loadSeriesOptions()
|
|
loadSearchSeriesOptions()
|
|
getTableData()
|
|
})
|
|
|
|
// 加载系列选项(用于新增/编辑对话框,默认加载10条)
|
|
const loadSeriesOptions = async (seriesName?: string) => {
|
|
seriesLoading.value = true
|
|
try {
|
|
const params: any = {
|
|
page: 1,
|
|
page_size: 10,
|
|
status: 1
|
|
}
|
|
if (seriesName) {
|
|
params.series_name = seriesName
|
|
}
|
|
const res = await PackageSeriesService.getPackageSeries(params)
|
|
if (res.code === 0) {
|
|
seriesOptions.value = res.data.items
|
|
}
|
|
} catch (error) {
|
|
console.error('加载系列选项失败:', error)
|
|
} finally {
|
|
seriesLoading.value = false
|
|
}
|
|
}
|
|
|
|
// 加载搜索栏系列选项(默认加载10条)
|
|
const loadSearchSeriesOptions = async (seriesName?: string) => {
|
|
try {
|
|
const params: any = {
|
|
page: 1,
|
|
page_size: 10,
|
|
status: 1
|
|
}
|
|
if (seriesName) {
|
|
params.series_name = seriesName
|
|
}
|
|
const res = await PackageSeriesService.getPackageSeries(params)
|
|
if (res.code === 0) {
|
|
searchSeriesOptions.value = res.data.items
|
|
}
|
|
} catch (error) {
|
|
console.error('加载搜索栏系列选项失败:', error)
|
|
}
|
|
}
|
|
|
|
// 搜索系列(用于新增/编辑对话框)
|
|
const searchSeries = (query: string) => {
|
|
if (query) {
|
|
loadSeriesOptions(query)
|
|
} else {
|
|
loadSeriesOptions()
|
|
}
|
|
}
|
|
|
|
// 搜索系列(用于搜索栏)
|
|
const handleSearchSeries = (query: string) => {
|
|
if (query) {
|
|
loadSearchSeriesOptions(query)
|
|
} else {
|
|
loadSearchSeriesOptions()
|
|
}
|
|
}
|
|
|
|
// 获取套餐列表
|
|
const getTableData = async () => {
|
|
loading.value = true
|
|
try {
|
|
const params = {
|
|
page: pagination.page,
|
|
page_size: pagination.page_size,
|
|
package_name: searchForm.package_name || undefined,
|
|
series_id: searchForm.series_id || undefined,
|
|
package_type: searchForm.package_type || undefined,
|
|
shelf_status: searchForm.shelf_status || undefined,
|
|
status: searchForm.status || undefined
|
|
}
|
|
const res = await PackageManageService.getPackages(params)
|
|
if (res.code === 0) {
|
|
packageList.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?: PackageResponse) => {
|
|
dialogVisible.value = true
|
|
dialogType.value = type
|
|
|
|
if (type === 'edit' && row) {
|
|
form.id = row.id
|
|
form.package_code = row.package_code
|
|
form.package_name = row.package_name
|
|
form.series_id = row.series_id
|
|
form.package_type = row.package_type
|
|
form.calendar_type = row.calendar_type || undefined
|
|
form.duration_days = row.duration_days || undefined
|
|
form.duration_months = row.duration_months
|
|
form.data_reset_cycle = row.data_reset_cycle || undefined
|
|
form.enable_virtual_data = row.enable_virtual_data || false
|
|
form.enable_realname_activation = row.enable_realname_activation || false
|
|
form.real_data_mb = row.real_data_mb || 0
|
|
form.virtual_data_mb = row.virtual_data_mb || 0
|
|
form.cost_price = row.cost_price / 100 // 分转换为元显示
|
|
form.suggested_retail_price = row.suggested_retail_price
|
|
? row.suggested_retail_price / 100
|
|
: undefined
|
|
form.description = row.description || ''
|
|
} else {
|
|
form.id = 0
|
|
form.package_code = ''
|
|
form.package_name = ''
|
|
form.series_id = undefined
|
|
form.package_type = ''
|
|
form.calendar_type = undefined
|
|
form.duration_days = undefined
|
|
form.duration_months = 1
|
|
form.data_reset_cycle = undefined
|
|
form.enable_virtual_data = false
|
|
form.enable_realname_activation = false
|
|
form.real_data_mb = 0
|
|
form.virtual_data_mb = 0
|
|
form.cost_price = 0
|
|
form.suggested_retail_price = undefined
|
|
form.description = ''
|
|
}
|
|
|
|
// 重置表单验证状态
|
|
nextTick(() => {
|
|
formRef.value?.clearValidate()
|
|
})
|
|
}
|
|
|
|
// 生成套餐编码
|
|
const handleGeneratePackageCode = () => {
|
|
form.package_code = generatePackageCode()
|
|
// 生成编码后清除该字段的验证错误
|
|
nextTick(() => {
|
|
formRef.value?.clearValidate('package_code')
|
|
})
|
|
ElMessage.success('编码生成成功')
|
|
}
|
|
|
|
// 处理弹窗关闭事件
|
|
const handleDialogClosed = () => {
|
|
// 清除表单验证状态
|
|
formRef.value?.clearValidate()
|
|
// 重置表单数据
|
|
form.id = 0
|
|
form.package_code = ''
|
|
form.package_name = ''
|
|
form.series_id = undefined
|
|
form.package_type = ''
|
|
form.calendar_type = undefined
|
|
form.duration_days = undefined
|
|
form.duration_months = 1
|
|
form.data_reset_cycle = undefined
|
|
form.enable_virtual_data = false
|
|
form.enable_realname_activation = false
|
|
form.real_data_mb = 0
|
|
form.virtual_data_mb = 0
|
|
form.cost_price = 0
|
|
form.suggested_retail_price = undefined
|
|
form.description = ''
|
|
}
|
|
|
|
// 删除套餐
|
|
const deletePackage = (row: PackageResponse) => {
|
|
ElMessageBox.confirm(`确定删除套餐 ${row.package_name} 吗?`, '删除确认', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'error'
|
|
})
|
|
.then(async () => {
|
|
try {
|
|
await PackageManageService.deletePackage(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 costPriceInCents = Math.round(form.cost_price * 100)
|
|
const suggestedRetailPriceInCents = form.suggested_retail_price
|
|
? Math.round(form.suggested_retail_price * 100)
|
|
: undefined
|
|
|
|
const data: any = {
|
|
package_code: form.package_code,
|
|
package_name: form.package_name,
|
|
package_type: form.package_type,
|
|
duration_months: form.duration_months,
|
|
cost_price: costPriceInCents,
|
|
enable_virtual_data: form.enable_virtual_data || false,
|
|
enable_realname_activation: form.enable_realname_activation || false
|
|
}
|
|
|
|
// 可选字段
|
|
if (form.series_id) {
|
|
data.series_id = form.series_id
|
|
}
|
|
if (form.calendar_type) {
|
|
data.calendar_type = form.calendar_type
|
|
}
|
|
if (form.calendar_type === 'by_day' && form.duration_days) {
|
|
data.duration_days = form.duration_days
|
|
}
|
|
if (form.data_reset_cycle) {
|
|
data.data_reset_cycle = form.data_reset_cycle
|
|
}
|
|
if (suggestedRetailPriceInCents !== undefined) {
|
|
data.suggested_retail_price = suggestedRetailPriceInCents
|
|
}
|
|
if (form.real_data_mb) {
|
|
data.real_data_mb = form.real_data_mb
|
|
}
|
|
if (form.enable_virtual_data && form.virtual_data_mb) {
|
|
data.virtual_data_mb = form.virtual_data_mb
|
|
}
|
|
|
|
if (dialogType.value === 'add') {
|
|
await PackageManageService.createPackage(data)
|
|
ElMessage.success('新增成功')
|
|
} else {
|
|
await PackageManageService.updatePackage(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: PackageResponse, newFrontendStatus: number) => {
|
|
const oldStatus = row.status
|
|
const newApiStatus = frontendStatusToApi(newFrontendStatus)
|
|
row.status = newApiStatus
|
|
try {
|
|
await PackageManageService.updatePackageStatus(row.id, newApiStatus)
|
|
ElMessage.success('状态切换成功')
|
|
} catch (error) {
|
|
row.status = oldStatus
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
// 上架状态切换
|
|
const handleShelfStatusChange = async (row: PackageResponse, newShelfStatus: number) => {
|
|
const oldShelfStatus = row.shelf_status
|
|
row.shelf_status = newShelfStatus
|
|
try {
|
|
await PackageManageService.updatePackageShelfStatus(row.id, newShelfStatus)
|
|
ElMessage.success('上架状态切换成功')
|
|
} catch (error) {
|
|
row.shelf_status = oldShelfStatus
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
// 查看详情
|
|
const handleViewDetail = (row: PackageResponse) => {
|
|
router.push(`${RoutesAlias.PackageDetail}/${row.id}`)
|
|
}
|
|
|
|
// 处理表格行右键菜单
|
|
const handleRowContextMenu = (row: PackageResponse, column: any, event: MouseEvent) => {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
currentRow.value = row
|
|
contextMenuRef.value?.show(event)
|
|
}
|
|
|
|
// 处理右键菜单选择
|
|
const handleContextMenuSelect = (item: MenuItemType) => {
|
|
if (!currentRow.value) return
|
|
|
|
switch (item.key) {
|
|
case 'detail':
|
|
handleViewDetail(currentRow.value)
|
|
break
|
|
case 'edit':
|
|
showDialog('edit', currentRow.value)
|
|
break
|
|
case 'delete':
|
|
deletePackage(currentRow.value)
|
|
break
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.package-list-page {
|
|
// 可以添加特定样式
|
|
}
|
|
|
|
.dialog-footer {
|
|
text-align: right;
|
|
}
|
|
</style>
|