This commit is contained in:
@@ -37,12 +37,21 @@
|
||||
: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"
|
||||
@@ -128,6 +137,8 @@
|
||||
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 {
|
||||
@@ -149,6 +160,8 @@
|
||||
const shopLoading = ref(false)
|
||||
const tableRef = ref()
|
||||
const formRef = ref<FormInstance>()
|
||||
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||
const currentRow = ref<ShopPackageAllocationResponse | null>(null)
|
||||
const packageOptions = ref<PackageResponse[]>([])
|
||||
const shopOptions = ref<ShopResponse[]>([])
|
||||
const shopTreeData = ref<ShopResponse[]>([])
|
||||
@@ -276,8 +289,7 @@
|
||||
{ label: '分配者', prop: 'allocator_shop_name' },
|
||||
{ label: '成本价', prop: 'cost_price' },
|
||||
{ label: '状态', prop: 'status' },
|
||||
{ label: '创建时间', prop: 'created_at' },
|
||||
{ label: '操作', prop: 'operation' }
|
||||
{ label: '创建时间', prop: 'created_at' }
|
||||
]
|
||||
|
||||
// 表单验证规则
|
||||
@@ -389,45 +401,35 @@
|
||||
label: '创建时间',
|
||||
width: 180,
|
||||
formatter: (row: ShopPackageAllocationResponse) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 220,
|
||||
fixed: 'right',
|
||||
formatter: (row: ShopPackageAllocationResponse) => {
|
||||
const buttons = []
|
||||
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '详情',
|
||||
onClick: () => handleViewDetail(row)
|
||||
})
|
||||
)
|
||||
|
||||
if (hasAuth('package_assign:edit')) {
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '编辑',
|
||||
onClick: () => showDialog('edit', row)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (hasAuth('package_assign:delete')) {
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '删除',
|
||||
onClick: () => deleteAllocation(row)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
// 右键菜单项配置
|
||||
const contextMenuItems = computed((): MenuItemType[] => {
|
||||
const items: MenuItemType[] = []
|
||||
|
||||
items.push({
|
||||
key: 'detail',
|
||||
label: '详情'
|
||||
})
|
||||
|
||||
if (hasAuth('package_assign:edit')) {
|
||||
items.push({
|
||||
key: 'edit',
|
||||
label: '编辑'
|
||||
})
|
||||
}
|
||||
|
||||
if (hasAuth('package_assign:delete')) {
|
||||
items.push({
|
||||
key: 'delete',
|
||||
label: '删除'
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
// 构建树形结构数据
|
||||
const buildTreeData = (items: ShopResponse[]) => {
|
||||
const map = new Map<number, ShopResponse & { children?: ShopResponse[] }>()
|
||||
@@ -478,7 +480,7 @@
|
||||
}
|
||||
const res = await PackageManageService.getPackages(params)
|
||||
if (res.code === 0) {
|
||||
packageOptions.value = res.data.items || []
|
||||
packageOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载套餐选项失败:', error)
|
||||
@@ -497,7 +499,7 @@
|
||||
page_size: 10000 // 使用较大的值获取所有店铺
|
||||
})
|
||||
if (res.code === 0) {
|
||||
shopOptions.value = res.data.items || []
|
||||
shopOptions.value = res.data.items
|
||||
// 构建树形结构数据
|
||||
shopTreeData.value = buildTreeData(shopOptions.value)
|
||||
}
|
||||
@@ -516,7 +518,7 @@
|
||||
page_size: 10
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchPackageOptions.value = res.data.items || []
|
||||
searchPackageOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载搜索栏套餐选项失败:', error)
|
||||
@@ -528,7 +530,7 @@
|
||||
try {
|
||||
const res = await ShopService.getShops({ page: 1, page_size: 10 })
|
||||
if (res.code === 0) {
|
||||
searchShopOptions.value = res.data.items || []
|
||||
searchShopOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载搜索栏店铺选项失败:', error)
|
||||
@@ -557,7 +559,7 @@
|
||||
package_name: query
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchPackageOptions.value = res.data.items || []
|
||||
searchPackageOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索套餐失败:', error)
|
||||
@@ -577,7 +579,7 @@
|
||||
shop_name: query
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchShopOptions.value = res.data.items || []
|
||||
searchShopOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索店铺失败:', error)
|
||||
@@ -589,7 +591,7 @@
|
||||
try {
|
||||
const res = await ShopService.getShops({ page: 1, page_size: 10 })
|
||||
if (res.code === 0) {
|
||||
searchAllocatorShopOptions.value = res.data.items || []
|
||||
searchAllocatorShopOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载搜索栏分配者店铺选项失败:', error)
|
||||
@@ -609,7 +611,7 @@
|
||||
shop_name: query
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchAllocatorShopOptions.value = res.data.items || []
|
||||
searchAllocatorShopOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索分配者店铺失败:', error)
|
||||
@@ -624,7 +626,7 @@
|
||||
page_size: 10
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchSeriesAllocationOptions.value = res.data.items || []
|
||||
searchSeriesAllocationOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载搜索栏系列分配选项失败:', error)
|
||||
@@ -644,7 +646,7 @@
|
||||
series_name: query
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchSeriesAllocationOptions.value = res.data.items || []
|
||||
searchSeriesAllocationOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索系列分配失败:', error)
|
||||
@@ -666,7 +668,7 @@
|
||||
}
|
||||
const res = await ShopPackageAllocationService.getShopPackageAllocations(params)
|
||||
if (res.code === 0) {
|
||||
allocationList.value = res.data.items || []
|
||||
allocationList.value = res.data.items
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -845,6 +847,31 @@
|
||||
const handleViewDetail = (row: ShopPackageAllocationResponse) => {
|
||||
router.push(`${RoutesAlias.PackageAssignDetail}/${row.id}`)
|
||||
}
|
||||
|
||||
// 处理表格行右键菜单
|
||||
const handleRowContextMenu = (row: ShopPackageAllocationResponse, 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':
|
||||
deleteAllocation(currentRow.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -68,6 +68,43 @@
|
||||
return `${data.duration_months} 个月`
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '套餐周期类型',
|
||||
formatter: (_, data) => {
|
||||
if (!data.calendar_type) return '-'
|
||||
const typeMap = {
|
||||
natural_month: '自然月',
|
||||
by_day: '按天'
|
||||
}
|
||||
return typeMap[data.calendar_type] || data.calendar_type
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '套餐天数',
|
||||
formatter: (_, data) => {
|
||||
if (data.calendar_type !== 'by_day' || !data.duration_days) return '-'
|
||||
return `${data.duration_days} 天`
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '流量重置周期',
|
||||
formatter: (_, data) => {
|
||||
if (!data.data_reset_cycle) return '-'
|
||||
const cycleMap = {
|
||||
daily: '每日',
|
||||
monthly: '每月',
|
||||
yearly: '每年',
|
||||
none: '不重置'
|
||||
}
|
||||
return cycleMap[data.data_reset_cycle] || data.data_reset_cycle
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '启用实名激活',
|
||||
formatter: (_, data) => {
|
||||
return data.enable_realname_activation ? '是' : '否'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
formatter: (_, data) => {
|
||||
@@ -141,12 +178,6 @@
|
||||
{
|
||||
title: '佣金配置',
|
||||
fields: [
|
||||
{
|
||||
label: '当前返佣比例',
|
||||
formatter: (_, data) => {
|
||||
return data.current_commission_rate || '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '一次性佣金金额',
|
||||
formatter: (_, data) => {
|
||||
@@ -159,7 +190,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '当前返佣档位',
|
||||
label: '当前返佣比例',
|
||||
formatter: (_, data) => {
|
||||
if (!data.tier_info || !data.tier_info.current_rate) return '-'
|
||||
return data.tier_info.current_rate
|
||||
|
||||
@@ -36,12 +36,21 @@
|
||||
: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"
|
||||
@@ -101,6 +110,54 @@
|
||||
/>
|
||||
</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"
|
||||
@@ -130,13 +187,11 @@
|
||||
placeholder="请输入虚流量额度"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="有效期(月)" prop="duration_months">
|
||||
<ElInputNumber
|
||||
v-model="form.duration_months"
|
||||
:min="1"
|
||||
:max="120"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
<ElFormItem label="启用实名激活">
|
||||
<ElSwitch
|
||||
v-model="form.enable_realname_activation"
|
||||
active-text="启用"
|
||||
inactive-text="不启用"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="成本价(元)" prop="cost_price">
|
||||
@@ -197,6 +252,8 @@
|
||||
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 {
|
||||
@@ -221,6 +278,8 @@
|
||||
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[]>([])
|
||||
|
||||
@@ -327,8 +386,7 @@
|
||||
{ label: '建议售价', prop: 'suggested_retail_price' },
|
||||
{ label: '上架状态', prop: 'shelf_status' },
|
||||
{ label: '状态', prop: 'status' },
|
||||
{ label: '创建时间', prop: 'created_at' },
|
||||
{ label: '操作', prop: 'operation' }
|
||||
{ label: '创建时间', prop: 'created_at' }
|
||||
]
|
||||
|
||||
// 表单验证规则
|
||||
@@ -355,6 +413,14 @@
|
||||
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
|
||||
})
|
||||
|
||||
@@ -365,10 +431,14 @@
|
||||
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,
|
||||
duration_months: 1,
|
||||
cost_price: 0,
|
||||
suggested_retail_price: undefined,
|
||||
description: ''
|
||||
@@ -388,12 +458,14 @@
|
||||
{
|
||||
prop: 'package_name',
|
||||
label: '套餐名称',
|
||||
minWidth: 160
|
||||
minWidth: 160,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'series_name',
|
||||
label: '所属系列',
|
||||
width: 120
|
||||
width: 160,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'package_type',
|
||||
@@ -476,45 +548,35 @@
|
||||
label: '创建时间',
|
||||
width: 180,
|
||||
formatter: (row: PackageResponse) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 220,
|
||||
fixed: 'right',
|
||||
formatter: (row: PackageResponse) => {
|
||||
const buttons = []
|
||||
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '详情',
|
||||
onClick: () => handleViewDetail(row)
|
||||
})
|
||||
)
|
||||
|
||||
if (hasAuth('package:edit')) {
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '编辑',
|
||||
onClick: () => showDialog('edit', row)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (hasAuth('package:delete')) {
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '删除',
|
||||
onClick: () => deletePackage(row)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
// 右键菜单项配置
|
||||
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,
|
||||
@@ -545,7 +607,7 @@
|
||||
}
|
||||
const res = await PackageSeriesService.getPackageSeries(params)
|
||||
if (res.code === 0) {
|
||||
seriesOptions.value = res.data.items || []
|
||||
seriesOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载系列选项失败:', error)
|
||||
@@ -567,7 +629,7 @@
|
||||
}
|
||||
const res = await PackageSeriesService.getPackageSeries(params)
|
||||
if (res.code === 0) {
|
||||
searchSeriesOptions.value = res.data.items || []
|
||||
searchSeriesOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载搜索栏系列选项失败:', error)
|
||||
@@ -607,8 +669,8 @@
|
||||
}
|
||||
const res = await PackageManageService.getPackages(params)
|
||||
if (res.code === 0) {
|
||||
packageList.value = res.data.items || []
|
||||
pagination.total = res.data.total || 0
|
||||
packageList.value = res.data.items
|
||||
pagination.total = res.data.total
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -658,10 +720,14 @@
|
||||
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.duration_months = row.duration_months
|
||||
form.cost_price = row.cost_price / 100 // 分转换为元显示
|
||||
form.suggested_retail_price = row.suggested_retail_price
|
||||
? row.suggested_retail_price / 100
|
||||
@@ -673,10 +739,14 @@
|
||||
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.duration_months = 1
|
||||
form.cost_price = 0
|
||||
form.suggested_retail_price = undefined
|
||||
form.description = ''
|
||||
@@ -708,10 +778,14 @@
|
||||
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.duration_months = 1
|
||||
form.cost_price = 0
|
||||
form.suggested_retail_price = undefined
|
||||
form.description = ''
|
||||
@@ -758,13 +832,23 @@
|
||||
package_type: form.package_type,
|
||||
duration_months: form.duration_months,
|
||||
cost_price: costPriceInCents,
|
||||
enable_virtual_data: form.enable_virtual_data
|
||||
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
|
||||
}
|
||||
@@ -826,6 +910,31 @@
|
||||
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>
|
||||
|
||||
@@ -44,28 +44,13 @@
|
||||
</template>
|
||||
</ArtTable>
|
||||
|
||||
<!-- 右键菜单 -->
|
||||
<div
|
||||
v-if="contextMenuVisible"
|
||||
:style="{
|
||||
position: 'fixed',
|
||||
left: contextMenuPosition.x + 'px',
|
||||
top: contextMenuPosition.y + 'px',
|
||||
zIndex: 9999
|
||||
}"
|
||||
>
|
||||
<ElCard shadow="always" class="context-menu">
|
||||
<div class="context-menu-item" @click="handleViewDetail(contextMenuRow)" v-if="hasAuth('package_series:detail')">
|
||||
<span>详情</span>
|
||||
</div>
|
||||
<div class="context-menu-item" @click="showDialog('edit', contextMenuRow)" v-if="hasAuth('package_series:edit')">
|
||||
<span>编辑</span>
|
||||
</div>
|
||||
<div class="context-menu-item danger" @click="deleteSeries(contextMenuRow)" v-if="hasAuth('package_series:delete')">
|
||||
<span>删除</span>
|
||||
</div>
|
||||
</ElCard>
|
||||
</div>
|
||||
<!-- 套餐系列操作右键菜单 -->
|
||||
<ArtMenuRight
|
||||
ref="seriesOperationMenuRef"
|
||||
:menu-items="seriesOperationMenuItems"
|
||||
:menu-width="140"
|
||||
@select="handleSeriesOperationMenuSelect"
|
||||
/>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<ElDialog
|
||||
@@ -389,6 +374,8 @@
|
||||
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 {
|
||||
CommonStatus,
|
||||
@@ -410,10 +397,9 @@
|
||||
const tableRef = ref()
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 右键菜单状态
|
||||
const contextMenuVisible = ref(false)
|
||||
const contextMenuPosition = reactive({ x: 0, y: 0 })
|
||||
const contextMenuRow = ref<PackageSeriesResponse | null>(null)
|
||||
// 右键菜单
|
||||
const seriesOperationMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||
const currentOperatingSeries = ref<PackageSeriesResponse | null>(null)
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
@@ -708,27 +694,74 @@
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
// 添加全局点击事件监听,关闭右键菜单
|
||||
document.addEventListener('click', closeContextMenu)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
// 移除全局点击事件监听
|
||||
document.removeEventListener('click', closeContextMenu)
|
||||
// 套餐系列操作菜单项配置
|
||||
const seriesOperationMenuItems = computed((): MenuItemType[] => {
|
||||
const items: MenuItemType[] = []
|
||||
|
||||
// 详情
|
||||
if (hasAuth('package_series:detail')) {
|
||||
items.push({
|
||||
key: 'detail',
|
||||
label: '详情'
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑
|
||||
if (hasAuth('package_series:edit')) {
|
||||
items.push({
|
||||
key: 'edit',
|
||||
label: '编辑'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除
|
||||
if (hasAuth('package_series:delete')) {
|
||||
items.push({
|
||||
key: 'delete',
|
||||
label: '删除'
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
// 处理右键菜单
|
||||
const handleRowContextMenu = (row: PackageSeriesResponse, column: any, event: MouseEvent) => {
|
||||
event.preventDefault()
|
||||
contextMenuRow.value = row
|
||||
contextMenuPosition.x = event.clientX
|
||||
contextMenuPosition.y = event.clientY
|
||||
contextMenuVisible.value = true
|
||||
// 显示套餐系列操作右键菜单
|
||||
const showSeriesOperationMenu = (e: MouseEvent, row: PackageSeriesResponse) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
currentOperatingSeries.value = row
|
||||
seriesOperationMenuRef.value?.show(e)
|
||||
}
|
||||
|
||||
// 关闭右键菜单
|
||||
const closeContextMenu = () => {
|
||||
contextMenuVisible.value = false
|
||||
// 处理表格行右键菜单
|
||||
const handleRowContextMenu = (row: PackageSeriesResponse, column: any, event: MouseEvent) => {
|
||||
// 如果用户有编辑或删除权限,显示右键菜单
|
||||
if (
|
||||
hasAuth('package_series:edit') ||
|
||||
hasAuth('package_series:delete') ||
|
||||
hasAuth('package_series:detail')
|
||||
) {
|
||||
showSeriesOperationMenu(event, row)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理套餐系列操作菜单选择
|
||||
const handleSeriesOperationMenuSelect = (item: MenuItemType) => {
|
||||
if (!currentOperatingSeries.value) return
|
||||
|
||||
switch (item.key) {
|
||||
case 'detail':
|
||||
handleViewDetail(currentOperatingSeries.value)
|
||||
break
|
||||
case 'edit':
|
||||
showDialog('edit', currentOperatingSeries.value)
|
||||
break
|
||||
case 'delete':
|
||||
deleteSeries(currentOperatingSeries.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 获取套餐系列列表
|
||||
@@ -744,8 +777,8 @@
|
||||
}
|
||||
const res = await PackageSeriesService.getPackageSeries(params)
|
||||
if (res.code === 0) {
|
||||
seriesList.value = res.data.items || []
|
||||
pagination.total = res.data.total || 0
|
||||
seriesList.value = res.data.items
|
||||
pagination.total = res.data.total
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -1058,35 +1091,4 @@
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
min-width: 120px;
|
||||
padding: 4px 0;
|
||||
background: white;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.context-menu-item {
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-primary);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
}
|
||||
|
||||
&.danger {
|
||||
color: var(--el-color-danger);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-danger-light-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -37,12 +37,21 @@
|
||||
: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"
|
||||
@@ -205,6 +214,8 @@
|
||||
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 {
|
||||
CommonStatus,
|
||||
@@ -225,6 +236,8 @@
|
||||
const shopLoading = ref(false)
|
||||
const tableRef = ref()
|
||||
const formRef = ref<FormInstance>()
|
||||
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||
const currentRow = ref<ShopSeriesAllocationResponse | null>(null)
|
||||
const seriesOptions = ref<PackageSeriesResponse[]>([])
|
||||
const shopOptions = ref<ShopResponse[]>([])
|
||||
const shopTreeData = ref<ShopResponse[]>([])
|
||||
@@ -340,8 +353,7 @@
|
||||
{ label: '强充金额', prop: 'force_recharge_amount' },
|
||||
{ label: '强充触发类型', prop: 'force_recharge_trigger_type' },
|
||||
{ label: '状态', prop: 'status' },
|
||||
{ label: '创建时间', prop: 'created_at' },
|
||||
{ label: '操作', prop: 'operation' }
|
||||
{ label: '创建时间', prop: 'created_at' }
|
||||
]
|
||||
|
||||
// 表单数据
|
||||
@@ -563,43 +575,6 @@
|
||||
label: '创建时间',
|
||||
width: 180,
|
||||
formatter: (row: ShopSeriesAllocationResponse) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 220,
|
||||
fixed: 'right',
|
||||
formatter: (row: ShopSeriesAllocationResponse) => {
|
||||
const buttons = []
|
||||
|
||||
// 详情按钮
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '详情',
|
||||
onClick: () => handleViewDetail(row)
|
||||
})
|
||||
)
|
||||
|
||||
if (hasAuth('series_assign:edit')) {
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '编辑',
|
||||
onClick: () => showDialog('edit', row)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (hasAuth('series_assign:delete')) {
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '删除',
|
||||
onClick: () => deleteAllocation(row)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
@@ -653,7 +628,7 @@
|
||||
}
|
||||
const res = await PackageSeriesService.getPackageSeries(params)
|
||||
if (res.code === 0) {
|
||||
seriesOptions.value = res.data.items || []
|
||||
seriesOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载系列选项失败:', error)
|
||||
@@ -672,7 +647,7 @@
|
||||
page_size: 10000 // 使用较大的值获取所有店铺
|
||||
})
|
||||
if (res.code === 0) {
|
||||
shopOptions.value = res.data.items || []
|
||||
shopOptions.value = res.data.items
|
||||
// 构建树形结构数据
|
||||
shopTreeData.value = buildTreeData(shopOptions.value)
|
||||
}
|
||||
@@ -692,7 +667,7 @@
|
||||
status: 1
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchSeriesOptions.value = res.data.items || []
|
||||
searchSeriesOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载搜索栏系列选项失败:', error)
|
||||
@@ -704,7 +679,7 @@
|
||||
try {
|
||||
const res = await ShopService.getShops({ page: 1, page_size: 10 })
|
||||
if (res.code === 0) {
|
||||
searchShopOptions.value = res.data.items || []
|
||||
searchShopOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载搜索栏店铺选项失败:', error)
|
||||
@@ -716,7 +691,7 @@
|
||||
try {
|
||||
const res = await ShopService.getShops({ page: 1, page_size: 10 })
|
||||
if (res.code === 0) {
|
||||
searchAllocatorShopOptions.value = res.data.items || []
|
||||
searchAllocatorShopOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载搜索栏分配者店铺选项失败:', error)
|
||||
@@ -746,7 +721,7 @@
|
||||
status: 1
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchSeriesOptions.value = res.data.items || []
|
||||
searchSeriesOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索系列失败:', error)
|
||||
@@ -766,7 +741,7 @@
|
||||
shop_name: query
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchShopOptions.value = res.data.items || []
|
||||
searchShopOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索店铺失败:', error)
|
||||
@@ -786,7 +761,7 @@
|
||||
shop_name: query
|
||||
})
|
||||
if (res.code === 0) {
|
||||
searchAllocatorShopOptions.value = res.data.items || []
|
||||
searchAllocatorShopOptions.value = res.data.items
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索分配者店铺失败:', error)
|
||||
@@ -808,7 +783,7 @@
|
||||
}
|
||||
const res = await ShopSeriesAllocationService.getShopSeriesAllocations(params)
|
||||
if (res.code === 0) {
|
||||
allocationList.value = res.data.items || []
|
||||
allocationList.value = res.data.items
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1011,6 +986,48 @@
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 右键菜单项配置
|
||||
const contextMenuItems = computed((): MenuItemType[] => {
|
||||
const items: MenuItemType[] = []
|
||||
|
||||
items.push({ key: 'detail', label: '详情' })
|
||||
|
||||
if (hasAuth('series_assign:edit')) {
|
||||
items.push({ key: 'edit', label: '编辑' })
|
||||
}
|
||||
|
||||
if (hasAuth('series_assign:delete')) {
|
||||
items.push({ key: 'delete', label: '删除' })
|
||||
}
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
// 处理表格行右键菜单
|
||||
const handleRowContextMenu = (row: ShopSeriesAllocationResponse, 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':
|
||||
deleteAllocation(currentRow.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Reference in New Issue
Block a user