This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
import { PackageManageService } from '@/api/modules'
|
||||
import type { PackageResponse } from '@/types/api'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { getStatusLabel, getShelfStatusText } from '@/config/constants'
|
||||
|
||||
defineOptions({ name: 'PackageDetail' })
|
||||
|
||||
@@ -55,7 +56,7 @@
|
||||
{
|
||||
label: '套餐类型',
|
||||
formatter: (_, data) => {
|
||||
const typeMap = {
|
||||
const typeMap: Record<string, string> = {
|
||||
formal: '正式套餐',
|
||||
addon: '附加套餐'
|
||||
}
|
||||
@@ -72,7 +73,7 @@
|
||||
label: '套餐周期类型',
|
||||
formatter: (_, data) => {
|
||||
if (!data.calendar_type) return '-'
|
||||
const typeMap = {
|
||||
const typeMap: Record<string, string> = {
|
||||
natural_month: '自然月',
|
||||
by_day: '按天'
|
||||
}
|
||||
@@ -90,7 +91,7 @@
|
||||
label: '流量重置周期',
|
||||
formatter: (_, data) => {
|
||||
if (!data.data_reset_cycle) return '-'
|
||||
const cycleMap = {
|
||||
const cycleMap: Record<string, string> = {
|
||||
daily: '每日',
|
||||
monthly: '每月',
|
||||
yearly: '每年',
|
||||
@@ -109,13 +110,13 @@
|
||||
{
|
||||
label: '状态',
|
||||
formatter: (_, data) => {
|
||||
return data.status === 1 ? '启用' : '禁用'
|
||||
return getStatusLabel(data.status ?? 0)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '上架状态',
|
||||
formatter: (_, data) => {
|
||||
return data.shelf_status === 1 ? '上架' : '下架'
|
||||
return getShelfStatusText(data.shelf_status ?? 0)
|
||||
}
|
||||
},
|
||||
{ label: '创建时间', prop: 'created_at', formatter: (value) => formatDateTime(value) },
|
||||
|
||||
@@ -34,29 +34,15 @@
|
||||
:pageSize="pagination.page_size"
|
||||
:total="pagination.total"
|
||||
:marginTop="10"
|
||||
:row-class-name="getRowClassName"
|
||||
:actions="getActions"
|
||||
@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="contextMenuRef"
|
||||
:menu-items="contextMenuItems"
|
||||
:menu-width="120"
|
||||
@select="handleContextMenuSelect"
|
||||
/>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
@@ -204,14 +190,20 @@
|
||||
<!-- 真流量额度 -->
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem label="真流量额度(MB)" prop="real_data_mb">
|
||||
<ElFormItem label="真流量额度(GB)" prop="real_data_gb">
|
||||
<ElInputNumber
|
||||
v-model="form.real_data_mb"
|
||||
v-model="realDataGb"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:step="0.1"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
placeholder="请输入真流量额度"
|
||||
@change="handleRealDataChange"
|
||||
/>
|
||||
<div v-if="realDataGb" style="margin-top: 4px; font-size: 12px; color: var(--el-text-color-secondary)">
|
||||
{{ realDataGb }}GB = {{ form.real_data_mb }}MB
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
@@ -228,14 +220,20 @@
|
||||
<!-- 虚流量额度和到期计时基准 -->
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="12" v-if="form.enable_virtual_data">
|
||||
<ElFormItem label="虚流量额度(MB)" prop="virtual_data_mb">
|
||||
<ElFormItem label="虚流量额度(GB)" prop="virtual_data_gb">
|
||||
<ElInputNumber
|
||||
v-model="form.virtual_data_mb"
|
||||
v-model="virtualDataGb"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:step="0.1"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
placeholder="请输入虚流量额度"
|
||||
@change="handleVirtualDataChange"
|
||||
/>
|
||||
<div v-if="virtualDataGb" style="margin-top: 4px; font-size: 12px; color: var(--el-text-color-secondary)">
|
||||
{{ virtualDataGb }}GB = {{ form.virtual_data_mb }}MB
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="12">
|
||||
@@ -363,16 +361,13 @@
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { PackageManageService, PackageSeriesService } from '@/api/modules'
|
||||
import { ElMessage, ElMessageBox, ElTag, ElSwitch, ElButton, ElInputNumber } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox, ElTag, ElSwitch, ElInputNumber } 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 { useTableContextMenu } from '@/composables/useTableContextMenu'
|
||||
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 { useUserStore } from '@/store/modules/user'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
import {
|
||||
@@ -382,13 +377,17 @@
|
||||
apiStatusToFrontend,
|
||||
PACKAGE_TYPE_OPTIONS,
|
||||
getPackageTypeLabel,
|
||||
getPackageTypeTag
|
||||
getPackageTypeTag,
|
||||
STATUS_SELECT_OPTIONS,
|
||||
SHELF_STATUS_SELECT_OPTIONS,
|
||||
getShelfStatusText
|
||||
} from '@/config/constants'
|
||||
import { generatePackageCode } from '@/utils/codeGenerator'
|
||||
|
||||
defineOptions({ name: 'PackageList' })
|
||||
|
||||
const { hasAuth } = useAuth()
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
@@ -397,8 +396,6 @@
|
||||
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[]>([])
|
||||
|
||||
@@ -419,14 +416,6 @@
|
||||
})
|
||||
|
||||
// 使用表格右键菜单功能
|
||||
const {
|
||||
showContextMenuHint,
|
||||
hintPosition,
|
||||
getRowClassName,
|
||||
handleCellMouseEnter,
|
||||
handleCellMouseLeave
|
||||
} = useTableContextMenu()
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
package_name: '',
|
||||
@@ -490,10 +479,7 @@
|
||||
clearable: true,
|
||||
placeholder: '请选择上架状态'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '上架', value: 1 },
|
||||
{ label: '下架', value: 2 }
|
||||
]
|
||||
options: SHELF_STATUS_SELECT_OPTIONS
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
@@ -503,10 +489,7 @@
|
||||
clearable: true,
|
||||
placeholder: '请选择状态'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 2 }
|
||||
]
|
||||
options: STATUS_SELECT_OPTIONS
|
||||
}
|
||||
])
|
||||
|
||||
@@ -559,7 +542,7 @@
|
||||
baseRules.virtual_data_mb = [
|
||||
{ required: true, message: '请输入虚流量额度', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validator: (_rule: any, value: any, callback: any) => {
|
||||
if (value && form.real_data_mb && value > form.real_data_mb) {
|
||||
callback(new Error('虚流量额度不能超过真流量额度'))
|
||||
} else {
|
||||
@@ -602,6 +585,27 @@
|
||||
description: ''
|
||||
})
|
||||
|
||||
// GB 输入值(用于界面显示)
|
||||
const realDataGb = ref<number>(0)
|
||||
const virtualDataGb = ref<number>(0)
|
||||
|
||||
// GB 转 MB 处理
|
||||
const handleRealDataChange = (value: number | null) => {
|
||||
if (value === null || value === undefined) {
|
||||
form.real_data_mb = 0
|
||||
return
|
||||
}
|
||||
form.real_data_mb = Math.round(value * 1024)
|
||||
}
|
||||
|
||||
const handleVirtualDataChange = (value: number | null) => {
|
||||
if (value === null || value === undefined) {
|
||||
form.virtual_data_mb = 0
|
||||
return
|
||||
}
|
||||
form.virtual_data_mb = Math.round(value * 1024)
|
||||
}
|
||||
|
||||
const packageList = ref<PackageResponse[]>([])
|
||||
const dialogType = ref('add')
|
||||
|
||||
@@ -619,14 +623,19 @@
|
||||
minWidth: 160,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: PackageResponse) => {
|
||||
const hasPermission = hasAuth('package:detail')
|
||||
return h(
|
||||
'span',
|
||||
{
|
||||
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||
onClick: (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
handleNameClick(row)
|
||||
}
|
||||
style: hasPermission
|
||||
? 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;'
|
||||
: '',
|
||||
onClick: hasPermission
|
||||
? (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
handleNameClick(row)
|
||||
}
|
||||
: undefined
|
||||
},
|
||||
row.package_name
|
||||
)
|
||||
@@ -652,13 +661,29 @@
|
||||
prop: 'real_data_mb',
|
||||
label: '真流量',
|
||||
width: 120,
|
||||
formatter: (row: PackageResponse) => `${row.real_data_mb}MB`
|
||||
formatter: (row: PackageResponse) => {
|
||||
const mb = row.real_data_mb ?? 0
|
||||
if (mb >= 1024) {
|
||||
return `${(mb / 1024).toFixed(2)}GB`
|
||||
}
|
||||
return `${mb}MB`
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'virtual_data_mb',
|
||||
label: '虚流量',
|
||||
width: 100,
|
||||
formatter: (row: PackageResponse) => `${row.virtual_data_mb}MB`
|
||||
width: 120,
|
||||
formatter: (row: PackageResponse) => {
|
||||
// 如果没有启用虚流量,显示 "-"
|
||||
if (!row.enable_virtual_data) {
|
||||
return '-'
|
||||
}
|
||||
const mb = row.virtual_data_mb ?? 0
|
||||
if (mb >= 1024) {
|
||||
return `${(mb / 1024).toFixed(2)}GB`
|
||||
}
|
||||
return `${mb}MB`
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'virtual_ratio',
|
||||
@@ -666,8 +691,10 @@
|
||||
width: 120,
|
||||
formatter: (row: PackageResponse) => {
|
||||
// 如果启用虚流量且虚流量大于0,计算比例
|
||||
if (row.enable_virtual_data && row.virtual_data_mb > 0) {
|
||||
const ratio = row.real_data_mb / row.virtual_data_mb
|
||||
const virtualData = row.virtual_data_mb ?? 0
|
||||
const realData = row.real_data_mb ?? 0
|
||||
if (row.enable_virtual_data && virtualData > 0) {
|
||||
const ratio = realData / virtualData
|
||||
return ratio.toFixed(2)
|
||||
}
|
||||
// 否则返回 1.0
|
||||
@@ -684,14 +711,16 @@
|
||||
prop: 'cost_price',
|
||||
label: '成本价',
|
||||
width: 100,
|
||||
formatter: (row: PackageResponse) => `¥${(row.cost_price / 100).toFixed(2)}`
|
||||
formatter: (row: PackageResponse) => `¥${((row.cost_price ?? 0) / 100).toFixed(2)}`
|
||||
},
|
||||
{
|
||||
prop: 'suggested_retail_price',
|
||||
label: '建议售价',
|
||||
width: 100,
|
||||
formatter: (row: PackageResponse) =>
|
||||
row.suggested_retail_price ? `¥${(row.suggested_retail_price / 100).toFixed(2)}` : '-'
|
||||
row.suggested_retail_price
|
||||
? `¥${((row.suggested_retail_price ?? 0) / 100).toFixed(2)}`
|
||||
: '-'
|
||||
},
|
||||
{
|
||||
prop: 'shelf_status',
|
||||
@@ -700,8 +729,8 @@
|
||||
formatter: (row: PackageResponse) => {
|
||||
return h(ElSwitch, {
|
||||
modelValue: row.shelf_status === 1,
|
||||
activeText: '上架',
|
||||
inactiveText: '下架',
|
||||
activeText: getShelfStatusText(1),
|
||||
inactiveText: getShelfStatusText(2),
|
||||
inlinePrompt: true,
|
||||
disabled: !hasAuth('package:update_away'),
|
||||
'onUpdate:modelValue': (val: string | number | boolean) =>
|
||||
@@ -714,7 +743,7 @@
|
||||
label: '状态',
|
||||
width: 100,
|
||||
formatter: (row: PackageResponse) => {
|
||||
const frontendStatus = apiStatusToFrontend(row.status)
|
||||
const frontendStatus = apiStatusToFrontend(row.status ?? 0)
|
||||
return h(ElSwitch, {
|
||||
modelValue: frontendStatus,
|
||||
activeValue: CommonStatus.ENABLED,
|
||||
@@ -736,33 +765,29 @@
|
||||
}
|
||||
])
|
||||
|
||||
// 右键菜单项配置
|
||||
const contextMenuItems = computed((): MenuItemType[] => {
|
||||
const items: MenuItemType[] = []
|
||||
// 操作列配置
|
||||
const getActions = (row: PackageResponse) => {
|
||||
const actions: any[] = []
|
||||
|
||||
if (hasAuth('package:edit')) {
|
||||
items.push({
|
||||
key: 'edit',
|
||||
label: '编辑'
|
||||
})
|
||||
actions.push({ label: '编辑', handler: () => showDialog('edit', row), type: 'primary' })
|
||||
}
|
||||
|
||||
if (hasAuth('package:update_retail_price')) {
|
||||
items.push({
|
||||
key: 'update-retail-price',
|
||||
label: '修改零售价'
|
||||
// 只有代理账号(user_type=3)才显示修改零售价
|
||||
if (hasAuth('package:update_retail_price') && userStore.getUserInfo.user_type === 3) {
|
||||
actions.push({
|
||||
label: '修改零售价',
|
||||
handler: () => showRetailPriceDialog(row),
|
||||
type: 'primary'
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('package:delete')) {
|
||||
items.push({
|
||||
key: 'delete',
|
||||
label: '删除'
|
||||
})
|
||||
actions.push({ label: '删除', handler: () => deletePackage(row), type: 'danger' })
|
||||
}
|
||||
|
||||
return items
|
||||
})
|
||||
return actions
|
||||
}
|
||||
|
||||
// 监听虚流量开关变化,关闭时重置虚流量额度
|
||||
watch(
|
||||
@@ -950,7 +975,10 @@
|
||||
form.expiry_base = row.expiry_base || 'from_activation'
|
||||
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 // 分转换为元显示
|
||||
// MB 转换为 GB 显示
|
||||
realDataGb.value = form.real_data_mb ? Number((form.real_data_mb / 1024).toFixed(2)) : 0
|
||||
virtualDataGb.value = form.virtual_data_mb ? Number((form.virtual_data_mb / 1024).toFixed(2)) : 0
|
||||
form.cost_price = (row.cost_price ?? 0) / 100 // 分转换为元显示
|
||||
form.suggested_retail_price = row.suggested_retail_price
|
||||
? row.suggested_retail_price / 100
|
||||
: undefined
|
||||
@@ -969,6 +997,9 @@
|
||||
form.expiry_base = 'from_activation'
|
||||
form.real_data_mb = 0
|
||||
form.virtual_data_mb = 0
|
||||
// 重置 GB 值
|
||||
realDataGb.value = 0
|
||||
virtualDataGb.value = 0
|
||||
form.cost_price = 0
|
||||
form.suggested_retail_price = undefined
|
||||
form.description = ''
|
||||
@@ -1008,6 +1039,9 @@
|
||||
form.expiry_base = 'from_activation'
|
||||
form.real_data_mb = 0
|
||||
form.virtual_data_mb = 0
|
||||
// 重置 GB 值
|
||||
realDataGb.value = 0
|
||||
virtualDataGb.value = 0
|
||||
form.cost_price = 0
|
||||
form.suggested_retail_price = undefined
|
||||
form.description = ''
|
||||
@@ -1147,20 +1181,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 处理表格行右键菜单
|
||||
const handleRowContextMenu = (row: PackageResponse, column: any, event: MouseEvent) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
currentRow.value = row
|
||||
contextMenuRef.value?.show(event)
|
||||
}
|
||||
|
||||
// 显示零售价修改对话框
|
||||
const showRetailPriceDialog = (row: PackageResponse) => {
|
||||
retailPriceForm.id = row.id
|
||||
retailPriceForm.package_name = row.package_name
|
||||
// 将分转换为元显示
|
||||
retailPriceForm.retail_price = row.retail_price ? row.retail_price / 100 : 0
|
||||
retailPriceForm.retail_price = row.suggested_retail_price ? row.suggested_retail_price / 100 : 0
|
||||
retailPriceDialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -1177,7 +1203,7 @@
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('零售价修改成功')
|
||||
retailPriceDialogVisible.value = false
|
||||
loadPackageList()
|
||||
await getTableData()
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('修改零售价失败:', error)
|
||||
@@ -1192,34 +1218,9 @@
|
||||
retailPriceDialogVisible.value = false
|
||||
retailPriceFormRef.value?.resetFields()
|
||||
}
|
||||
|
||||
// 处理右键菜单选择
|
||||
const handleContextMenuSelect = (item: MenuItemType) => {
|
||||
if (!currentRow.value) return
|
||||
|
||||
switch (item.key) {
|
||||
case 'edit':
|
||||
showDialog('edit', currentRow.value)
|
||||
break
|
||||
case 'update-retail-price':
|
||||
showRetailPriceDialog(currentRow.value)
|
||||
break
|
||||
case 'delete':
|
||||
deletePackage(currentRow.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-table__row.table-row-with-context-menu) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.package-list-page {
|
||||
// 可以添加特定样式
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
import { PackageSeriesService } from '@/api/modules'
|
||||
import type { PackageSeriesResponse } from '@/types/api'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { getEnableStatusText } from '@/config/constants'
|
||||
|
||||
defineOptions({ name: 'PackageSeriesDetail' })
|
||||
|
||||
@@ -72,7 +73,7 @@
|
||||
{
|
||||
label: '启用状态',
|
||||
formatter: (_, data) => {
|
||||
return data.one_time_commission_config?.enable ? '已启用' : '未启用'
|
||||
return getEnableStatusText(data.one_time_commission_config?.enable || false)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -151,7 +152,9 @@
|
||||
{
|
||||
label: '启用状态',
|
||||
formatter: (_, data) => {
|
||||
return data.one_time_commission_config?.enable_force_recharge ? '已启用' : '未启用'
|
||||
return getEnableStatusText(
|
||||
data.one_time_commission_config?.enable_force_recharge || false
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -180,7 +183,7 @@
|
||||
formatter: (_, data) => {
|
||||
const config = data.one_time_commission_config
|
||||
if (!config?.validity_type) return '-'
|
||||
const typeMap = {
|
||||
const typeMap: Record<string, string> = {
|
||||
permanent: '永久',
|
||||
fixed_date: '固定日期',
|
||||
relative: '相对时长'
|
||||
|
||||
@@ -35,29 +35,15 @@
|
||||
:pageSize="pagination.page_size"
|
||||
:total="pagination.total"
|
||||
:marginTop="10"
|
||||
:row-class-name="getRowClassName"
|
||||
:actions="getActions"
|
||||
@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"
|
||||
@@ -404,18 +390,16 @@
|
||||
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
|
||||
apiStatusToFrontend,
|
||||
STATUS_SELECT_OPTIONS,
|
||||
ENABLE_STATUS_OPTIONS,
|
||||
getEnableStatusText
|
||||
} from '@/config/constants'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -430,15 +414,11 @@
|
||||
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
|
||||
status: undefined,
|
||||
enable_one_time_commission: undefined
|
||||
}
|
||||
|
||||
// 搜索表单
|
||||
@@ -463,10 +443,7 @@
|
||||
clearable: true,
|
||||
placeholder: '请选择状态'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 2 }
|
||||
]
|
||||
options: STATUS_SELECT_OPTIONS
|
||||
},
|
||||
{
|
||||
label: '一次性佣金',
|
||||
@@ -476,10 +453,7 @@
|
||||
clearable: true,
|
||||
placeholder: '请选择'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '已启用', value: true },
|
||||
{ label: '未启用', value: false }
|
||||
]
|
||||
options: ENABLE_STATUS_OPTIONS
|
||||
}
|
||||
]
|
||||
|
||||
@@ -557,14 +531,19 @@
|
||||
label: '系列名称',
|
||||
minWidth: 150,
|
||||
formatter: (row: PackageSeriesResponse) => {
|
||||
const hasPermission = hasAuth('package_series:detail')
|
||||
return h(
|
||||
'span',
|
||||
{
|
||||
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||
onClick: (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
handleNameClick(row)
|
||||
}
|
||||
style: hasPermission
|
||||
? 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;'
|
||||
: '',
|
||||
onClick: hasPermission
|
||||
? (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
handleNameClick(row)
|
||||
}
|
||||
: undefined
|
||||
},
|
||||
row.series_name
|
||||
)
|
||||
@@ -582,10 +561,10 @@
|
||||
return h(
|
||||
ElTag,
|
||||
{
|
||||
type: row.one_time_commission_config.enable ? 'success' : 'info',
|
||||
type: row.one_time_commission_config?.enable ? 'success' : 'info',
|
||||
size: 'small'
|
||||
},
|
||||
() => (row.one_time_commission_config.enable ? '已启用' : '未启用')
|
||||
() => getEnableStatusText(row.one_time_commission_config?.enable || false)
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -641,10 +620,10 @@
|
||||
return h(
|
||||
ElTag,
|
||||
{
|
||||
type: row.one_time_commission_config.enable_force_recharge ? 'warning' : 'info',
|
||||
type: row.one_time_commission_config?.enable_force_recharge ? 'warning' : 'info',
|
||||
size: 'small'
|
||||
},
|
||||
() => (row.one_time_commission_config.enable_force_recharge ? '已启用' : '未启用')
|
||||
() => getEnableStatusText(row.one_time_commission_config?.enable_force_recharge || false)
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -738,63 +717,22 @@
|
||||
}
|
||||
])
|
||||
|
||||
// 操作列配置
|
||||
const getActions = (row: PackageSeriesResponse) => {
|
||||
const actions: any[] = []
|
||||
if (hasAuth('package_series:edit')) {
|
||||
actions.push({ label: '编辑', handler: () => showDialog('edit', row), type: 'primary' })
|
||||
}
|
||||
if (hasAuth('package_series:delete')) {
|
||||
actions.push({ label: '删除', handler: () => deleteSeries(row), type: 'danger' })
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1121,27 +1059,10 @@
|
||||
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>
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="启用状态">
|
||||
<ElTag :type="detailData.force_recharge_enabled ? 'warning' : 'info'">
|
||||
{{ detailData.force_recharge_enabled ? '已启用' : '未启用' }}
|
||||
{{ getEnableStatusText(detailData.force_recharge_enabled || false) }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="锁定状态">
|
||||
@@ -314,6 +314,7 @@
|
||||
PackageSeriesResponse
|
||||
} from '@/types/api'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { getEnableStatusText } from '@/config/constants'
|
||||
|
||||
defineOptions({ name: 'SeriesGrantsDetail' })
|
||||
|
||||
@@ -504,6 +505,7 @@
|
||||
const handleSavePackage = async () => {
|
||||
if (!packageFormRef.value || !detailData.value) return
|
||||
|
||||
const grantId = detailData.value.id // 保存到常量中避免类型检查问题
|
||||
await packageFormRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
|
||||
@@ -525,7 +527,7 @@
|
||||
})
|
||||
}
|
||||
|
||||
await ShopSeriesGrantService.manageGrantPackages(detailData.value.id, { packages })
|
||||
await ShopSeriesGrantService.manageGrantPackages(grantId, { packages })
|
||||
ElMessage.success(packageDialogType.value === 'add' ? '添加成功' : '更新成功')
|
||||
packageDialogVisible.value = false
|
||||
|
||||
|
||||
@@ -35,29 +35,16 @@
|
||||
:pageSize="pagination.page_size"
|
||||
:total="pagination.total"
|
||||
:marginTop="10"
|
||||
:row-class-name="getRowClassName"
|
||||
:actions="getActions"
|
||||
:actionsWidth="160"
|
||||
@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="contextMenuRef"
|
||||
:menu-items="contextMenuItems"
|
||||
:menu-width="120"
|
||||
@select="handleContextMenuSelect"
|
||||
/>
|
||||
|
||||
<!-- 套餐列表对话框 -->
|
||||
<ElDialog
|
||||
v-model="packageListDialogVisible"
|
||||
@@ -777,17 +764,14 @@
|
||||
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 { formatDateTime } from '@/utils/business/format'
|
||||
import {
|
||||
CommonStatus,
|
||||
getStatusText,
|
||||
frontendStatusToApi,
|
||||
apiStatusToFrontend
|
||||
apiStatusToFrontend,
|
||||
STATUS_SELECT_OPTIONS,
|
||||
getEnableStatusText
|
||||
} from '@/config/constants'
|
||||
|
||||
defineOptions({ name: 'SeriesGrants' })
|
||||
@@ -803,8 +787,6 @@
|
||||
const packageLoading = ref(false)
|
||||
const tableRef = ref()
|
||||
const formRef = ref<FormInstance>()
|
||||
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||
const currentRow = ref<ShopSeriesGrantResponse | null>(null)
|
||||
const currentStep = ref(0) // 当前步骤,0 为第一步,1 为第二步
|
||||
|
||||
// 套餐列表对话框相关
|
||||
@@ -930,10 +912,7 @@
|
||||
clearable: true,
|
||||
placeholder: '请选择状态'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 2 }
|
||||
]
|
||||
options: STATUS_SELECT_OPTIONS
|
||||
}
|
||||
])
|
||||
|
||||
@@ -1064,14 +1043,19 @@
|
||||
label: '系列名称',
|
||||
minWidth: 150,
|
||||
formatter: (row: ShopSeriesGrantResponse) => {
|
||||
const hasPermission = hasAuth('series_name_grants:detail')
|
||||
return h(
|
||||
'span',
|
||||
{
|
||||
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||
onClick: (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
handleNameClick(row)
|
||||
}
|
||||
style: hasPermission
|
||||
? 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;'
|
||||
: '',
|
||||
onClick: hasPermission
|
||||
? (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
handleNameClick(row)
|
||||
}
|
||||
: undefined
|
||||
},
|
||||
row.series_name
|
||||
)
|
||||
@@ -1137,7 +1121,7 @@
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: row.force_recharge_enabled ? 'warning' : 'info', size: 'small' },
|
||||
() => (row.force_recharge_enabled ? '已启用' : '未启用')
|
||||
() => getEnableStatusText(row.force_recharge_enabled || false)
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -1173,6 +1157,29 @@
|
||||
}
|
||||
])
|
||||
|
||||
// 操作列配置
|
||||
const getActions = (row: ShopSeriesGrantResponse) => {
|
||||
const actions: any[] = []
|
||||
|
||||
if (hasAuth('series_grants:detail')) {
|
||||
actions.push({
|
||||
label: '套餐列表',
|
||||
handler: () => showPackageListDialog(row),
|
||||
type: 'primary'
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('series_grants:edit')) {
|
||||
actions.push({ label: '编辑', handler: () => showDialog('edit', row), type: 'primary' })
|
||||
}
|
||||
|
||||
if (hasAuth('series_grants:delete')) {
|
||||
actions.push({ label: '删除', handler: () => deleteAllocation(row), type: 'danger' })
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
// 构建树形结构数据
|
||||
const buildTreeData = (items: ShopResponse[]) => {
|
||||
const map = new Map<number, ShopResponse & { children?: ShopResponse[] }>()
|
||||
@@ -1865,50 +1872,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 右键菜单项配置
|
||||
const contextMenuItems = computed((): MenuItemType[] => {
|
||||
const items: MenuItemType[] = []
|
||||
|
||||
if (hasAuth('series_grants:detail')) {
|
||||
items.push({ key: 'view_packages', label: '查看套餐列表' })
|
||||
}
|
||||
|
||||
if (hasAuth('series_grants:edit')) {
|
||||
items.push({ key: 'edit', label: '编辑' })
|
||||
}
|
||||
|
||||
if (hasAuth('series_grants:delete')) {
|
||||
items.push({ key: 'delete', label: '删除' })
|
||||
}
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
// 处理表格行右键菜单
|
||||
const handleRowContextMenu = (row: ShopSeriesGrantResponse, 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 'view_packages':
|
||||
showPackageListDialog(currentRow.value)
|
||||
break
|
||||
case 'edit':
|
||||
showDialog('edit', currentRow.value)
|
||||
break
|
||||
case 'delete':
|
||||
deleteAllocation(currentRow.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 显示套餐列表对话框
|
||||
const showPackageListDialog = async (row: ShopSeriesGrantResponse) => {
|
||||
currentGrantId.value = row.id
|
||||
@@ -2108,14 +2071,6 @@
|
||||
}
|
||||
|
||||
// 使用表格右键菜单功能
|
||||
const {
|
||||
showContextMenuHint,
|
||||
hintPosition,
|
||||
getRowClassName,
|
||||
handleCellMouseEnter,
|
||||
handleCellMouseLeave
|
||||
} = useTableContextMenu()
|
||||
|
||||
// 下一步
|
||||
const handleNextStep = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
Reference in New Issue
Block a user